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