1 /* Copyright (c) Mark Harmstone 2017
3 * This file is part of WinBtrfs.
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.
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.
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/>. */
34 #include <smmintrin.h>
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.";
44 bool have_sse42
= false;
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,
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) \
87 for (; (len) >= sizeof (type); (len) -= (ULONG)sizeof(type), buf += sizeof (type)) { \
88 (crc) = (uint32_t)op((crc), *(type *) (buf)); \
93 static uint32_t crc32c_hw(const void *input
, ULONG len
, uint32_t crc
) {
94 const char* buf
= (const char*)input
;
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!
100 for (; (len
> 0) && ((size_t)buf
& ALIGN_MASK
); len
--, buf
++) {
102 crc
= crctable
[(crc
^ *buf
) & 0xff] ^ (crc
>> 8);
104 crc
= _mm_crc32_u8(crc
, *buf
);
110 #pragma warning(push)
111 #pragma warning(disable:4244) // _mm_crc32_u64 wants to return uint64_t(!)
112 #pragma warning(disable:4242)
114 CALC_CRC(_mm_crc32_u64
, crc
, uint64_t, buf
, len
);
119 CALC_CRC(_mm_crc32_u32
, crc
, uint32_t, buf
, len
);
122 for (; len
> 0; len
--, buf
++) {
123 crc
= crctable
[(crc
^ *buf
) & 0xff] ^ (crc
>> 8);
126 CALC_CRC(_mm_crc32_u16
, crc
, uint16_t, buf
, len
);
127 CALC_CRC(_mm_crc32_u8
, crc
, uint8_t, buf
, len
);
134 static uint32_t calc_crc32c(uint32_t seed
, uint8_t* msg
, ULONG msglen
) {
137 return crc32c_hw(msg
, msglen
, seed
);
145 for (i
= 0; i
< msglen
; i
++) {
146 rem
= crctable
[(rem
^ msg
[i
]) & 0xff] ^ (rem
>> 8);
155 bool BtrfsRecv::find_tlv(uint8_t* data
, ULONG datalen
, uint16_t type
, void** value
, ULONG
* len
) {
158 while (off
< datalen
) {
159 btrfs_send_tlv
* tlv
= (btrfs_send_tlv
*)(data
+ off
);
160 uint8_t* payload
= data
+ off
+ sizeof(btrfs_send_tlv
);
162 if (off
+ sizeof(btrfs_send_tlv
) + tlv
->length
> datalen
) // file is truncated
165 if (tlv
->type
== type
) {
171 off
+= sizeof(btrfs_send_tlv
) + tlv
->length
;
177 void BtrfsRecv::cmd_subvol(HWND hwnd
, btrfs_send_command
* cmd
, uint8_t* data
, const win_handle
& parent
) {
181 ULONG uuidlen
, genlen
;
182 btrfs_create_subvol
* bcs
;
184 IO_STATUS_BLOCK iosb
;
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");
193 name
= string(namebuf
, namelen
);
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");
199 if (uuidlen
< sizeof(BTRFS_UUID
))
200 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"uuid", uuidlen
, sizeof(BTRFS_UUID
));
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");
205 if (genlen
< sizeof(uint64_t))
206 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"transid", genlen
, sizeof(uint64_t));
208 this->subvol_uuid
= *uuid
;
209 this->stransid
= *gen
;
211 auto nameu
= utf8_to_utf16(name
);
213 size_t bcslen
= offsetof(btrfs_create_subvol
, name
[0]) + (nameu
.length() * sizeof(WCHAR
));
214 bcs
= (btrfs_create_subvol
*)malloc(bcslen
);
216 bcs
->readonly
= true;
218 bcs
->namelen
= (uint16_t)(nameu
.length() * sizeof(WCHAR
));
219 memcpy(bcs
->name
, nameu
.c_str(), bcs
->namelen
);
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());
225 subvolpath
= dirpath
;
229 if (dir
!= INVALID_HANDLE_VALUE
)
232 if (master
!= INVALID_HANDLE_VALUE
)
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());
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());
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());
252 add_cache_entry(&this->subvol_uuid
, this->stransid
, subvolpath
);
257 void BtrfsRecv::add_cache_entry(BTRFS_UUID
* uuid
, uint64_t transid
, const wstring
& path
) {
261 sc
.transid
= transid
;
267 void BtrfsRecv::cmd_snapshot(HWND hwnd
, btrfs_send_command
* cmd
, uint8_t* data
, const win_handle
& parent
) {
269 BTRFS_UUID
*uuid
, *parent_uuid
;
270 uint64_t *gen
, *parent_transid
;
271 ULONG uuidlen
, genlen
, paruuidlen
, partransidlen
;
272 btrfs_create_snapshot
* bcs
;
274 IO_STATUS_BLOCK iosb
;
276 btrfs_find_subvol bfs
;
277 WCHAR parpathw
[MAX_PATH
], volpathw
[MAX_PATH
];
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");
287 name
= string(namebuf
, namelen
);
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");
293 if (uuidlen
< sizeof(BTRFS_UUID
))
294 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"uuid", uuidlen
, sizeof(BTRFS_UUID
));
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");
299 if (genlen
< sizeof(uint64_t))
300 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"transid", genlen
, sizeof(uint64_t));
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");
305 if (paruuidlen
< sizeof(BTRFS_UUID
))
306 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"clone_uuid", paruuidlen
, sizeof(BTRFS_UUID
));
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");
311 if (partransidlen
< sizeof(uint64_t))
312 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"clone_ctransid", partransidlen
, sizeof(uint64_t));
314 this->subvol_uuid
= *uuid
;
315 this->stransid
= *gen
;
317 auto nameu
= utf8_to_utf16(name
);
319 bfs
.uuid
= *parent_uuid
;
320 bfs
.ctransid
= *parent_transid
;
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());
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());
333 if (parpath
.substr(parpath
.length() - 1) == L
"\\")
334 parpath
= parpath
.substr(0, parpath
.length() - 1);
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());
344 bcslen
= offsetof(btrfs_create_snapshot
, name
[0]) + (nameu
.length() * sizeof(WCHAR
));
345 bcs
= (btrfs_create_snapshot
*)malloc(bcslen
);
347 bcs
->readonly
= true;
349 bcs
->subvol
= subvol
;
350 bcs
->namelen
= (uint16_t)(nameu
.length() * sizeof(WCHAR
));
351 memcpy(bcs
->name
, nameu
.c_str(), bcs
->namelen
);
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());
358 subvolpath
= dirpath
;
362 if (dir
!= INVALID_HANDLE_VALUE
)
365 if (master
!= INVALID_HANDLE_VALUE
)
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());
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());
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());
385 add_cache_entry(&this->subvol_uuid
, this->stransid
, subvolpath
);
390 void BtrfsRecv::cmd_mkfile(HWND hwnd
, btrfs_send_command
* cmd
, uint8_t* data
) {
391 uint64_t *inode
, *rdev
= nullptr, *mode
= nullptr;
394 IO_STATUS_BLOCK iosb
;
396 wstring nameu
, pathlinku
;
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");
405 nameu
= utf8_to_utf16(string(name
, namelen
));
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");
411 if (inodelen
< sizeof(uint64_t))
412 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"inode", inodelen
, sizeof(uint64_t));
414 if (cmd
->cmd
== BTRFS_SEND_CMD_MKNOD
|| cmd
->cmd
== BTRFS_SEND_CMD_MKFIFO
|| cmd
->cmd
== BTRFS_SEND_CMD_MKSOCK
) {
415 ULONG rdevlen
, modelen
;
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");
420 if (rdevlen
< sizeof(uint64_t))
421 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"rdev", rdev
, sizeof(uint64_t));
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");
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
) {
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");
435 pathlinku
= utf8_to_utf16(string(pathlink
, pathlinklen
));
438 size_t bmnsize
= sizeof(btrfs_mknod
) - sizeof(WCHAR
) + (nameu
.length() * sizeof(WCHAR
));
439 bmn
= (btrfs_mknod
*)malloc(bmnsize
);
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
;
452 bmn
->type
= BTRFS_TYPE_FILE
;
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
);
458 Status
= NtFsControlFile(dir
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_MKNOD
, bmn
, (ULONG
)bmnsize
, nullptr, 0);
459 if (!NT_SUCCESS(Status
)) {
461 throw string_error(IDS_RECV_MKNOD_FAILED
, Status
, format_ntstatus(Status
).c_str());
466 if (cmd
->cmd
== BTRFS_SEND_CMD_SYMLINK
) {
467 REPARSE_DATA_BUFFER
* rdb
;
468 btrfs_set_inode_info bsii
;
470 size_t rdblen
= offsetof(REPARSE_DATA_BUFFER
, SymbolicLinkReparseBuffer
.PathBuffer
[0]) + (2 * pathlinku
.length() * sizeof(WCHAR
));
472 if (rdblen
>= 0x10000)
473 throw string_error(IDS_RECV_PATH_TOO_LONG
, funcname
);
475 rdb
= (REPARSE_DATA_BUFFER
*)malloc(rdblen
);
477 rdb
->ReparseTag
= IO_REPARSE_TAG_SYMLINK
;
478 rdb
->ReparseDataLength
= (uint16_t)(rdblen
- offsetof(REPARSE_DATA_BUFFER
, SymbolicLinkReparseBuffer
));
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
;
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
);
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
) {
494 throw string_error(IDS_RECV_CANT_OPEN_FILE
, funcname
, nameu
.c_str(), GetLastError(), format_message(GetLastError()).c_str());
497 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_SET_REPARSE_POINT
, rdb
, (ULONG
)rdblen
, nullptr, 0);
498 if (!NT_SUCCESS(Status
)) {
500 throw string_error(IDS_RECV_SET_REPARSE_POINT_FAILED
, Status
, format_ntstatus(Status
).c_str());
505 memset(&bsii
, 0, sizeof(btrfs_set_inode_info
));
507 bsii
.mode_changed
= true;
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
) {
517 if (find_tlv(data
, cmd
->length
, BTRFS_SEND_TLV_MODE
, (void**)&mode
, &modelen
)) {
518 btrfs_set_inode_info bsii
;
520 if (modelen
< sizeof(uint64_t))
521 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"mode", modelen
, sizeof(uint64_t));
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());
528 memset(&bsii
, 0, sizeof(btrfs_set_inode_info
));
530 bsii
.mode_changed
= true;
531 bsii
.st_mode
= (uint32_t)*mode
;
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());
540 void BtrfsRecv::cmd_rename(HWND hwnd
, btrfs_send_command
* cmd
, uint8_t* data
) {
541 wstring pathu
, path_tou
;
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");
550 pathu
= utf8_to_utf16(string(path
, path_len
));
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");
560 path_tou
= utf8_to_utf16(string(path_to
, path_to_len
));
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());
567 void BtrfsRecv::cmd_link(HWND hwnd
, btrfs_send_command
* cmd
, uint8_t* data
) {
568 wstring pathu
, path_linku
;
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");
577 pathu
= utf8_to_utf16(string(path
, path_len
));
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");
587 path_linku
= utf8_to_utf16(string(path_link
, path_link_len
));
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());
594 void BtrfsRecv::cmd_unlink(HWND hwnd
, btrfs_send_command
* cmd
, uint8_t* data
) {
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");
605 pathu
= utf8_to_utf16(string(path
, pathlen
));
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());
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());
617 if (!DeleteFileW((subvolpath
+ pathu
).c_str()))
618 throw string_error(IDS_RECV_DELETEFILE_FAILED
, pathu
.c_str(), GetLastError(), format_message(GetLastError()).c_str());
621 void BtrfsRecv::cmd_rmdir(HWND hwnd
, btrfs_send_command
* cmd
, uint8_t* data
) {
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");
632 pathu
= utf8_to_utf16(string(path
, pathlen
));
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());
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());
644 if (!RemoveDirectoryW((subvolpath
+ pathu
).c_str()))
645 throw string_error(IDS_RECV_REMOVEDIRECTORY_FAILED
, pathu
.c_str(), GetLastError(), format_message(GetLastError()).c_str());
648 void BtrfsRecv::cmd_setxattr(HWND hwnd
, btrfs_send_command
* cmd
, uint8_t* data
) {
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");
661 pathu
= utf8_to_utf16(string(path
, pathlen
));
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");
671 xattrname
= string(xattrnamebuf
, xattrnamelen
);
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");
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
) {
681 auto streamname
= utf8_to_utf16(xattrname
);
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());
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());
692 streamname
= streamname
.substr(XATTR_USER
.length());
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());
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());
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());
709 IO_STATUS_BLOCK iosb
;
711 ULONG perms
= FILE_WRITE_ATTRIBUTES
;
712 btrfs_set_xattr
* bsxa
;
714 if (xattrname
== EA_NTACL
)
715 perms
|= WRITE_DAC
| WRITE_OWNER
;
716 else if (xattrname
== EA_EA
)
717 perms
|= FILE_WRITE_EA
;
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());
724 size_t bsxalen
= offsetof(btrfs_set_xattr
, data
[0]) + xattrname
.length() + xattrdatalen
;
725 bsxa
= (btrfs_set_xattr
*)malloc(bsxalen
);
727 throw string_error(IDS_OUT_OF_MEMORY
);
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
);
734 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_SET_XATTR
, bsxa
, (ULONG
)bsxalen
, nullptr, 0);
735 if (!NT_SUCCESS(Status
)) {
737 throw string_error(IDS_RECV_SETXATTR_FAILED
, Status
, format_ntstatus(Status
).c_str());
744 void BtrfsRecv::cmd_removexattr(HWND hwnd
, btrfs_send_command
* cmd
, uint8_t* data
) {
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");
755 pathu
= utf8_to_utf16(string(path
, pathlen
));
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");
765 xattrname
= string(xattrnamebuf
, xattrnamelen
);
768 if (xattrname
.length() > XATTR_USER
.length() && xattrname
.substr(0, XATTR_USER
.length()) == XATTR_USER
&& xattrname
!= EA_DOSATTRIB
&& xattrname
!= EA_EA
) { // deleting stream
771 auto streamname
= utf8_to_utf16(xattrname
);
773 streamname
= streamname
.substr(XATTR_USER
.length());
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());
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());
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());
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());
792 IO_STATUS_BLOCK iosb
;
794 ULONG perms
= FILE_WRITE_ATTRIBUTES
;
795 btrfs_set_xattr
* bsxa
;
797 if (xattrname
== EA_NTACL
)
798 perms
|= WRITE_DAC
| WRITE_OWNER
;
799 else if (xattrname
== EA_EA
)
800 perms
|= FILE_WRITE_EA
;
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());
807 size_t bsxalen
= offsetof(btrfs_set_xattr
, data
[0]) + xattrname
.length();
808 bsxa
= (btrfs_set_xattr
*)malloc(bsxalen
);
810 throw string_error(IDS_OUT_OF_MEMORY
);
812 bsxa
->namelen
= (uint16_t)(xattrname
.length());
814 memcpy(bsxa
->data
, xattrname
.c_str(), xattrname
.length());
816 Status
= NtFsControlFile(h
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_SET_XATTR
, bsxa
, (ULONG
)bsxalen
, nullptr, 0);
817 if (!NT_SUCCESS(Status
)) {
819 throw string_error(IDS_RECV_SETXATTR_FAILED
, Status
, format_ntstatus(Status
).c_str());
826 void BtrfsRecv::cmd_write(HWND hwnd
, btrfs_send_command
* cmd
, uint8_t* data
) {
829 ULONG offsetlen
, datalen
;
834 IO_STATUS_BLOCK iosb
;
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");
843 pathu
= utf8_to_utf16(string(path
, pathlen
));
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");
849 if (offsetlen
< sizeof(uint64_t))
850 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"offset", offsetlen
, sizeof(uint64_t));
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");
855 if (lastwritepath
!= pathu
) {
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());
863 CloseHandle(lastwritefile
);
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());
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());
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());
879 lastwritepath
= pathu
;
882 memset(&fbi
, 0, sizeof(FILE_BASIC_INFO
));
884 fbi
.LastWriteTime
.QuadPart
= -1;
886 Status
= NtSetInformationFile(h
, &iosb
, &fbi
, sizeof(FILE_BASIC_INFO
), FileBasicInformation
);
887 if (!NT_SUCCESS(Status
))
888 throw ntstatus_error(Status
);
892 offli
.QuadPart
= *offset
;
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());
897 if (!WriteFile(h
, writedata
, datalen
, nullptr, nullptr))
898 throw string_error(IDS_RECV_WRITEFILE_FAILED
, GetLastError(), format_message(GetLastError()).c_str());
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
;
908 IO_STATUS_BLOCK iosb
;
909 WCHAR cloneparw
[MAX_PATH
];
910 DUPLICATE_EXTENTS_DATA ded
;
911 LARGE_INTEGER filesize
;
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");
917 if (offsetlen
< sizeof(uint64_t))
918 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"offset", offsetlen
, sizeof(uint64_t));
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");
923 if (clonelenlen
< sizeof(uint64_t))
924 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"clone_len", clonelenlen
, sizeof(uint64_t));
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");
933 pathu
= utf8_to_utf16(string(path
, pathlen
));
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");
939 if (cloneuuidlen
< sizeof(BTRFS_UUID
))
940 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"clone_uuid", cloneuuidlen
, sizeof(BTRFS_UUID
));
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");
945 if (clonetransidlen
< sizeof(uint64_t))
946 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"clone_ctransid", clonetransidlen
, sizeof(uint64_t));
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");
955 clonepathu
= utf8_to_utf16(string(clonepath
, clonepathlen
));
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");
961 if (cloneoffsetlen
< sizeof(uint64_t))
962 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"clone_offset", cloneoffsetlen
, sizeof(uint64_t));
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
;
973 WCHAR volpathw
[MAX_PATH
];
975 bfs
.uuid
= *cloneuuid
;
976 bfs
.ctransid
= *clonetransid
;
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());
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());
989 if (clonepar
.substr(clonepar
.length() - 1) == L
"\\")
990 clonepar
= clonepar
.substr(0, clonepar
.length() - 1);
992 clonepar
+= cloneparw
;
995 add_cache_entry(cloneuuid
, *clonetransid
, clonepar
);
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());
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());
1009 if (!GetFileSizeEx(dest
, &filesize
))
1010 throw string_error(IDS_RECV_GETFILESIZEEX_FAILED
, GetLastError(), format_message(GetLastError()).c_str());
1012 if ((uint64_t)filesize
.QuadPart
< *offset
+ *clonelen
) {
1013 LARGE_INTEGER sizeli
;
1015 sizeli
.QuadPart
= *offset
+ *clonelen
;
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());
1020 if (!SetEndOfFile(dest
))
1021 throw string_error(IDS_RECV_SETENDOFFILE_FAILED
, GetLastError(), format_message(GetLastError()).c_str());
1024 ded
.FileHandle
= src
;
1025 ded
.SourceFileOffset
.QuadPart
= *cloneoffset
;
1026 ded
.TargetFileOffset
.QuadPart
= *offset
;
1027 ded
.ByteCount
.QuadPart
= *clonelen
;
1029 Status
= NtFsControlFile(dest
, nullptr, nullptr, nullptr, &iosb
, FSCTL_DUPLICATE_EXTENTS_TO_FILE
, &ded
, sizeof(DUPLICATE_EXTENTS_DATA
),
1031 if (!NT_SUCCESS(Status
))
1032 throw string_error(IDS_RECV_DUPLICATE_EXTENTS_FAILED
, Status
, format_ntstatus(Status
).c_str());
1036 void BtrfsRecv::cmd_truncate(HWND hwnd
, btrfs_send_command
* cmd
, uint8_t* data
) {
1040 LARGE_INTEGER sizeli
;
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");
1050 pathu
= utf8_to_utf16(string(path
, pathlen
));
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");
1056 if (sizelen
< sizeof(uint64_t))
1057 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"size", sizelen
, sizeof(uint64_t));
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());
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());
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);
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());
1075 sizeli
.QuadPart
= *size
;
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());
1080 if (!SetEndOfFile(h
))
1081 throw string_error(IDS_RECV_SETENDOFFILE_FAILED
, GetLastError(), format_message(GetLastError()).c_str());
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());
1090 void BtrfsRecv::cmd_chmod(HWND hwnd
, btrfs_send_command
* cmd
, uint8_t* data
) {
1095 btrfs_set_inode_info bsii
;
1097 IO_STATUS_BLOCK iosb
;
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");
1106 pathu
= utf8_to_utf16(string(path
, pathlen
));
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");
1112 if (modelen
< sizeof(uint32_t))
1113 throw string_error(IDS_RECV_SHORT_PARAM
, funcname
, L
"mode", modelen
, sizeof(uint32_t));
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());
1120 memset(&bsii
, 0, sizeof(btrfs_set_inode_info
));
1122 bsii
.mode_changed
= true;
1123 bsii
.st_mode
= *mode
;
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());
1130 void BtrfsRecv::cmd_chown(HWND hwnd
, btrfs_send_command
* cmd
, uint8_t* data
) {
1132 uint32_t *uid
, *gid
;
1133 ULONG uidlen
, gidlen
;
1135 btrfs_set_inode_info bsii
;
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");
1144 pathu
= utf8_to_utf16(string(path
, pathlen
));
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());
1152 memset(&bsii
, 0, sizeof(btrfs_set_inode_info
));
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));
1158 bsii
.uid_changed
= true;
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));
1166 bsii
.gid_changed
= true;
1170 if (bsii
.uid_changed
|| bsii
.gid_changed
) {
1172 IO_STATUS_BLOCK iosb
;
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());
1180 static __inline
uint64_t unix_time_to_win(BTRFS_TIME
* t
) {
1181 return (t
->seconds
* 10000000) + (t
->nanoseconds
/ 100) + 116444736000000000;
1184 void BtrfsRecv::cmd_utimes(HWND hwnd
, btrfs_send_command
* cmd
, uint8_t* data
) {
1187 FILE_BASIC_INFO fbi
;
1190 IO_STATUS_BLOCK iosb
;
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");
1200 pathu
= utf8_to_utf16(string(path
, pathlen
));
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());
1208 memset(&fbi
, 0, sizeof(FILE_BASIC_INFO
));
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
);
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
);
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
);
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
);
1222 Status
= NtSetInformationFile(h
, &iosb
, &fbi
, sizeof(FILE_BASIC_INFO
), FileBasicInformation
);
1223 if (!NT_SUCCESS(Status
))
1224 throw ntstatus_error(Status
);
1227 static void delete_directory(const wstring
& dir
) {
1228 WIN32_FIND_DATAW fff
;
1230 fff_handle h
= FindFirstFileW((dir
+ L
"*").c_str(), &fff
);
1232 if (h
== INVALID_HANDLE_VALUE
)
1238 file
= fff
.cFileName
;
1240 if (file
!= L
"." && file
!= L
"..") {
1241 if (fff
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
)
1242 SetFileAttributesW((dir
+ file
).c_str(), fff
.dwFileAttributes
& ~FILE_ATTRIBUTE_READONLY
);
1244 if (fff
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
1245 if (!(fff
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
))
1246 delete_directory(dir
+ file
+ L
"\\");
1248 RemoveDirectoryW((dir
+ file
).c_str());
1250 DeleteFileW((dir
+ file
).c_str());
1252 } while (FindNextFileW(h
, &fff
));
1254 RemoveDirectoryW(dir
.c_str());
1257 static bool check_csum(btrfs_send_command
* cmd
, uint8_t* data
) {
1258 uint32_t crc32
= cmd
->csum
, calc
;
1262 calc
= calc_crc32c(0, (uint8_t*)cmd
, sizeof(btrfs_send_command
));
1264 if (cmd
->length
> 0)
1265 calc
= calc_crc32c(calc
, data
, cmd
->length
);
1267 return calc
== crc32
? true : false;
1270 void BtrfsRecv::do_recv(const win_handle
& f
, uint64_t* pos
, uint64_t size
, const win_handle
& parent
) {
1272 btrfs_send_header header
;
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());
1278 *pos
+= sizeof(btrfs_send_header
);
1280 if (memcmp(header
.magic
, BTRFS_SEND_MAGIC
, sizeof(header
.magic
)))
1281 throw string_error(IDS_RECV_NOT_A_SEND_STREAM
);
1283 if (header
.version
> 1)
1284 throw string_error(IDS_RECV_UNSUPPORTED_VERSION
, header
.version
);
1286 SendMessageW(GetDlgItem(hwnd
, IDC_RECV_PROGRESS
), PBM_SETRANGE32
, 0, (LPARAM
)65536);
1288 lastwritefile
= INVALID_HANDLE_VALUE
;
1289 lastwritepath
= L
"";
1293 btrfs_send_command cmd
;
1294 uint8_t* data
= nullptr;
1300 progress
= (ULONG
)((float)*pos
* 65536.0f
/ (float)size
);
1301 SendMessageW(GetDlgItem(hwnd
, IDC_RECV_PROGRESS
), PBM_SETPOS
, progress
, 0);
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());
1310 *pos
+= sizeof(btrfs_send_command
);
1312 if (cmd
.length
> 0) {
1313 if (*pos
+ cmd
.length
> size
)
1314 throw string_error(IDS_RECV_FILE_TRUNCATED
);
1316 data
= (uint8_t*)malloc(cmd
.length
);
1318 throw string_error(IDS_OUT_OF_MEMORY
);
1323 if (!ReadFile(f
, data
, cmd
.length
, nullptr, nullptr))
1324 throw string_error(IDS_RECV_READFILE_FAILED
, GetLastError(), format_message(GetLastError()).c_str());
1329 if (!check_csum(&cmd
, data
))
1330 throw string_error(IDS_RECV_CSUM_ERROR
);
1332 if (cmd
.cmd
== BTRFS_SEND_CMD_END
) {
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());
1343 CloseHandle(lastwritefile
);
1345 lastwritefile
= INVALID_HANDLE_VALUE
;
1346 lastwritepath
= L
"";
1351 case BTRFS_SEND_CMD_SUBVOL
:
1352 cmd_subvol(hwnd
, &cmd
, data
, parent
);
1355 case BTRFS_SEND_CMD_SNAPSHOT
:
1356 cmd_snapshot(hwnd
, &cmd
, data
, parent
);
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
);
1368 case BTRFS_SEND_CMD_RENAME
:
1369 cmd_rename(hwnd
, &cmd
, data
);
1372 case BTRFS_SEND_CMD_LINK
:
1373 cmd_link(hwnd
, &cmd
, data
);
1376 case BTRFS_SEND_CMD_UNLINK
:
1377 cmd_unlink(hwnd
, &cmd
, data
);
1380 case BTRFS_SEND_CMD_RMDIR
:
1381 cmd_rmdir(hwnd
, &cmd
, data
);
1384 case BTRFS_SEND_CMD_SET_XATTR
:
1385 cmd_setxattr(hwnd
, &cmd
, data
);
1388 case BTRFS_SEND_CMD_REMOVE_XATTR
:
1389 cmd_removexattr(hwnd
, &cmd
, data
);
1392 case BTRFS_SEND_CMD_WRITE
:
1393 cmd_write(hwnd
, &cmd
, data
);
1396 case BTRFS_SEND_CMD_CLONE
:
1397 cmd_clone(hwnd
, &cmd
, data
);
1400 case BTRFS_SEND_CMD_TRUNCATE
:
1401 cmd_truncate(hwnd
, &cmd
, data
);
1404 case BTRFS_SEND_CMD_CHMOD
:
1405 cmd_chmod(hwnd
, &cmd
, data
);
1408 case BTRFS_SEND_CMD_CHOWN
:
1409 cmd_chown(hwnd
, &cmd
, data
);
1412 case BTRFS_SEND_CMD_UTIMES
:
1413 cmd_utimes(hwnd
, &cmd
, data
);
1416 case BTRFS_SEND_CMD_UPDATE_EXTENT
:
1421 throw string_error(IDS_RECV_UNKNOWN_COMMAND
, cmd
.cmd
);
1424 if (data
) free(data
);
1428 if (data
) free(data
);
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());
1437 CloseHandle(lastwritefile
);
1440 if (!ended
&& !cancelling
)
1441 throw string_error(IDS_RECV_FILE_TRUNCATED
);
1445 IO_STATUS_BLOCK iosb
;
1446 btrfs_received_subvol brs
;
1448 brs
.generation
= stransid
;
1449 brs
.uuid
= subvol_uuid
;
1451 Status
= NtFsControlFile(dir
, nullptr, nullptr, nullptr, &iosb
, FSCTL_BTRFS_RECEIVED_SUBVOL
, &brs
, sizeof(btrfs_received_subvol
),
1453 if (!NT_SUCCESS(Status
))
1454 throw string_error(IDS_RECV_RECEIVED_SUBVOL_FAILED
, Status
, format_ntstatus(Status
).c_str());
1459 if (master
!= INVALID_HANDLE_VALUE
)
1460 CloseHandle(master
);
1462 if (subvolpath
!= L
"") {
1465 attrib
= GetFileAttributesW(subvolpath
.c_str());
1466 attrib
&= ~FILE_ATTRIBUTE_READONLY
;
1468 if (SetFileAttributesW(subvolpath
.c_str(), attrib
))
1469 delete_directory(subvolpath
);
1476 DWORD
BtrfsRecv::recv_thread() {
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());
1488 if (!GetFileSizeEx(f
, &size
))
1489 throw string_error(IDS_RECV_GETFILESIZEEX_FAILED
, GetLastError(), format_message(GetLastError()).c_str());
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());
1498 do_recv(f
, &pos
, size
.QuadPart
, parent
);
1499 } while (pos
< (uint64_t)size
.QuadPart
);
1501 } catch (const exception
& e
) {
1502 auto msg
= utf8_to_utf16(e
.what());
1504 SetDlgItemTextW(hwnd
, IDC_RECV_MSG
, msg
.c_str());
1506 SendMessageW(GetDlgItem(hwnd
, IDC_RECV_PROGRESS
), PBM_SETSTATE
, PBST_ERROR
, 0);
1514 SendMessageW(GetDlgItem(hwnd
, IDC_RECV_PROGRESS
), PBM_SETPOS
, 65536, 0);
1516 if (num_received
== 1) {
1517 load_string(module
, IDS_RECV_SUCCESS
, s
);
1518 SetDlgItemTextW(hwnd
, IDC_RECV_MSG
, s
.c_str());
1522 load_string(module
, IDS_RECV_SUCCESS_PLURAL
, s
);
1524 wstring_sprintf(t
, s
, num_received
);
1526 SetDlgItemTextW(hwnd
, IDC_RECV_MSG
, t
.c_str());
1529 load_string(module
, IDS_RECV_BUTTON_OK
, s
);
1531 SetDlgItemTextW(hwnd
, IDCANCEL
, s
.c_str());
1540 static DWORD WINAPI
global_recv_thread(LPVOID lpParameter
) {
1541 BtrfsRecv
* br
= (BtrfsRecv
*)lpParameter
;
1543 return br
->recv_thread();
1546 INT_PTR CALLBACK
BtrfsRecv::RecvProgressDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
1550 this->hwnd
= hwndDlg
;
1551 thread
= CreateThread(nullptr, 0, global_recv_thread
, this, 0, nullptr);
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());
1558 SetDlgItemTextW(hwnd
, IDC_RECV_MSG
, msg
.c_str());
1560 SendMessageW(GetDlgItem(hwnd
, IDC_RECV_PROGRESS
), PBM_SETSTATE
, PBST_ERROR
, 0);
1565 switch (HIWORD(wParam
)) {
1567 switch (LOWORD(wParam
)) {
1575 if (!load_string(module
, IDS_RECV_CANCELLED
, s
))
1576 throw last_error(GetLastError());
1578 SetDlgItemTextW(hwnd
, IDC_RECV_MSG
, s
.c_str());
1579 SendMessageW(GetDlgItem(hwnd
, IDC_RECV_PROGRESS
), PBM_SETPOS
, 0, 0);
1581 if (!load_string(module
, IDS_RECV_BUTTON_OK
, s
))
1582 throw last_error(GetLastError());
1584 SetDlgItemTextW(hwnd
, IDCANCEL
, s
.c_str());
1586 EndDialog(hwndDlg
, 1);
1598 static INT_PTR CALLBACK
stub_RecvProgressDlgProc(HWND hwndDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
1601 if (uMsg
== WM_INITDIALOG
) {
1602 SetWindowLongPtr(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)lParam
);
1603 br
= (BtrfsRecv
*)lParam
;
1605 br
= (BtrfsRecv
*)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1609 return br
->RecvProgressDlgProc(hwndDlg
, uMsg
, wParam
, lParam
);
1614 void BtrfsRecv::Open(HWND hwnd
, const wstring
& file
, const wstring
& path
, bool quiet
) {
1616 uint32_t cpuInfo
[4];
1625 __get_cpuid(1, &cpuInfo
[0], &cpuInfo
[1], &cpuInfo
[2], &cpuInfo
[3]);
1626 have_sse42
= cpuInfo
[2] & bit_SSE4_2
;
1628 __cpuid((int*)cpuInfo
, 1);
1629 have_sse42
= cpuInfo
[2] & (1 << 20);
1636 if (DialogBoxParamW(module
, MAKEINTRESOURCEW(IDD_RECV_PROGRESS
), hwnd
, stub_RecvProgressDlgProc
, (LPARAM
)this) <= 0)
1637 throw last_error(GetLastError());
1645 void CALLBACK
RecvSubvolGUIW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
1648 WCHAR file
[MAX_PATH
];
1650 TOKEN_PRIVILEGES
* tp
;
1656 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES
| TOKEN_QUERY
, &token
))
1657 throw last_error(GetLastError());
1659 tplen
= offsetof(TOKEN_PRIVILEGES
, Privileges
[0]) + (3 * sizeof(LUID_AND_ATTRIBUTES
));
1660 tp
= (TOKEN_PRIVILEGES
*)malloc(tplen
);
1662 throw string_error(IDS_OUT_OF_MEMORY
);
1664 tp
->PrivilegeCount
= 3;
1666 if (!LookupPrivilegeValueW(nullptr, L
"SeManageVolumePrivilege", &luid
)) {
1668 throw last_error(GetLastError());
1671 tp
->Privileges
[0].Luid
= luid
;
1672 tp
->Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
1674 if (!LookupPrivilegeValueW(nullptr, L
"SeSecurityPrivilege", &luid
)) {
1676 throw last_error(GetLastError());
1679 tp
->Privileges
[1].Luid
= luid
;
1680 tp
->Privileges
[1].Attributes
= SE_PRIVILEGE_ENABLED
;
1682 if (!LookupPrivilegeValueW(nullptr, L
"SeRestorePrivilege", &luid
)) {
1684 throw last_error(GetLastError());
1687 tp
->Privileges
[2].Luid
= luid
;
1688 tp
->Privileges
[2].Attributes
= SE_PRIVILEGE_ENABLED
;
1690 if (!AdjustTokenPrivileges(token
, false, tp
, tplen
, nullptr, nullptr)) {
1692 throw last_error(GetLastError());
1697 memset(&ofn
, 0, sizeof(OPENFILENAMEW
));
1698 ofn
.lStructSize
= sizeof(OPENFILENAMEW
);
1699 ofn
.hwndOwner
= hwnd
;
1700 ofn
.hInstance
= module
;
1701 ofn
.lpstrFile
= file
;
1702 ofn
.nMaxFile
= sizeof(file
) / sizeof(WCHAR
);
1703 ofn
.Flags
= OFN_EXPLORER
| OFN_FILEMUSTEXIST
| OFN_HIDEREADONLY
;
1705 if (GetOpenFileNameW(&ofn
)) {
1708 recv
.Open(hwnd
, file
, lpszCmdLine
, false);
1712 } catch (const exception
& e
) {
1713 error_message(hwnd
, e
.what());
1717 void CALLBACK
RecvSubvolW(HWND hwnd
, HINSTANCE hinst
, LPWSTR lpszCmdLine
, int nCmdShow
) {
1719 vector
<wstring
> args
;
1721 command_line_to_args(lpszCmdLine
, args
);
1723 if (args
.size() >= 2) {
1725 TOKEN_PRIVILEGES
* tp
;
1729 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES
| TOKEN_QUERY
, &token
))
1732 tplen
= offsetof(TOKEN_PRIVILEGES
, Privileges
[0]) + (3 * sizeof(LUID_AND_ATTRIBUTES
));
1733 tp
= (TOKEN_PRIVILEGES
*)malloc(tplen
);
1737 tp
->PrivilegeCount
= 3;
1739 if (!LookupPrivilegeValueW(nullptr, L
"SeManageVolumePrivilege", &luid
)) {
1744 tp
->Privileges
[0].Luid
= luid
;
1745 tp
->Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
1747 if (!LookupPrivilegeValueW(nullptr, L
"SeSecurityPrivilege", &luid
)) {
1752 tp
->Privileges
[1].Luid
= luid
;
1753 tp
->Privileges
[1].Attributes
= SE_PRIVILEGE_ENABLED
;
1755 if (!LookupPrivilegeValueW(nullptr, L
"SeRestorePrivilege", &luid
)) {
1760 tp
->Privileges
[2].Luid
= luid
;
1761 tp
->Privileges
[2].Attributes
= SE_PRIVILEGE_ENABLED
;
1763 if (!AdjustTokenPrivileges(token
, false, tp
, tplen
, nullptr, nullptr)) {
1771 br
.Open(nullptr, args
[0], args
[1], true);
1773 } catch (const exception
& e
) {
1774 cerr
<< "Error: " << e
.what() << endl
;