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 "btrfs_drv.h" 19 #include "crc32c.h" 20 21 typedef struct send_dir { 22 LIST_ENTRY list_entry; 23 uint64_t inode; 24 bool dummy; 25 BTRFS_TIME atime; 26 BTRFS_TIME mtime; 27 BTRFS_TIME ctime; 28 struct send_dir* parent; 29 uint16_t namelen; 30 char* name; 31 LIST_ENTRY deleted_children; 32 } send_dir; 33 34 typedef struct { 35 LIST_ENTRY list_entry; 36 uint64_t inode; 37 bool dir; 38 send_dir* sd; 39 char tmpname[64]; 40 } orphan; 41 42 typedef struct { 43 LIST_ENTRY list_entry; 44 ULONG namelen; 45 char name[1]; 46 } deleted_child; 47 48 typedef struct { 49 LIST_ENTRY list_entry; 50 send_dir* sd; 51 uint16_t namelen; 52 char name[1]; 53 } ref; 54 55 typedef struct { 56 send_dir* sd; 57 uint64_t last_child_inode; 58 LIST_ENTRY list_entry; 59 } pending_rmdir; 60 61 typedef struct { 62 uint64_t offset; 63 LIST_ENTRY list_entry; 64 ULONG datalen; 65 EXTENT_DATA data; 66 } send_ext; 67 68 typedef struct { 69 device_extension* Vcb; 70 root* root; 71 root* parent; 72 uint8_t* data; 73 ULONG datalen; 74 ULONG num_clones; 75 root** clones; 76 LIST_ENTRY orphans; 77 LIST_ENTRY dirs; 78 LIST_ENTRY pending_rmdirs; 79 KEVENT buffer_event; 80 send_dir* root_dir; 81 send_info* send; 82 83 struct { 84 uint64_t inode; 85 bool deleting; 86 bool new; 87 uint64_t gen; 88 uint64_t uid; 89 uint64_t olduid; 90 uint64_t gid; 91 uint64_t oldgid; 92 uint64_t mode; 93 uint64_t oldmode; 94 uint64_t size; 95 uint64_t flags; 96 BTRFS_TIME atime; 97 BTRFS_TIME mtime; 98 BTRFS_TIME ctime; 99 bool file; 100 char* path; 101 orphan* o; 102 send_dir* sd; 103 LIST_ENTRY refs; 104 LIST_ENTRY oldrefs; 105 LIST_ENTRY exts; 106 LIST_ENTRY oldexts; 107 } lastinode; 108 } send_context; 109 110 #define MAX_SEND_WRITE 0xc000 // 48 KB 111 #define SEND_BUFFER_LENGTH 0x100000 // 1 MB 112 113 static NTSTATUS find_send_dir(send_context* context, uint64_t dir, uint64_t generation, send_dir** psd, bool* added_dummy); 114 static NTSTATUS wait_for_flush(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2); 115 116 static void send_command(send_context* context, uint16_t cmd) { 117 btrfs_send_command* bsc = (btrfs_send_command*)&context->data[context->datalen]; 118 119 bsc->cmd = cmd; 120 bsc->csum = 0; 121 122 context->datalen += sizeof(btrfs_send_command); 123 } 124 125 static void send_command_finish(send_context* context, ULONG pos) { 126 btrfs_send_command* bsc = (btrfs_send_command*)&context->data[pos]; 127 128 bsc->length = context->datalen - pos - sizeof(btrfs_send_command); 129 bsc->csum = calc_crc32c(0, (uint8_t*)bsc, context->datalen - pos); 130 } 131 132 static void send_add_tlv(send_context* context, uint16_t type, void* data, uint16_t length) { 133 btrfs_send_tlv* tlv = (btrfs_send_tlv*)&context->data[context->datalen]; 134 135 tlv->type = type; 136 tlv->length = length; 137 138 if (length > 0 && data) 139 RtlCopyMemory(&tlv[1], data, length); 140 141 context->datalen += sizeof(btrfs_send_tlv) + length; 142 } 143 144 static char* uint64_to_char(uint64_t num, char* buf) { 145 char *tmp, tmp2[20]; 146 147 if (num == 0) { 148 buf[0] = '0'; 149 return buf + 1; 150 } 151 152 tmp = &tmp2[20]; 153 while (num > 0) { 154 tmp--; 155 *tmp = (num % 10) + '0'; 156 num /= 10; 157 } 158 159 RtlCopyMemory(buf, tmp, tmp2 + sizeof(tmp2) - tmp); 160 161 return &buf[tmp2 + sizeof(tmp2) - tmp]; 162 } 163 164 static NTSTATUS get_orphan_name(send_context* context, uint64_t inode, uint64_t generation, char* name) { 165 char *ptr, *ptr2; 166 uint64_t index = 0; 167 KEY searchkey; 168 169 name[0] = 'o'; 170 171 ptr = uint64_to_char(inode, &name[1]); 172 *ptr = '-'; ptr++; 173 ptr = uint64_to_char(generation, ptr); 174 *ptr = '-'; ptr++; 175 ptr2 = ptr; 176 177 searchkey.obj_id = SUBVOL_ROOT_INODE; 178 searchkey.obj_type = TYPE_DIR_ITEM; 179 180 do { 181 NTSTATUS Status; 182 traverse_ptr tp; 183 184 ptr = uint64_to_char(index, ptr); 185 *ptr = 0; 186 187 searchkey.offset = calc_crc32c(0xfffffffe, (uint8_t*)name, (ULONG)(ptr - name)); 188 189 Status = find_item(context->Vcb, context->root, &tp, &searchkey, false, NULL); 190 if (!NT_SUCCESS(Status)) { 191 ERR("find_item returned %08lx\n", Status); 192 return Status; 193 } 194 195 if (!keycmp(searchkey, tp.item->key)) 196 goto cont; 197 198 if (context->parent) { 199 Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL); 200 if (!NT_SUCCESS(Status)) { 201 ERR("find_item returned %08lx\n", Status); 202 return Status; 203 } 204 205 if (!keycmp(searchkey, tp.item->key)) 206 goto cont; 207 } 208 209 return STATUS_SUCCESS; 210 211 cont: 212 index++; 213 ptr = ptr2; 214 } while (true); 215 } 216 217 static void add_orphan(send_context* context, orphan* o) { 218 LIST_ENTRY* le; 219 220 le = context->orphans.Flink; 221 while (le != &context->orphans) { 222 orphan* o2 = CONTAINING_RECORD(le, orphan, list_entry); 223 224 if (o2->inode > o->inode) { 225 InsertHeadList(o2->list_entry.Blink, &o->list_entry); 226 return; 227 } 228 229 le = le->Flink; 230 } 231 232 InsertTailList(&context->orphans, &o->list_entry); 233 } 234 235 static NTSTATUS send_read_symlink(send_context* context, uint64_t inode, char** link, uint16_t* linklen) { 236 NTSTATUS Status; 237 KEY searchkey; 238 traverse_ptr tp; 239 EXTENT_DATA* ed; 240 241 searchkey.obj_id = inode; 242 searchkey.obj_type = TYPE_EXTENT_DATA; 243 searchkey.offset = 0; 244 245 Status = find_item(context->Vcb, context->root, &tp, &searchkey, false, NULL); 246 if (!NT_SUCCESS(Status)) { 247 ERR("find_item returned %08lx\n", Status); 248 return Status; 249 } 250 251 if (keycmp(tp.item->key, searchkey)) { 252 ERR("could not find (%I64x,%x,%I64x)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 253 return STATUS_INTERNAL_ERROR; 254 } 255 256 if (tp.item->size < sizeof(EXTENT_DATA)) { 257 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, 258 tp.item->size, sizeof(EXTENT_DATA)); 259 return STATUS_INTERNAL_ERROR; 260 } 261 262 ed = (EXTENT_DATA*)tp.item->data; 263 264 if (ed->type != EXTENT_TYPE_INLINE) { 265 WARN("symlink data was not inline, returning blank string\n"); 266 *link = NULL; 267 *linklen = 0; 268 return STATUS_SUCCESS; 269 } 270 271 if (tp.item->size < offsetof(EXTENT_DATA, data[0]) + ed->decoded_size) { 272 ERR("(%I64x,%x,%I64x) was %u bytes, expected %I64u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, 273 tp.item->size, offsetof(EXTENT_DATA, data[0]) + ed->decoded_size); 274 return STATUS_INTERNAL_ERROR; 275 } 276 277 *link = (char*)ed->data; 278 *linklen = (uint16_t)ed->decoded_size; 279 280 return STATUS_SUCCESS; 281 } 282 283 static NTSTATUS send_inode(send_context* context, traverse_ptr* tp, traverse_ptr* tp2) { 284 NTSTATUS Status; 285 INODE_ITEM* ii; 286 287 if (tp2 && !tp) { 288 INODE_ITEM* ii2 = (INODE_ITEM*)tp2->item->data; 289 290 if (tp2->item->size < sizeof(INODE_ITEM)) { 291 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset, 292 tp2->item->size, sizeof(INODE_ITEM)); 293 return STATUS_INTERNAL_ERROR; 294 } 295 296 context->lastinode.inode = tp2->item->key.obj_id; 297 context->lastinode.deleting = true; 298 context->lastinode.gen = ii2->generation; 299 context->lastinode.mode = ii2->st_mode; 300 context->lastinode.flags = ii2->flags; 301 context->lastinode.o = NULL; 302 context->lastinode.sd = NULL; 303 304 return STATUS_SUCCESS; 305 } 306 307 ii = (INODE_ITEM*)tp->item->data; 308 309 if (tp->item->size < sizeof(INODE_ITEM)) { 310 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, 311 tp->item->size, sizeof(INODE_ITEM)); 312 return STATUS_INTERNAL_ERROR; 313 } 314 315 context->lastinode.inode = tp->item->key.obj_id; 316 context->lastinode.deleting = false; 317 context->lastinode.gen = ii->generation; 318 context->lastinode.uid = ii->st_uid; 319 context->lastinode.gid = ii->st_gid; 320 context->lastinode.mode = ii->st_mode; 321 context->lastinode.size = ii->st_size; 322 context->lastinode.atime = ii->st_atime; 323 context->lastinode.mtime = ii->st_mtime; 324 context->lastinode.ctime = ii->st_ctime; 325 context->lastinode.flags = ii->flags; 326 context->lastinode.file = false; 327 context->lastinode.o = NULL; 328 context->lastinode.sd = NULL; 329 330 if (context->lastinode.path) { 331 ExFreePool(context->lastinode.path); 332 context->lastinode.path = NULL; 333 } 334 335 if (tp2) { 336 INODE_ITEM* ii2 = (INODE_ITEM*)tp2->item->data; 337 LIST_ENTRY* le; 338 339 if (tp2->item->size < sizeof(INODE_ITEM)) { 340 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset, 341 tp2->item->size, sizeof(INODE_ITEM)); 342 return STATUS_INTERNAL_ERROR; 343 } 344 345 context->lastinode.oldmode = ii2->st_mode; 346 context->lastinode.olduid = ii2->st_uid; 347 context->lastinode.oldgid = ii2->st_gid; 348 349 if ((ii2->st_mode & __S_IFREG) == __S_IFREG && (ii2->st_mode & __S_IFLNK) != __S_IFLNK && (ii2->st_mode & __S_IFSOCK) != __S_IFSOCK) 350 context->lastinode.file = true; 351 352 context->lastinode.new = false; 353 354 le = context->orphans.Flink; 355 while (le != &context->orphans) { 356 orphan* o2 = CONTAINING_RECORD(le, orphan, list_entry); 357 358 if (o2->inode == tp->item->key.obj_id) { 359 context->lastinode.o = o2; 360 break; 361 } else if (o2->inode > tp->item->key.obj_id) 362 break; 363 364 le = le->Flink; 365 } 366 } else 367 context->lastinode.new = true; 368 369 if (tp->item->key.obj_id == SUBVOL_ROOT_INODE) { 370 send_dir* sd; 371 372 Status = find_send_dir(context, tp->item->key.obj_id, ii->generation, &sd, NULL); 373 if (!NT_SUCCESS(Status)) { 374 ERR("find_send_dir returned %08lx\n", Status); 375 return Status; 376 } 377 378 sd->atime = ii->st_atime; 379 sd->mtime = ii->st_mtime; 380 sd->ctime = ii->st_ctime; 381 context->root_dir = sd; 382 } else if (!tp2) { 383 ULONG pos = context->datalen; 384 uint16_t cmd; 385 send_dir* sd; 386 387 char name[64]; 388 orphan* o; 389 390 // skip creating orphan directory if we've already done so 391 if (ii->st_mode & __S_IFDIR) { 392 LIST_ENTRY* le; 393 394 le = context->orphans.Flink; 395 while (le != &context->orphans) { 396 orphan* o2 = CONTAINING_RECORD(le, orphan, list_entry); 397 398 if (o2->inode == tp->item->key.obj_id) { 399 context->lastinode.o = o2; 400 o2->sd->atime = ii->st_atime; 401 o2->sd->mtime = ii->st_mtime; 402 o2->sd->ctime = ii->st_ctime; 403 o2->sd->dummy = false; 404 return STATUS_SUCCESS; 405 } else if (o2->inode > tp->item->key.obj_id) 406 break; 407 408 le = le->Flink; 409 } 410 } 411 412 if ((ii->st_mode & __S_IFSOCK) == __S_IFSOCK) 413 cmd = BTRFS_SEND_CMD_MKSOCK; 414 else if ((ii->st_mode & __S_IFLNK) == __S_IFLNK) 415 cmd = BTRFS_SEND_CMD_SYMLINK; 416 else if ((ii->st_mode & __S_IFCHR) == __S_IFCHR || (ii->st_mode & __S_IFBLK) == __S_IFBLK) 417 cmd = BTRFS_SEND_CMD_MKNOD; 418 else if ((ii->st_mode & __S_IFDIR) == __S_IFDIR) 419 cmd = BTRFS_SEND_CMD_MKDIR; 420 else if ((ii->st_mode & __S_IFIFO) == __S_IFIFO) 421 cmd = BTRFS_SEND_CMD_MKFIFO; 422 else { 423 cmd = BTRFS_SEND_CMD_MKFILE; 424 context->lastinode.file = true; 425 } 426 427 send_command(context, cmd); 428 429 Status = get_orphan_name(context, tp->item->key.obj_id, ii->generation, name); 430 if (!NT_SUCCESS(Status)) { 431 ERR("get_orphan_name returned %08lx\n", Status); 432 return Status; 433 } 434 435 send_add_tlv(context, BTRFS_SEND_TLV_PATH, name, (uint16_t)strlen(name)); 436 send_add_tlv(context, BTRFS_SEND_TLV_INODE, &tp->item->key.obj_id, sizeof(uint64_t)); 437 438 if (cmd == BTRFS_SEND_CMD_MKNOD || cmd == BTRFS_SEND_CMD_MKFIFO || cmd == BTRFS_SEND_CMD_MKSOCK) { 439 uint64_t rdev = makedev((ii->st_rdev & 0xFFFFFFFFFFF) >> 20, ii->st_rdev & 0xFFFFF), mode = ii->st_mode; 440 441 send_add_tlv(context, BTRFS_SEND_TLV_RDEV, &rdev, sizeof(uint64_t)); 442 send_add_tlv(context, BTRFS_SEND_TLV_MODE, &mode, sizeof(uint64_t)); 443 } else if (cmd == BTRFS_SEND_CMD_SYMLINK && ii->st_size > 0) { 444 char* link; 445 uint16_t linklen; 446 447 Status = send_read_symlink(context, tp->item->key.obj_id, &link, &linklen); 448 if (!NT_SUCCESS(Status)) { 449 ERR("send_read_symlink returned %08lx\n", Status); 450 return Status; 451 } 452 453 send_add_tlv(context, BTRFS_SEND_TLV_PATH_LINK, link, linklen); 454 } 455 456 send_command_finish(context, pos); 457 458 if (ii->st_mode & __S_IFDIR) { 459 Status = find_send_dir(context, tp->item->key.obj_id, ii->generation, &sd, NULL); 460 if (!NT_SUCCESS(Status)) { 461 ERR("find_send_dir returned %08lx\n", Status); 462 return Status; 463 } 464 465 sd->dummy = false; 466 } else 467 sd = NULL; 468 469 context->lastinode.sd = sd; 470 471 o = ExAllocatePoolWithTag(PagedPool, sizeof(orphan), ALLOC_TAG); 472 if (!o) { 473 ERR("out of memory\n"); 474 return STATUS_INSUFFICIENT_RESOURCES; 475 } 476 477 o->inode = tp->item->key.obj_id; 478 o->dir = (ii->st_mode & __S_IFDIR && ii->st_size > 0) ? true : false; 479 strcpy(o->tmpname, name); 480 o->sd = sd; 481 add_orphan(context, o); 482 483 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, strlen(o->tmpname) + 1, ALLOC_TAG); 484 if (!context->lastinode.path) { 485 ERR("out of memory\n"); 486 return STATUS_INSUFFICIENT_RESOURCES; 487 } 488 489 strcpy(context->lastinode.path, o->tmpname); 490 491 context->lastinode.o = o; 492 } 493 494 return STATUS_SUCCESS; 495 } 496 497 static NTSTATUS send_add_dir(send_context* context, uint64_t inode, send_dir* parent, char* name, uint16_t namelen, bool dummy, LIST_ENTRY* lastentry, send_dir** psd) { 498 LIST_ENTRY* le; 499 send_dir* sd = ExAllocatePoolWithTag(PagedPool, sizeof(send_dir), ALLOC_TAG); 500 501 if (!sd) { 502 ERR("out of memory\n"); 503 return STATUS_INSUFFICIENT_RESOURCES; 504 } 505 506 sd->inode = inode; 507 sd->dummy = dummy; 508 sd->parent = parent; 509 510 if (!dummy) { 511 sd->atime = context->lastinode.atime; 512 sd->mtime = context->lastinode.mtime; 513 sd->ctime = context->lastinode.ctime; 514 } 515 516 if (namelen > 0) { 517 sd->name = ExAllocatePoolWithTag(PagedPool, namelen, ALLOC_TAG); 518 if (!sd->name) { 519 ERR("out of memory\n"); 520 ExFreePool(sd); 521 return STATUS_INSUFFICIENT_RESOURCES; 522 } 523 524 memcpy(sd->name, name, namelen); 525 } else 526 sd->name = NULL; 527 528 sd->namelen = namelen; 529 530 InitializeListHead(&sd->deleted_children); 531 532 if (lastentry) 533 InsertHeadList(lastentry, &sd->list_entry); 534 else { 535 le = context->dirs.Flink; 536 while (le != &context->dirs) { 537 send_dir* sd2 = CONTAINING_RECORD(le, send_dir, list_entry); 538 539 if (sd2->inode > sd->inode) { 540 InsertHeadList(sd2->list_entry.Blink, &sd->list_entry); 541 542 if (psd) 543 *psd = sd; 544 545 return STATUS_SUCCESS; 546 } 547 548 le = le->Flink; 549 } 550 551 InsertTailList(&context->dirs, &sd->list_entry); 552 } 553 554 if (psd) 555 *psd = sd; 556 557 return STATUS_SUCCESS; 558 } 559 560 static __inline uint16_t find_path_len(send_dir* parent, uint16_t namelen) { 561 uint16_t len = namelen; 562 563 while (parent && parent->namelen > 0) { 564 len += parent->namelen + 1; 565 parent = parent->parent; 566 } 567 568 return len; 569 } 570 571 static void find_path(char* path, send_dir* parent, char* name, ULONG namelen) { 572 ULONG len = namelen; 573 574 RtlCopyMemory(path, name, namelen); 575 576 while (parent && parent->namelen > 0) { 577 RtlMoveMemory(path + parent->namelen + 1, path, len); 578 RtlCopyMemory(path, parent->name, parent->namelen); 579 path[parent->namelen] = '/'; 580 len += parent->namelen + 1; 581 582 parent = parent->parent; 583 } 584 } 585 586 static void send_add_tlv_path(send_context* context, uint16_t type, send_dir* parent, char* name, uint16_t namelen) { 587 uint16_t len = find_path_len(parent, namelen); 588 589 send_add_tlv(context, type, NULL, len); 590 591 if (len > 0) 592 find_path((char*)&context->data[context->datalen - len], parent, name, namelen); 593 } 594 595 static NTSTATUS found_path(send_context* context, send_dir* parent, char* name, uint16_t namelen) { 596 ULONG pos = context->datalen; 597 598 if (context->lastinode.o) { 599 send_command(context, BTRFS_SEND_CMD_RENAME); 600 601 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, context->root_dir, context->lastinode.o->tmpname, (uint16_t)strlen(context->lastinode.o->tmpname)); 602 603 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH_TO, parent, name, namelen); 604 605 send_command_finish(context, pos); 606 } else { 607 send_command(context, BTRFS_SEND_CMD_LINK); 608 609 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, parent, name, namelen); 610 611 send_add_tlv(context, BTRFS_SEND_TLV_PATH_LINK, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 612 613 send_command_finish(context, pos); 614 } 615 616 if (context->lastinode.o) { 617 uint16_t pathlen; 618 619 if (context->lastinode.o->sd) { 620 if (context->lastinode.o->sd->name) 621 ExFreePool(context->lastinode.o->sd->name); 622 623 context->lastinode.o->sd->name = ExAllocatePoolWithTag(PagedPool, namelen, ALLOC_TAG); 624 if (!context->lastinode.o->sd->name) { 625 ERR("out of memory\n"); 626 return STATUS_INSUFFICIENT_RESOURCES; 627 } 628 629 RtlCopyMemory(context->lastinode.o->sd->name, name, namelen); 630 context->lastinode.o->sd->namelen = namelen; 631 context->lastinode.o->sd->parent = parent; 632 } 633 634 if (context->lastinode.path) 635 ExFreePool(context->lastinode.path); 636 637 pathlen = find_path_len(parent, namelen); 638 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, pathlen + 1, ALLOC_TAG); 639 if (!context->lastinode.path) { 640 ERR("out of memory\n"); 641 return STATUS_INSUFFICIENT_RESOURCES; 642 } 643 644 find_path(context->lastinode.path, parent, name, namelen); 645 context->lastinode.path[pathlen] = 0; 646 647 RemoveEntryList(&context->lastinode.o->list_entry); 648 ExFreePool(context->lastinode.o); 649 650 context->lastinode.o = NULL; 651 } 652 653 return STATUS_SUCCESS; 654 } 655 656 static void send_utimes_command_dir(send_context* context, send_dir* sd, BTRFS_TIME* atime, BTRFS_TIME* mtime, BTRFS_TIME* ctime) { 657 ULONG pos = context->datalen; 658 659 send_command(context, BTRFS_SEND_CMD_UTIMES); 660 661 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, sd->parent, sd->name, sd->namelen); 662 663 send_add_tlv(context, BTRFS_SEND_TLV_ATIME, atime, sizeof(BTRFS_TIME)); 664 send_add_tlv(context, BTRFS_SEND_TLV_MTIME, mtime, sizeof(BTRFS_TIME)); 665 send_add_tlv(context, BTRFS_SEND_TLV_CTIME, ctime, sizeof(BTRFS_TIME)); 666 667 send_command_finish(context, pos); 668 } 669 670 static NTSTATUS find_send_dir(send_context* context, uint64_t dir, uint64_t generation, send_dir** psd, bool* added_dummy) { 671 NTSTATUS Status; 672 LIST_ENTRY* le; 673 char name[64]; 674 675 le = context->dirs.Flink; 676 while (le != &context->dirs) { 677 send_dir* sd2 = CONTAINING_RECORD(le, send_dir, list_entry); 678 679 if (sd2->inode > dir) 680 break; 681 else if (sd2->inode == dir) { 682 *psd = sd2; 683 684 if (added_dummy) 685 *added_dummy = false; 686 687 return STATUS_SUCCESS; 688 } 689 690 le = le->Flink; 691 } 692 693 if (dir == SUBVOL_ROOT_INODE) { 694 Status = send_add_dir(context, dir, NULL, NULL, 0, false, le, psd); 695 if (!NT_SUCCESS(Status)) { 696 ERR("send_add_dir returned %08lx\n", Status); 697 return Status; 698 } 699 700 if (added_dummy) 701 *added_dummy = false; 702 703 return STATUS_SUCCESS; 704 } 705 706 if (context->parent) { 707 KEY searchkey; 708 traverse_ptr tp; 709 710 searchkey.obj_id = dir; 711 searchkey.obj_type = TYPE_INODE_REF; // directories should never have an extiref 712 searchkey.offset = 0xffffffffffffffff; 713 714 Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL); 715 if (!NT_SUCCESS(Status)) { 716 ERR("find_item returned %08lx\n", Status); 717 return Status; 718 } 719 720 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { 721 INODE_REF* ir = (INODE_REF*)tp.item->data; 722 send_dir* parent; 723 724 if (tp.item->size < sizeof(INODE_REF) || tp.item->size < offsetof(INODE_REF, name[0]) + ir->n) { 725 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 726 return STATUS_INTERNAL_ERROR; 727 } 728 729 if (tp.item->key.offset == SUBVOL_ROOT_INODE) 730 parent = context->root_dir; 731 else { 732 Status = find_send_dir(context, tp.item->key.offset, generation, &parent, NULL); 733 if (!NT_SUCCESS(Status)) { 734 ERR("find_send_dir returned %08lx\n", Status); 735 return Status; 736 } 737 } 738 739 Status = send_add_dir(context, dir, parent, ir->name, ir->n, true, NULL, psd); 740 if (!NT_SUCCESS(Status)) { 741 ERR("send_add_dir returned %08lx\n", Status); 742 return Status; 743 } 744 745 if (added_dummy) 746 *added_dummy = false; 747 748 return STATUS_SUCCESS; 749 } 750 } 751 752 Status = get_orphan_name(context, dir, generation, name); 753 if (!NT_SUCCESS(Status)) { 754 ERR("get_orphan_name returned %08lx\n", Status); 755 return Status; 756 } 757 758 Status = send_add_dir(context, dir, NULL, name, (uint16_t)strlen(name), true, le, psd); 759 if (!NT_SUCCESS(Status)) { 760 ERR("send_add_dir returned %08lx\n", Status); 761 return Status; 762 } 763 764 if (added_dummy) 765 *added_dummy = true; 766 767 return STATUS_SUCCESS; 768 } 769 770 static NTSTATUS send_inode_ref(send_context* context, traverse_ptr* tp, bool tree2) { 771 NTSTATUS Status; 772 uint64_t inode = tp ? tp->item->key.obj_id : 0, dir = tp ? tp->item->key.offset : 0; 773 LIST_ENTRY* le; 774 INODE_REF* ir; 775 uint16_t len; 776 send_dir* sd = NULL; 777 orphan* o2 = NULL; 778 779 if (inode == dir) // root 780 return STATUS_SUCCESS; 781 782 if (tp->item->size < sizeof(INODE_REF)) { 783 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, 784 tp->item->size, sizeof(INODE_REF)); 785 return STATUS_INTERNAL_ERROR; 786 } 787 788 if (dir != SUBVOL_ROOT_INODE) { 789 bool added_dummy; 790 791 Status = find_send_dir(context, dir, context->root->root_item.ctransid, &sd, &added_dummy); 792 if (!NT_SUCCESS(Status)) { 793 ERR("find_send_dir returned %08lx\n", Status); 794 return Status; 795 } 796 797 // directory has higher inode number than file, so might need to be created 798 if (added_dummy) { 799 bool found = false; 800 801 le = context->orphans.Flink; 802 while (le != &context->orphans) { 803 o2 = CONTAINING_RECORD(le, orphan, list_entry); 804 805 if (o2->inode == dir) { 806 found = true; 807 break; 808 } else if (o2->inode > dir) 809 break; 810 811 le = le->Flink; 812 } 813 814 if (!found) { 815 ULONG pos = context->datalen; 816 817 send_command(context, BTRFS_SEND_CMD_MKDIR); 818 819 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, NULL, sd->name, sd->namelen); 820 821 send_add_tlv(context, BTRFS_SEND_TLV_INODE, &dir, sizeof(uint64_t)); 822 823 send_command_finish(context, pos); 824 825 o2 = ExAllocatePoolWithTag(PagedPool, sizeof(orphan), ALLOC_TAG); 826 if (!o2) { 827 ERR("out of memory\n"); 828 return STATUS_INSUFFICIENT_RESOURCES; 829 } 830 831 o2->inode = dir; 832 o2->dir = true; 833 memcpy(o2->tmpname, sd->name, sd->namelen); 834 o2->tmpname[sd->namelen] = 0; 835 o2->sd = sd; 836 add_orphan(context, o2); 837 } 838 } 839 } else 840 sd = context->root_dir; 841 842 len = tp->item->size; 843 ir = (INODE_REF*)tp->item->data; 844 845 while (len > 0) { 846 ref* r; 847 848 if (len < sizeof(INODE_REF) || len < offsetof(INODE_REF, name[0]) + ir->n) { 849 ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset); 850 return STATUS_INTERNAL_ERROR; 851 } 852 853 r = ExAllocatePoolWithTag(PagedPool, offsetof(ref, name[0]) + ir->n, ALLOC_TAG); 854 if (!r) { 855 ERR("out of memory\n"); 856 return STATUS_INSUFFICIENT_RESOURCES; 857 } 858 859 r->sd = sd; 860 r->namelen = ir->n; 861 RtlCopyMemory(r->name, ir->name, ir->n); 862 863 InsertTailList(tree2 ? &context->lastinode.oldrefs : &context->lastinode.refs, &r->list_entry); 864 865 len -= (uint16_t)offsetof(INODE_REF, name[0]) + ir->n; 866 ir = (INODE_REF*)&ir->name[ir->n]; 867 } 868 869 return STATUS_SUCCESS; 870 } 871 872 static NTSTATUS send_inode_extref(send_context* context, traverse_ptr* tp, bool tree2) { 873 INODE_EXTREF* ier; 874 uint16_t len; 875 876 if (tp->item->size < sizeof(INODE_EXTREF)) { 877 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, 878 tp->item->size, sizeof(INODE_EXTREF)); 879 return STATUS_INTERNAL_ERROR; 880 } 881 882 len = tp->item->size; 883 ier = (INODE_EXTREF*)tp->item->data; 884 885 while (len > 0) { 886 NTSTATUS Status; 887 send_dir* sd = NULL; 888 orphan* o2 = NULL; 889 ref* r; 890 891 if (len < sizeof(INODE_EXTREF) || len < offsetof(INODE_EXTREF, name[0]) + ier->n) { 892 ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset); 893 return STATUS_INTERNAL_ERROR; 894 } 895 896 if (ier->dir != SUBVOL_ROOT_INODE) { 897 LIST_ENTRY* le; 898 bool added_dummy; 899 900 Status = find_send_dir(context, ier->dir, context->root->root_item.ctransid, &sd, &added_dummy); 901 if (!NT_SUCCESS(Status)) { 902 ERR("find_send_dir returned %08lx\n", Status); 903 return Status; 904 } 905 906 // directory has higher inode number than file, so might need to be created 907 if (added_dummy) { 908 bool found = false; 909 910 le = context->orphans.Flink; 911 while (le != &context->orphans) { 912 o2 = CONTAINING_RECORD(le, orphan, list_entry); 913 914 if (o2->inode == ier->dir) { 915 found = true; 916 break; 917 } else if (o2->inode > ier->dir) 918 break; 919 920 le = le->Flink; 921 } 922 923 if (!found) { 924 ULONG pos = context->datalen; 925 926 send_command(context, BTRFS_SEND_CMD_MKDIR); 927 928 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, NULL, sd->name, sd->namelen); 929 send_add_tlv(context, BTRFS_SEND_TLV_INODE, &ier->dir, sizeof(uint64_t)); 930 931 send_command_finish(context, pos); 932 933 o2 = ExAllocatePoolWithTag(PagedPool, sizeof(orphan), ALLOC_TAG); 934 if (!o2) { 935 ERR("out of memory\n"); 936 return STATUS_INSUFFICIENT_RESOURCES; 937 } 938 939 o2->inode = ier->dir; 940 o2->dir = true; 941 memcpy(o2->tmpname, sd->name, sd->namelen); 942 o2->tmpname[sd->namelen] = 0; 943 o2->sd = sd; 944 add_orphan(context, o2); 945 } 946 } 947 } else 948 sd = context->root_dir; 949 950 r = ExAllocatePoolWithTag(PagedPool, offsetof(ref, name[0]) + ier->n, ALLOC_TAG); 951 if (!r) { 952 ERR("out of memory\n"); 953 return STATUS_INSUFFICIENT_RESOURCES; 954 } 955 956 r->sd = sd; 957 r->namelen = ier->n; 958 RtlCopyMemory(r->name, ier->name, ier->n); 959 960 InsertTailList(tree2 ? &context->lastinode.oldrefs : &context->lastinode.refs, &r->list_entry); 961 962 len -= (uint16_t)offsetof(INODE_EXTREF, name[0]) + ier->n; 963 ier = (INODE_EXTREF*)&ier->name[ier->n]; 964 } 965 966 return STATUS_SUCCESS; 967 } 968 969 static void send_subvol_header(send_context* context, root* r, file_ref* fr) { 970 ULONG pos = context->datalen; 971 972 send_command(context, context->parent ? BTRFS_SEND_CMD_SNAPSHOT : BTRFS_SEND_CMD_SUBVOL); 973 974 send_add_tlv(context, BTRFS_SEND_TLV_PATH, fr->dc->utf8.Buffer, fr->dc->utf8.Length); 975 976 send_add_tlv(context, BTRFS_SEND_TLV_UUID, r->root_item.rtransid == 0 ? &r->root_item.uuid : &r->root_item.received_uuid, sizeof(BTRFS_UUID)); 977 send_add_tlv(context, BTRFS_SEND_TLV_TRANSID, &r->root_item.ctransid, sizeof(uint64_t)); 978 979 if (context->parent) { 980 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_UUID, 981 context->parent->root_item.rtransid == 0 ? &context->parent->root_item.uuid : &context->parent->root_item.received_uuid, sizeof(BTRFS_UUID)); 982 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_CTRANSID, &context->parent->root_item.ctransid, sizeof(uint64_t)); 983 } 984 985 send_command_finish(context, pos); 986 } 987 988 static void send_chown_command(send_context* context, char* path, uint64_t uid, uint64_t gid) { 989 ULONG pos = context->datalen; 990 991 send_command(context, BTRFS_SEND_CMD_CHOWN); 992 993 send_add_tlv(context, BTRFS_SEND_TLV_PATH, path, path ? (uint16_t)strlen(path) : 0); 994 send_add_tlv(context, BTRFS_SEND_TLV_UID, &uid, sizeof(uint64_t)); 995 send_add_tlv(context, BTRFS_SEND_TLV_GID, &gid, sizeof(uint64_t)); 996 997 send_command_finish(context, pos); 998 } 999 1000 static void send_chmod_command(send_context* context, char* path, uint64_t mode) { 1001 ULONG pos = context->datalen; 1002 1003 send_command(context, BTRFS_SEND_CMD_CHMOD); 1004 1005 mode &= 07777; 1006 1007 send_add_tlv(context, BTRFS_SEND_TLV_PATH, path, path ? (uint16_t)strlen(path) : 0); 1008 send_add_tlv(context, BTRFS_SEND_TLV_MODE, &mode, sizeof(uint64_t)); 1009 1010 send_command_finish(context, pos); 1011 } 1012 1013 static void send_utimes_command(send_context* context, char* path, BTRFS_TIME* atime, BTRFS_TIME* mtime, BTRFS_TIME* ctime) { 1014 ULONG pos = context->datalen; 1015 1016 send_command(context, BTRFS_SEND_CMD_UTIMES); 1017 1018 send_add_tlv(context, BTRFS_SEND_TLV_PATH, path, path ? (uint16_t)strlen(path) : 0); 1019 send_add_tlv(context, BTRFS_SEND_TLV_ATIME, atime, sizeof(BTRFS_TIME)); 1020 send_add_tlv(context, BTRFS_SEND_TLV_MTIME, mtime, sizeof(BTRFS_TIME)); 1021 send_add_tlv(context, BTRFS_SEND_TLV_CTIME, ctime, sizeof(BTRFS_TIME)); 1022 1023 send_command_finish(context, pos); 1024 } 1025 1026 static void send_truncate_command(send_context* context, char* path, uint64_t size) { 1027 ULONG pos = context->datalen; 1028 1029 send_command(context, BTRFS_SEND_CMD_TRUNCATE); 1030 1031 send_add_tlv(context, BTRFS_SEND_TLV_PATH, path, path ? (uint16_t)strlen(path) : 0); 1032 send_add_tlv(context, BTRFS_SEND_TLV_SIZE, &size, sizeof(uint64_t)); 1033 1034 send_command_finish(context, pos); 1035 } 1036 1037 static NTSTATUS send_unlink_command(send_context* context, send_dir* parent, uint16_t namelen, char* name) { 1038 ULONG pos = context->datalen; 1039 uint16_t pathlen; 1040 1041 send_command(context, BTRFS_SEND_CMD_UNLINK); 1042 1043 pathlen = find_path_len(parent, namelen); 1044 send_add_tlv(context, BTRFS_SEND_TLV_PATH, NULL, pathlen); 1045 1046 find_path((char*)&context->data[context->datalen - pathlen], parent, name, namelen); 1047 1048 send_command_finish(context, pos); 1049 1050 return STATUS_SUCCESS; 1051 } 1052 1053 static void send_rmdir_command(send_context* context, uint16_t pathlen, char* path) { 1054 ULONG pos = context->datalen; 1055 1056 send_command(context, BTRFS_SEND_CMD_RMDIR); 1057 send_add_tlv(context, BTRFS_SEND_TLV_PATH, path, pathlen); 1058 send_command_finish(context, pos); 1059 } 1060 1061 static NTSTATUS get_dir_last_child(send_context* context, uint64_t* last_inode) { 1062 NTSTATUS Status; 1063 KEY searchkey; 1064 traverse_ptr tp; 1065 1066 *last_inode = 0; 1067 1068 searchkey.obj_id = context->lastinode.inode; 1069 searchkey.obj_type = TYPE_DIR_INDEX; 1070 searchkey.offset = 2; 1071 1072 Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL); 1073 if (!NT_SUCCESS(Status)) { 1074 ERR("find_item returned %08lx\n", Status); 1075 return Status; 1076 } 1077 1078 do { 1079 traverse_ptr next_tp; 1080 1081 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { 1082 DIR_ITEM* di = (DIR_ITEM*)tp.item->data; 1083 1084 if (tp.item->size < sizeof(DIR_ITEM) || tp.item->size < offsetof(DIR_ITEM, name[0]) + di->m + di->n) { 1085 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 1086 return STATUS_INTERNAL_ERROR; 1087 } 1088 1089 if (di->key.obj_type == TYPE_INODE_ITEM) 1090 *last_inode = max(*last_inode, di->key.obj_id); 1091 } else 1092 break; 1093 1094 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL)) 1095 tp = next_tp; 1096 else 1097 break; 1098 } while (true); 1099 1100 return STATUS_SUCCESS; 1101 } 1102 1103 static NTSTATUS add_pending_rmdir(send_context* context, uint64_t last_inode) { 1104 pending_rmdir* pr; 1105 LIST_ENTRY* le; 1106 1107 pr = ExAllocatePoolWithTag(PagedPool, sizeof(pending_rmdir), ALLOC_TAG); 1108 if (!pr) { 1109 ERR("out of memory\n"); 1110 return STATUS_INSUFFICIENT_RESOURCES; 1111 } 1112 1113 pr->sd = context->lastinode.sd; 1114 pr->last_child_inode = last_inode; 1115 1116 le = context->pending_rmdirs.Flink; 1117 while (le != &context->pending_rmdirs) { 1118 pending_rmdir* pr2 = CONTAINING_RECORD(le, pending_rmdir, list_entry); 1119 1120 if (pr2->last_child_inode > pr->last_child_inode) { 1121 InsertHeadList(pr2->list_entry.Blink, &pr->list_entry); 1122 return STATUS_SUCCESS; 1123 } 1124 1125 le = le->Flink; 1126 } 1127 1128 InsertTailList(&context->pending_rmdirs, &pr->list_entry); 1129 1130 return STATUS_SUCCESS; 1131 } 1132 1133 static NTSTATUS look_for_collision(send_context* context, send_dir* sd, char* name, ULONG namelen, uint64_t* inode, bool* dir) { 1134 NTSTATUS Status; 1135 KEY searchkey; 1136 traverse_ptr tp; 1137 DIR_ITEM* di; 1138 uint16_t len; 1139 1140 searchkey.obj_id = sd->inode; 1141 searchkey.obj_type = TYPE_DIR_ITEM; 1142 searchkey.offset = calc_crc32c(0xfffffffe, (uint8_t*)name, namelen); 1143 1144 Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL); 1145 if (!NT_SUCCESS(Status)) { 1146 ERR("find_item returned %08lx\n", Status); 1147 return Status; 1148 } 1149 1150 if (keycmp(tp.item->key, searchkey)) 1151 return STATUS_SUCCESS; 1152 1153 di = (DIR_ITEM*)tp.item->data; 1154 len = tp.item->size; 1155 1156 do { 1157 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) { 1158 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 1159 return STATUS_INTERNAL_ERROR; 1160 } 1161 1162 if (di->n == namelen && RtlCompareMemory(di->name, name, namelen) == namelen) { 1163 *inode = di->key.obj_type == TYPE_INODE_ITEM ? di->key.obj_id : 0; 1164 *dir = di->type == BTRFS_TYPE_DIRECTORY ? true: false; 1165 return STATUS_OBJECT_NAME_COLLISION; 1166 } 1167 1168 di = (DIR_ITEM*)&di->name[di->m + di->n]; 1169 len -= (uint16_t)offsetof(DIR_ITEM, name[0]) + di->m + di->n; 1170 } while (len > 0); 1171 1172 return STATUS_SUCCESS; 1173 } 1174 1175 static NTSTATUS make_file_orphan(send_context* context, uint64_t inode, bool dir, uint64_t generation, ref* r) { 1176 NTSTATUS Status; 1177 ULONG pos = context->datalen; 1178 send_dir* sd = NULL; 1179 orphan* o; 1180 LIST_ENTRY* le; 1181 char name[64]; 1182 1183 if (!dir) { 1184 deleted_child* dc; 1185 1186 dc = ExAllocatePoolWithTag(PagedPool, offsetof(deleted_child, name[0]) + r->namelen, ALLOC_TAG); 1187 if (!dc) { 1188 ERR("out of memory\n"); 1189 return STATUS_INSUFFICIENT_RESOURCES; 1190 } 1191 1192 dc->namelen = r->namelen; 1193 RtlCopyMemory(dc->name, r->name, r->namelen); 1194 InsertTailList(&r->sd->deleted_children, &dc->list_entry); 1195 } 1196 1197 le = context->orphans.Flink; 1198 while (le != &context->orphans) { 1199 orphan* o2 = CONTAINING_RECORD(le, orphan, list_entry); 1200 1201 if (o2->inode == inode) { 1202 send_command(context, BTRFS_SEND_CMD_UNLINK); 1203 1204 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, r->sd, r->name, r->namelen); 1205 1206 send_command_finish(context, pos); 1207 1208 return STATUS_SUCCESS; 1209 } else if (o2->inode > inode) 1210 break; 1211 1212 le = le->Flink; 1213 } 1214 1215 Status = get_orphan_name(context, inode, generation, name); 1216 if (!NT_SUCCESS(Status)) { 1217 ERR("get_orphan_name returned %08lx\n", Status); 1218 return Status; 1219 } 1220 1221 if (dir) { 1222 Status = find_send_dir(context, inode, generation, &sd, NULL); 1223 if (!NT_SUCCESS(Status)) { 1224 ERR("find_send_dir returned %08lx\n", Status); 1225 return Status; 1226 } 1227 1228 sd->dummy = true; 1229 1230 send_command(context, BTRFS_SEND_CMD_RENAME); 1231 1232 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, r->sd, r->name, r->namelen); 1233 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH_TO, context->root_dir, name, (uint16_t)strlen(name)); 1234 1235 send_command_finish(context, pos); 1236 1237 if (sd->name) 1238 ExFreePool(sd->name); 1239 1240 sd->namelen = (uint16_t)strlen(name); 1241 sd->name = ExAllocatePoolWithTag(PagedPool, sd->namelen, ALLOC_TAG); 1242 if (!sd->name) { 1243 ERR("out of memory\n"); 1244 return STATUS_INSUFFICIENT_RESOURCES; 1245 } 1246 1247 RtlCopyMemory(sd->name, name, sd->namelen); 1248 sd->parent = context->root_dir; 1249 } else { 1250 send_command(context, BTRFS_SEND_CMD_RENAME); 1251 1252 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, r->sd, r->name, r->namelen); 1253 1254 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH_TO, context->root_dir, name, (uint16_t)strlen(name)); 1255 1256 send_command_finish(context, pos); 1257 } 1258 1259 o = ExAllocatePoolWithTag(PagedPool, sizeof(orphan), ALLOC_TAG); 1260 if (!o) { 1261 ERR("out of memory\n"); 1262 return STATUS_INSUFFICIENT_RESOURCES; 1263 } 1264 1265 o->inode = inode; 1266 o->dir = true; 1267 strcpy(o->tmpname, name); 1268 o->sd = sd; 1269 add_orphan(context, o); 1270 1271 return STATUS_SUCCESS; 1272 } 1273 1274 static NTSTATUS flush_refs(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2) { 1275 NTSTATUS Status; 1276 LIST_ENTRY* le; 1277 ref *nameref = NULL, *nameref2 = NULL; 1278 1279 if (context->lastinode.mode & __S_IFDIR) { // directory 1280 ref* r = IsListEmpty(&context->lastinode.refs) ? NULL : CONTAINING_RECORD(context->lastinode.refs.Flink, ref, list_entry); 1281 ref* or = IsListEmpty(&context->lastinode.oldrefs) ? NULL : CONTAINING_RECORD(context->lastinode.oldrefs.Flink, ref, list_entry); 1282 1283 if (or && !context->lastinode.o) { 1284 ULONG len = find_path_len(or->sd, or->namelen); 1285 1286 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG); 1287 if (!context->lastinode.path) { 1288 ERR("out of memory\n"); 1289 return STATUS_INSUFFICIENT_RESOURCES; 1290 } 1291 1292 find_path(context->lastinode.path, or->sd, or->name, or->namelen); 1293 context->lastinode.path[len] = 0; 1294 1295 if (!context->lastinode.sd) { 1296 Status = find_send_dir(context, context->lastinode.inode, context->lastinode.gen, &context->lastinode.sd, false); 1297 if (!NT_SUCCESS(Status)) { 1298 ERR("find_send_dir returned %08lx\n", Status); 1299 return Status; 1300 } 1301 } 1302 } 1303 1304 if (r && or) { 1305 uint64_t inode; 1306 bool dir; 1307 1308 Status = look_for_collision(context, r->sd, r->name, r->namelen, &inode, &dir); 1309 if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION) { 1310 ERR("look_for_collision returned %08lx\n", Status); 1311 return Status; 1312 } 1313 1314 if (Status == STATUS_OBJECT_NAME_COLLISION && inode > context->lastinode.inode) { 1315 Status = make_file_orphan(context, inode, dir, context->parent->root_item.ctransid, r); 1316 if (!NT_SUCCESS(Status)) { 1317 ERR("make_file_orphan returned %08lx\n", Status); 1318 return Status; 1319 } 1320 } 1321 1322 if (context->lastinode.o) { 1323 Status = found_path(context, r->sd, r->name, r->namelen); 1324 if (!NT_SUCCESS(Status)) { 1325 ERR("found_path returned %08lx\n", Status); 1326 return Status; 1327 } 1328 1329 if (!r->sd->dummy) 1330 send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime); 1331 } else if (r->sd != or->sd || r->namelen != or->namelen || RtlCompareMemory(r->name, or->name, r->namelen) != r->namelen) { // moved or renamed 1332 ULONG pos = context->datalen, len; 1333 1334 send_command(context, BTRFS_SEND_CMD_RENAME); 1335 1336 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, (uint16_t)strlen(context->lastinode.path)); 1337 1338 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH_TO, r->sd, r->name, r->namelen); 1339 1340 send_command_finish(context, pos); 1341 1342 if (!r->sd->dummy) 1343 send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime); 1344 1345 if (context->lastinode.sd->name) 1346 ExFreePool(context->lastinode.sd->name); 1347 1348 context->lastinode.sd->name = ExAllocatePoolWithTag(PagedPool, r->namelen, ALLOC_TAG); 1349 if (!context->lastinode.sd->name) { 1350 ERR("out of memory\n"); 1351 return STATUS_INSUFFICIENT_RESOURCES; 1352 } 1353 1354 RtlCopyMemory(context->lastinode.sd->name, r->name, r->namelen); 1355 context->lastinode.sd->parent = r->sd; 1356 1357 if (context->lastinode.path) 1358 ExFreePool(context->lastinode.path); 1359 1360 len = find_path_len(r->sd, r->namelen); 1361 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG); 1362 if (!context->lastinode.path) { 1363 ERR("out of memory\n"); 1364 return STATUS_INSUFFICIENT_RESOURCES; 1365 } 1366 1367 find_path(context->lastinode.path, r->sd, r->name, r->namelen); 1368 context->lastinode.path[len] = 0; 1369 } 1370 } else if (r && !or) { // new 1371 Status = found_path(context, r->sd, r->name, r->namelen); 1372 if (!NT_SUCCESS(Status)) { 1373 ERR("found_path returned %08lx\n", Status); 1374 return Status; 1375 } 1376 1377 if (!r->sd->dummy) 1378 send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime); 1379 } else { // deleted 1380 uint64_t last_inode; 1381 1382 Status = get_dir_last_child(context, &last_inode); 1383 if (!NT_SUCCESS(Status)) { 1384 ERR("get_dir_last_child returned %08lx\n", Status); 1385 return Status; 1386 } 1387 1388 if (last_inode <= context->lastinode.inode) { 1389 send_rmdir_command(context, (uint16_t)strlen(context->lastinode.path), context->lastinode.path); 1390 1391 if (!or->sd->dummy) 1392 send_utimes_command_dir(context, or->sd, &or->sd->atime, &or->sd->mtime, &or->sd->ctime); 1393 } else { 1394 char name[64]; 1395 ULONG pos = context->datalen; 1396 1397 Status = get_orphan_name(context, context->lastinode.inode, context->lastinode.gen, name); 1398 if (!NT_SUCCESS(Status)) { 1399 ERR("get_orphan_name returned %08lx\n", Status); 1400 return Status; 1401 } 1402 1403 send_command(context, BTRFS_SEND_CMD_RENAME); 1404 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, (uint16_t)strlen(context->lastinode.path)); 1405 send_add_tlv(context, BTRFS_SEND_TLV_PATH_TO, name, (uint16_t)strlen(name)); 1406 send_command_finish(context, pos); 1407 1408 if (context->lastinode.sd->name) 1409 ExFreePool(context->lastinode.sd->name); 1410 1411 context->lastinode.sd->name = ExAllocatePoolWithTag(PagedPool, strlen(name), ALLOC_TAG); 1412 if (!context->lastinode.sd->name) { 1413 ERR("out of memory\n"); 1414 return STATUS_INSUFFICIENT_RESOURCES; 1415 } 1416 1417 RtlCopyMemory(context->lastinode.sd->name, name, strlen(name)); 1418 context->lastinode.sd->namelen = (uint16_t)strlen(name); 1419 context->lastinode.sd->dummy = true; 1420 context->lastinode.sd->parent = NULL; 1421 1422 send_utimes_command(context, NULL, &context->root_dir->atime, &context->root_dir->mtime, &context->root_dir->ctime); 1423 1424 Status = add_pending_rmdir(context, last_inode); 1425 if (!NT_SUCCESS(Status)) { 1426 ERR("add_pending_rmdir returned %08lx\n", Status); 1427 return Status; 1428 } 1429 } 1430 } 1431 1432 while (!IsListEmpty(&context->lastinode.refs)) { 1433 r = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.refs), ref, list_entry); 1434 ExFreePool(r); 1435 } 1436 1437 while (!IsListEmpty(&context->lastinode.oldrefs)) { 1438 or = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldrefs), ref, list_entry); 1439 ExFreePool(or); 1440 } 1441 1442 return STATUS_SUCCESS; 1443 } else { 1444 if (!IsListEmpty(&context->lastinode.oldrefs)) { 1445 ref* or = CONTAINING_RECORD(context->lastinode.oldrefs.Flink, ref, list_entry); 1446 ULONG len = find_path_len(or->sd, or->namelen); 1447 1448 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG); 1449 if (!context->lastinode.path) { 1450 ERR("out of memory\n"); 1451 return STATUS_INSUFFICIENT_RESOURCES; 1452 } 1453 1454 find_path(context->lastinode.path, or->sd, or->name, or->namelen); 1455 context->lastinode.path[len] = 0; 1456 nameref = or; 1457 } 1458 1459 // remove unchanged refs 1460 le = context->lastinode.oldrefs.Flink; 1461 while (le != &context->lastinode.oldrefs) { 1462 ref* or = CONTAINING_RECORD(le, ref, list_entry); 1463 LIST_ENTRY* le2; 1464 bool matched = false; 1465 1466 le2 = context->lastinode.refs.Flink; 1467 while (le2 != &context->lastinode.refs) { 1468 ref* r = CONTAINING_RECORD(le2, ref, list_entry); 1469 1470 if (r->sd == or->sd && r->namelen == or->namelen && RtlCompareMemory(r->name, or->name, r->namelen) == r->namelen) { 1471 RemoveEntryList(&r->list_entry); 1472 ExFreePool(r); 1473 matched = true; 1474 break; 1475 } 1476 1477 le2 = le2->Flink; 1478 } 1479 1480 if (matched) { 1481 le = le->Flink; 1482 RemoveEntryList(&or->list_entry); 1483 ExFreePool(or); 1484 continue; 1485 } 1486 1487 le = le->Flink; 1488 } 1489 1490 while (!IsListEmpty(&context->lastinode.refs)) { 1491 ref* r = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.refs), ref, list_entry); 1492 uint64_t inode; 1493 bool dir; 1494 1495 if (context->parent) { 1496 Status = look_for_collision(context, r->sd, r->name, r->namelen, &inode, &dir); 1497 if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION) { 1498 ERR("look_for_collision returned %08lx\n", Status); 1499 return Status; 1500 } 1501 1502 if (Status == STATUS_OBJECT_NAME_COLLISION && inode > context->lastinode.inode) { 1503 Status = make_file_orphan(context, inode, dir, context->lastinode.gen, r); 1504 if (!NT_SUCCESS(Status)) { 1505 ERR("make_file_orphan returned %08lx\n", Status); 1506 return Status; 1507 } 1508 } 1509 } 1510 1511 if (context->datalen > SEND_BUFFER_LENGTH) { 1512 Status = wait_for_flush(context, tp1, tp2); 1513 if (!NT_SUCCESS(Status)) { 1514 ERR("wait_for_flush returned %08lx\n", Status); 1515 return Status; 1516 } 1517 1518 if (context->send->cancelling) 1519 return STATUS_SUCCESS; 1520 } 1521 1522 Status = found_path(context, r->sd, r->name, r->namelen); 1523 if (!NT_SUCCESS(Status)) { 1524 ERR("found_path returned %08lx\n", Status); 1525 return Status; 1526 } 1527 1528 if (!r->sd->dummy) 1529 send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime); 1530 1531 if (nameref && !nameref2) 1532 nameref2 = r; 1533 else 1534 ExFreePool(r); 1535 } 1536 1537 while (!IsListEmpty(&context->lastinode.oldrefs)) { 1538 ref* or = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldrefs), ref, list_entry); 1539 bool deleted = false; 1540 1541 le = or->sd->deleted_children.Flink; 1542 while (le != &or->sd->deleted_children) { 1543 deleted_child* dc = CONTAINING_RECORD(le, deleted_child, list_entry); 1544 1545 if (dc->namelen == or->namelen && RtlCompareMemory(dc->name, or->name, or->namelen) == or->namelen) { 1546 RemoveEntryList(&dc->list_entry); 1547 ExFreePool(dc); 1548 deleted = true; 1549 break; 1550 } 1551 1552 le = le->Flink; 1553 } 1554 1555 if (!deleted) { 1556 if (context->datalen > SEND_BUFFER_LENGTH) { 1557 Status = wait_for_flush(context, tp1, tp2); 1558 if (!NT_SUCCESS(Status)) { 1559 ERR("wait_for_flush returned %08lx\n", Status); 1560 return Status; 1561 } 1562 1563 if (context->send->cancelling) 1564 return STATUS_SUCCESS; 1565 } 1566 1567 Status = send_unlink_command(context, or->sd, or->namelen, or->name); 1568 if (!NT_SUCCESS(Status)) { 1569 ERR("send_unlink_command returned %08lx\n", Status); 1570 return Status; 1571 } 1572 1573 if (!or->sd->dummy) 1574 send_utimes_command_dir(context, or->sd, &or->sd->atime, &or->sd->mtime, &or->sd->ctime); 1575 } 1576 1577 if (or == nameref && nameref2) { 1578 uint16_t len = find_path_len(nameref2->sd, nameref2->namelen); 1579 1580 if (context->lastinode.path) 1581 ExFreePool(context->lastinode.path); 1582 1583 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG); 1584 if (!context->lastinode.path) { 1585 ERR("out of memory\n"); 1586 return STATUS_INSUFFICIENT_RESOURCES; 1587 } 1588 1589 find_path(context->lastinode.path, nameref2->sd, nameref2->name, nameref2->namelen); 1590 context->lastinode.path[len] = 0; 1591 1592 ExFreePool(nameref2); 1593 } 1594 1595 ExFreePool(or); 1596 } 1597 } 1598 1599 return STATUS_SUCCESS; 1600 } 1601 1602 static NTSTATUS wait_for_flush(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2) { 1603 NTSTATUS Status; 1604 KEY key1, key2; 1605 1606 if (tp1) 1607 key1 = tp1->item->key; 1608 1609 if (tp2) 1610 key2 = tp2->item->key; 1611 1612 ExReleaseResourceLite(&context->Vcb->tree_lock); 1613 1614 KeClearEvent(&context->send->cleared_event); 1615 KeSetEvent(&context->buffer_event, 0, true); 1616 KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, false, NULL); 1617 1618 ExAcquireResourceSharedLite(&context->Vcb->tree_lock, true); 1619 1620 if (context->send->cancelling) 1621 return STATUS_SUCCESS; 1622 1623 if (tp1) { 1624 Status = find_item(context->Vcb, context->root, tp1, &key1, false, NULL); 1625 if (!NT_SUCCESS(Status)) { 1626 ERR("find_item returned %08lx\n", Status); 1627 return Status; 1628 } 1629 1630 if (keycmp(tp1->item->key, key1)) { 1631 ERR("readonly subvolume changed\n"); 1632 return STATUS_INTERNAL_ERROR; 1633 } 1634 } 1635 1636 if (tp2) { 1637 Status = find_item(context->Vcb, context->parent, tp2, &key2, false, NULL); 1638 if (!NT_SUCCESS(Status)) { 1639 ERR("find_item returned %08lx\n", Status); 1640 return Status; 1641 } 1642 1643 if (keycmp(tp2->item->key, key2)) { 1644 ERR("readonly subvolume changed\n"); 1645 return STATUS_INTERNAL_ERROR; 1646 } 1647 } 1648 1649 return STATUS_SUCCESS; 1650 } 1651 1652 static NTSTATUS add_ext_holes(device_extension* Vcb, LIST_ENTRY* exts, uint64_t size) { 1653 uint64_t lastoff = 0; 1654 LIST_ENTRY* le; 1655 1656 le = exts->Flink; 1657 while (le != exts) { 1658 send_ext* ext = CONTAINING_RECORD(le, send_ext, list_entry); 1659 1660 if (ext->offset > lastoff) { 1661 send_ext* ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data.data) + sizeof(EXTENT_DATA2), ALLOC_TAG); 1662 EXTENT_DATA2* ed2; 1663 1664 if (!ext2) { 1665 ERR("out of memory\n"); 1666 return STATUS_INSUFFICIENT_RESOURCES; 1667 } 1668 1669 ed2 = (EXTENT_DATA2*)ext2->data.data; 1670 1671 ext2->offset = lastoff; 1672 ext2->datalen = offsetof(EXTENT_DATA, data) + sizeof(EXTENT_DATA2); 1673 ext2->data.decoded_size = ed2->num_bytes = ext->offset - lastoff; 1674 ext2->data.type = EXTENT_TYPE_REGULAR; 1675 ed2->address = ed2->size = ed2->offset = 0; 1676 1677 InsertHeadList(le->Blink, &ext2->list_entry); 1678 } 1679 1680 if (ext->data.type == EXTENT_TYPE_INLINE) 1681 lastoff = ext->offset + ext->data.decoded_size; 1682 else { 1683 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data.data; 1684 lastoff = ext->offset + ed2->num_bytes; 1685 } 1686 1687 le = le->Flink; 1688 } 1689 1690 if (size > lastoff) { 1691 send_ext* ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data.data) + sizeof(EXTENT_DATA2), ALLOC_TAG); 1692 EXTENT_DATA2* ed2; 1693 1694 if (!ext2) { 1695 ERR("out of memory\n"); 1696 return STATUS_INSUFFICIENT_RESOURCES; 1697 } 1698 1699 ed2 = (EXTENT_DATA2*)ext2->data.data; 1700 1701 ext2->offset = lastoff; 1702 ext2->datalen = offsetof(EXTENT_DATA, data) + sizeof(EXTENT_DATA2); 1703 ext2->data.decoded_size = ed2->num_bytes = sector_align(size - lastoff, Vcb->superblock.sector_size); 1704 ext2->data.type = EXTENT_TYPE_REGULAR; 1705 ed2->address = ed2->size = ed2->offset = 0; 1706 1707 InsertTailList(exts, &ext2->list_entry); 1708 } 1709 1710 return STATUS_SUCCESS; 1711 } 1712 1713 static NTSTATUS divide_ext(send_ext* ext, uint64_t len, bool trunc) { 1714 send_ext* ext2; 1715 EXTENT_DATA2 *ed2a, *ed2b; 1716 1717 if (ext->data.type == EXTENT_TYPE_INLINE) { 1718 if (!trunc) { 1719 ext2 = ExAllocatePoolWithTag(PagedPool, (ULONG)(offsetof(send_ext, data.data) + ext->data.decoded_size - len), ALLOC_TAG); 1720 1721 if (!ext2) { 1722 ERR("out of memory\n"); 1723 return STATUS_INSUFFICIENT_RESOURCES; 1724 } 1725 1726 ext2->offset = ext->offset + len; 1727 ext2->datalen = (ULONG)(ext->data.decoded_size - len); 1728 ext2->data.decoded_size = ext->data.decoded_size - len; 1729 ext2->data.compression = ext->data.compression; 1730 ext2->data.encryption = ext->data.encryption; 1731 ext2->data.encoding = ext->data.encoding; 1732 ext2->data.type = ext->data.type; 1733 RtlCopyMemory(ext2->data.data, ext->data.data + len, (ULONG)(ext->data.decoded_size - len)); 1734 1735 InsertHeadList(&ext->list_entry, &ext2->list_entry); 1736 } 1737 1738 ext->data.decoded_size = len; 1739 1740 return STATUS_SUCCESS; 1741 } 1742 1743 ed2a = (EXTENT_DATA2*)ext->data.data; 1744 1745 if (!trunc) { 1746 ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data.data) + sizeof(EXTENT_DATA2), ALLOC_TAG); 1747 1748 if (!ext2) { 1749 ERR("out of memory\n"); 1750 return STATUS_INSUFFICIENT_RESOURCES; 1751 } 1752 1753 ed2b = (EXTENT_DATA2*)ext2->data.data; 1754 1755 ext2->offset = ext->offset + len; 1756 ext2->datalen = offsetof(EXTENT_DATA, data) + sizeof(EXTENT_DATA2); 1757 1758 ext2->data.compression = ext->data.compression; 1759 ext2->data.encryption = ext->data.encryption; 1760 ext2->data.encoding = ext->data.encoding; 1761 ext2->data.type = ext->data.type; 1762 ed2b->num_bytes = ed2a->num_bytes - len; 1763 1764 if (ed2a->size == 0) { 1765 ext2->data.decoded_size = ed2b->num_bytes; 1766 ext->data.decoded_size = len; 1767 1768 ed2b->address = ed2b->size = ed2b->offset = 0; 1769 } else { 1770 ext2->data.decoded_size = ext->data.decoded_size; 1771 1772 ed2b->address = ed2a->address; 1773 ed2b->size = ed2a->size; 1774 ed2b->offset = ed2a->offset + len; 1775 } 1776 1777 InsertHeadList(&ext->list_entry, &ext2->list_entry); 1778 } 1779 1780 ed2a->num_bytes = len; 1781 1782 return STATUS_SUCCESS; 1783 } 1784 1785 static NTSTATUS sync_ext_cutoff_points(send_context* context) { 1786 NTSTATUS Status; 1787 send_ext *ext1, *ext2; 1788 1789 ext1 = CONTAINING_RECORD(context->lastinode.exts.Flink, send_ext, list_entry); 1790 ext2 = CONTAINING_RECORD(context->lastinode.oldexts.Flink, send_ext, list_entry); 1791 1792 do { 1793 uint64_t len1, len2; 1794 EXTENT_DATA2 *ed2a, *ed2b; 1795 1796 ed2a = ext1->data.type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ext1->data.data; 1797 ed2b = ext2->data.type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ext2->data.data; 1798 1799 len1 = ed2a ? ed2a->num_bytes : ext1->data.decoded_size; 1800 len2 = ed2b ? ed2b->num_bytes : ext2->data.decoded_size; 1801 1802 if (len1 < len2) { 1803 Status = divide_ext(ext2, len1, false); 1804 if (!NT_SUCCESS(Status)) { 1805 ERR("divide_ext returned %08lx\n", Status); 1806 return Status; 1807 } 1808 } else if (len2 < len1) { 1809 Status = divide_ext(ext1, len2, false); 1810 if (!NT_SUCCESS(Status)) { 1811 ERR("divide_ext returned %08lx\n", Status); 1812 return Status; 1813 } 1814 } 1815 1816 if (ext1->list_entry.Flink == &context->lastinode.exts || ext2->list_entry.Flink == &context->lastinode.oldexts) 1817 break; 1818 1819 ext1 = CONTAINING_RECORD(ext1->list_entry.Flink, send_ext, list_entry); 1820 ext2 = CONTAINING_RECORD(ext2->list_entry.Flink, send_ext, list_entry); 1821 } while (true); 1822 1823 ext1 = CONTAINING_RECORD(context->lastinode.exts.Blink, send_ext, list_entry); 1824 ext2 = CONTAINING_RECORD(context->lastinode.oldexts.Blink, send_ext, list_entry); 1825 1826 Status = divide_ext(ext1, context->lastinode.size - ext1->offset, true); 1827 if (!NT_SUCCESS(Status)) { 1828 ERR("divide_ext returned %08lx\n", Status); 1829 return Status; 1830 } 1831 1832 Status = divide_ext(ext2, context->lastinode.size - ext2->offset, true); 1833 if (!NT_SUCCESS(Status)) { 1834 ERR("divide_ext returned %08lx\n", Status); 1835 return Status; 1836 } 1837 1838 return STATUS_SUCCESS; 1839 } 1840 1841 static bool send_add_tlv_clone_path(send_context* context, root* r, uint64_t inode) { 1842 NTSTATUS Status; 1843 KEY searchkey; 1844 traverse_ptr tp; 1845 uint16_t len = 0; 1846 uint64_t num; 1847 uint8_t* ptr; 1848 1849 num = inode; 1850 1851 while (num != SUBVOL_ROOT_INODE) { 1852 searchkey.obj_id = num; 1853 searchkey.obj_type = TYPE_INODE_EXTREF; 1854 searchkey.offset = 0xffffffffffffffff; 1855 1856 Status = find_item(context->Vcb, r, &tp, &searchkey, false, NULL); 1857 if (!NT_SUCCESS(Status)) { 1858 ERR("find_item returned %08lx\n", Status); 1859 return false; 1860 } 1861 1862 if (tp.item->key.obj_id != searchkey.obj_id || (tp.item->key.obj_type != TYPE_INODE_REF && tp.item->key.obj_type != TYPE_INODE_EXTREF)) { 1863 ERR("could not find INODE_REF for inode %I64x\n", searchkey.obj_id); 1864 return false; 1865 } 1866 1867 if (len > 0) 1868 len++; 1869 1870 if (tp.item->key.obj_type == TYPE_INODE_REF) { 1871 INODE_REF* ir = (INODE_REF*)tp.item->data; 1872 1873 if (tp.item->size < sizeof(INODE_REF) || tp.item->size < offsetof(INODE_REF, name[0]) + ir->n) { 1874 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 1875 return false; 1876 } 1877 1878 len += ir->n; 1879 num = tp.item->key.offset; 1880 } else { 1881 INODE_EXTREF* ier = (INODE_EXTREF*)tp.item->data; 1882 1883 if (tp.item->size < sizeof(INODE_EXTREF) || tp.item->size < offsetof(INODE_EXTREF, name[0]) + ier->n) { 1884 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 1885 return false; 1886 } 1887 1888 len += ier->n; 1889 num = ier->dir; 1890 } 1891 } 1892 1893 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_PATH, NULL, len); 1894 ptr = &context->data[context->datalen]; 1895 1896 num = inode; 1897 1898 while (num != SUBVOL_ROOT_INODE) { 1899 searchkey.obj_id = num; 1900 searchkey.obj_type = TYPE_INODE_EXTREF; 1901 searchkey.offset = 0xffffffffffffffff; 1902 1903 Status = find_item(context->Vcb, r, &tp, &searchkey, false, NULL); 1904 if (!NT_SUCCESS(Status)) { 1905 ERR("find_item returned %08lx\n", Status); 1906 return false; 1907 } 1908 1909 if (tp.item->key.obj_id != searchkey.obj_id || (tp.item->key.obj_type != TYPE_INODE_REF && tp.item->key.obj_type != TYPE_INODE_EXTREF)) { 1910 ERR("could not find INODE_REF for inode %I64x\n", searchkey.obj_id); 1911 return false; 1912 } 1913 1914 if (num != inode) { 1915 ptr--; 1916 *ptr = '/'; 1917 } 1918 1919 if (tp.item->key.obj_type == TYPE_INODE_REF) { 1920 INODE_REF* ir = (INODE_REF*)tp.item->data; 1921 1922 RtlCopyMemory(ptr - ir->n, ir->name, ir->n); 1923 ptr -= ir->n; 1924 num = tp.item->key.offset; 1925 } else { 1926 INODE_EXTREF* ier = (INODE_EXTREF*)tp.item->data; 1927 1928 RtlCopyMemory(ptr - ier->n, ier->name, ier->n); 1929 ptr -= ier->n; 1930 num = ier->dir; 1931 } 1932 } 1933 1934 return true; 1935 } 1936 1937 static bool try_clone_edr(send_context* context, send_ext* se, EXTENT_DATA_REF* edr) { 1938 NTSTATUS Status; 1939 root* r = NULL; 1940 KEY searchkey; 1941 traverse_ptr tp; 1942 EXTENT_DATA2* seed2 = (EXTENT_DATA2*)se->data.data; 1943 1944 if (context->parent && edr->root == context->parent->id) 1945 r = context->parent; 1946 1947 if (!r && context->num_clones > 0) { 1948 ULONG i; 1949 1950 for (i = 0; i < context->num_clones; i++) { 1951 if (context->clones[i]->id == edr->root && context->clones[i] != context->root) { 1952 r = context->clones[i]; 1953 break; 1954 } 1955 } 1956 } 1957 1958 if (!r) 1959 return false; 1960 1961 searchkey.obj_id = edr->objid; 1962 searchkey.obj_type = TYPE_EXTENT_DATA; 1963 searchkey.offset = 0; 1964 1965 Status = find_item(context->Vcb, r, &tp, &searchkey, false, NULL); 1966 if (!NT_SUCCESS(Status)) { 1967 ERR("find_item returned %08lx\n", Status); 1968 return false; 1969 } 1970 1971 while (true) { 1972 traverse_ptr next_tp; 1973 1974 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { 1975 if (tp.item->size < sizeof(EXTENT_DATA)) 1976 ERR("(%I64x,%x,%I64x) has size %u, not at least %Iu as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); 1977 else { 1978 EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data; 1979 1980 if (ed->type == EXTENT_TYPE_REGULAR) { 1981 if (tp.item->size < offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)) 1982 ERR("(%I64x,%x,%I64x) has size %u, not %Iu as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, 1983 tp.item->size, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)); 1984 else { 1985 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; 1986 1987 if (ed2->address == seed2->address && ed2->size == seed2->size && seed2->offset <= ed2->offset && seed2->offset + seed2->num_bytes >= ed2->offset + ed2->num_bytes) { 1988 uint64_t clone_offset = tp.item->key.offset + ed2->offset - seed2->offset; 1989 uint64_t clone_len = min(context->lastinode.size - se->offset, ed2->num_bytes); 1990 1991 if (clone_offset % context->Vcb->superblock.sector_size == 0 && clone_len % context->Vcb->superblock.sector_size == 0) { 1992 ULONG pos = context->datalen; 1993 1994 send_command(context, BTRFS_SEND_CMD_CLONE); 1995 1996 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &se->offset, sizeof(uint64_t)); 1997 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_LENGTH, &clone_len, sizeof(uint64_t)); 1998 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 1999 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_UUID, r->root_item.rtransid == 0 ? &r->root_item.uuid : &r->root_item.received_uuid, sizeof(BTRFS_UUID)); 2000 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_CTRANSID, &r->root_item.ctransid, sizeof(uint64_t)); 2001 2002 if (!send_add_tlv_clone_path(context, r, tp.item->key.obj_id)) 2003 context->datalen = pos; 2004 else { 2005 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_OFFSET, &clone_offset, sizeof(uint64_t)); 2006 2007 send_command_finish(context, pos); 2008 2009 return true; 2010 } 2011 } 2012 } 2013 } 2014 } 2015 } 2016 } else if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > searchkey.obj_type)) 2017 break; 2018 2019 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL)) 2020 tp = next_tp; 2021 else 2022 break; 2023 } 2024 2025 return false; 2026 } 2027 2028 static bool try_clone(send_context* context, send_ext* se) { 2029 NTSTATUS Status; 2030 KEY searchkey; 2031 traverse_ptr tp; 2032 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)se->data.data; 2033 EXTENT_ITEM* ei; 2034 uint64_t rc = 0; 2035 2036 searchkey.obj_id = ed2->address; 2037 searchkey.obj_type = TYPE_EXTENT_ITEM; 2038 searchkey.offset = ed2->size; 2039 2040 Status = find_item(context->Vcb, context->Vcb->extent_root, &tp, &searchkey, false, NULL); 2041 if (!NT_SUCCESS(Status)) { 2042 ERR("find_item returned %08lx\n", Status); 2043 return false; 2044 } 2045 2046 if (keycmp(tp.item->key, searchkey)) { 2047 ERR("(%I64x,%x,%I64x) not found\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); 2048 return false; 2049 } 2050 2051 if (tp.item->size < sizeof(EXTENT_ITEM)) { 2052 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM)); 2053 return false; 2054 } 2055 2056 ei = (EXTENT_ITEM*)tp.item->data; 2057 2058 if (tp.item->size > sizeof(EXTENT_ITEM)) { 2059 uint32_t len = tp.item->size - sizeof(EXTENT_ITEM); 2060 uint8_t* ptr = (uint8_t*)&ei[1]; 2061 2062 while (len > 0) { 2063 uint8_t secttype = *ptr; 2064 ULONG sectlen = get_extent_data_len(secttype); 2065 uint64_t sectcount = get_extent_data_refcount(secttype, ptr + sizeof(uint8_t)); 2066 2067 len--; 2068 2069 if (sectlen > len) { 2070 ERR("(%I64x,%x,%I64x): %x bytes left, expecting at least %lx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, len, sectlen); 2071 return false; 2072 } 2073 2074 if (sectlen == 0) { 2075 ERR("(%I64x,%x,%I64x): unrecognized extent type %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, secttype); 2076 return false; 2077 } 2078 2079 rc += sectcount; 2080 2081 if (secttype == TYPE_EXTENT_DATA_REF) { 2082 EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)(ptr + sizeof(uint8_t)); 2083 2084 if (try_clone_edr(context, se, sectedr)) 2085 return true; 2086 } 2087 2088 len -= sectlen; 2089 ptr += sizeof(uint8_t) + sectlen; 2090 } 2091 } 2092 2093 if (rc >= ei->refcount) 2094 return false; 2095 2096 searchkey.obj_type = TYPE_EXTENT_DATA_REF; 2097 searchkey.offset = 0; 2098 2099 Status = find_item(context->Vcb, context->Vcb->extent_root, &tp, &searchkey, false, NULL); 2100 if (!NT_SUCCESS(Status)) { 2101 ERR("find_item returned %08lx\n", Status); 2102 return false; 2103 } 2104 2105 while (true) { 2106 traverse_ptr next_tp; 2107 2108 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { 2109 if (tp.item->size < sizeof(EXTENT_DATA_REF)) 2110 ERR("(%I64x,%x,%I64x) has size %u, not %Iu as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA_REF)); 2111 else { 2112 if (try_clone_edr(context, se, (EXTENT_DATA_REF*)tp.item->data)) 2113 return true; 2114 } 2115 } else if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > searchkey.obj_type)) 2116 break; 2117 2118 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL)) 2119 tp = next_tp; 2120 else 2121 break; 2122 } 2123 2124 return false; 2125 } 2126 2127 static NTSTATUS flush_extents(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2) { 2128 NTSTATUS Status; 2129 2130 if ((IsListEmpty(&context->lastinode.exts) && IsListEmpty(&context->lastinode.oldexts)) || context->lastinode.size == 0) 2131 return STATUS_SUCCESS; 2132 2133 if (context->parent) { 2134 Status = add_ext_holes(context->Vcb, &context->lastinode.exts, context->lastinode.size); 2135 if (!NT_SUCCESS(Status)) { 2136 ERR("add_ext_holes returned %08lx\n", Status); 2137 return Status; 2138 } 2139 2140 Status = add_ext_holes(context->Vcb, &context->lastinode.oldexts, context->lastinode.size); 2141 if (!NT_SUCCESS(Status)) { 2142 ERR("add_ext_holes returned %08lx\n", Status); 2143 return Status; 2144 } 2145 2146 Status = sync_ext_cutoff_points(context); 2147 if (!NT_SUCCESS(Status)) { 2148 ERR("sync_ext_cutoff_points returned %08lx\n", Status); 2149 return Status; 2150 } 2151 } 2152 2153 while (!IsListEmpty(&context->lastinode.exts)) { 2154 send_ext* se = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.exts), send_ext, list_entry); 2155 send_ext* se2 = context->parent ? CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldexts), send_ext, list_entry) : NULL; 2156 ULONG pos; 2157 EXTENT_DATA2* ed2; 2158 2159 if (se2) { 2160 if (se->data.type == EXTENT_TYPE_INLINE && se2->data.type == EXTENT_TYPE_INLINE && 2161 RtlCompareMemory(se->data.data, se2->data.data, (ULONG)se->data.decoded_size) == (ULONG)se->data.decoded_size) { 2162 ExFreePool(se); 2163 ExFreePool(se2); 2164 continue; 2165 } 2166 2167 if (se->data.type == EXTENT_TYPE_REGULAR && se2->data.type == EXTENT_TYPE_REGULAR) { 2168 EXTENT_DATA2 *ed2a, *ed2b; 2169 2170 ed2a = (EXTENT_DATA2*)se->data.data; 2171 ed2b = (EXTENT_DATA2*)se2->data.data; 2172 2173 if (RtlCompareMemory(ed2a, ed2b, sizeof(EXTENT_DATA2)) == sizeof(EXTENT_DATA2)) { 2174 ExFreePool(se); 2175 ExFreePool(se2); 2176 continue; 2177 } 2178 } 2179 } 2180 2181 if (se->data.type == EXTENT_TYPE_INLINE) { 2182 pos = context->datalen; 2183 2184 send_command(context, BTRFS_SEND_CMD_WRITE); 2185 2186 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2187 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &se->offset, sizeof(uint64_t)); 2188 2189 if (se->data.compression == BTRFS_COMPRESSION_NONE) 2190 send_add_tlv(context, BTRFS_SEND_TLV_DATA, se->data.data, (uint16_t)se->data.decoded_size); 2191 else if (se->data.compression == BTRFS_COMPRESSION_ZLIB || se->data.compression == BTRFS_COMPRESSION_LZO || se->data.compression == BTRFS_COMPRESSION_ZSTD) { 2192 ULONG inlen = se->datalen - (ULONG)offsetof(EXTENT_DATA, data[0]); 2193 2194 send_add_tlv(context, BTRFS_SEND_TLV_DATA, NULL, (uint16_t)se->data.decoded_size); 2195 RtlZeroMemory(&context->data[context->datalen - se->data.decoded_size], (ULONG)se->data.decoded_size); 2196 2197 if (se->data.compression == BTRFS_COMPRESSION_ZLIB) { 2198 Status = zlib_decompress(se->data.data, inlen, &context->data[context->datalen - se->data.decoded_size], (uint32_t)se->data.decoded_size); 2199 if (!NT_SUCCESS(Status)) { 2200 ERR("zlib_decompress returned %08lx\n", Status); 2201 ExFreePool(se); 2202 if (se2) ExFreePool(se2); 2203 return Status; 2204 } 2205 } else if (se->data.compression == BTRFS_COMPRESSION_LZO) { 2206 if (inlen < sizeof(uint32_t)) { 2207 ERR("extent data was truncated\n"); 2208 ExFreePool(se); 2209 if (se2) ExFreePool(se2); 2210 return STATUS_INTERNAL_ERROR; 2211 } else 2212 inlen -= sizeof(uint32_t); 2213 2214 Status = lzo_decompress(se->data.data + sizeof(uint32_t), inlen, &context->data[context->datalen - se->data.decoded_size], (uint32_t)se->data.decoded_size, sizeof(uint32_t)); 2215 if (!NT_SUCCESS(Status)) { 2216 ERR("lzo_decompress returned %08lx\n", Status); 2217 ExFreePool(se); 2218 if (se2) ExFreePool(se2); 2219 return Status; 2220 } 2221 } else if (se->data.compression == BTRFS_COMPRESSION_ZSTD) { 2222 Status = zstd_decompress(se->data.data, inlen, &context->data[context->datalen - se->data.decoded_size], (uint32_t)se->data.decoded_size); 2223 if (!NT_SUCCESS(Status)) { 2224 ERR("zlib_decompress returned %08lx\n", Status); 2225 ExFreePool(se); 2226 if (se2) ExFreePool(se2); 2227 return Status; 2228 } 2229 } 2230 } else { 2231 ERR("unhandled compression type %x\n", se->data.compression); 2232 ExFreePool(se); 2233 if (se2) ExFreePool(se2); 2234 return STATUS_NOT_IMPLEMENTED; 2235 } 2236 2237 send_command_finish(context, pos); 2238 2239 ExFreePool(se); 2240 if (se2) ExFreePool(se2); 2241 continue; 2242 } 2243 2244 ed2 = (EXTENT_DATA2*)se->data.data; 2245 2246 if (ed2->size != 0 && (context->parent || context->num_clones > 0)) { 2247 if (try_clone(context, se)) { 2248 ExFreePool(se); 2249 if (se2) ExFreePool(se2); 2250 continue; 2251 } 2252 } 2253 2254 if (ed2->size == 0) { // write sparse 2255 uint64_t off, offset; 2256 2257 for (off = ed2->offset; off < ed2->offset + ed2->num_bytes; off += MAX_SEND_WRITE) { 2258 uint16_t length = (uint16_t)min(min(ed2->offset + ed2->num_bytes - off, MAX_SEND_WRITE), context->lastinode.size - se->offset - off); 2259 2260 if (context->datalen > SEND_BUFFER_LENGTH) { 2261 Status = wait_for_flush(context, tp1, tp2); 2262 if (!NT_SUCCESS(Status)) { 2263 ERR("wait_for_flush returned %08lx\n", Status); 2264 ExFreePool(se); 2265 if (se2) ExFreePool(se2); 2266 return Status; 2267 } 2268 2269 if (context->send->cancelling) { 2270 ExFreePool(se); 2271 if (se2) ExFreePool(se2); 2272 return STATUS_SUCCESS; 2273 } 2274 } 2275 2276 pos = context->datalen; 2277 2278 send_command(context, BTRFS_SEND_CMD_WRITE); 2279 2280 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2281 2282 offset = se->offset + off; 2283 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &offset, sizeof(uint64_t)); 2284 2285 send_add_tlv(context, BTRFS_SEND_TLV_DATA, NULL, length); 2286 RtlZeroMemory(&context->data[context->datalen - length], length); 2287 2288 send_command_finish(context, pos); 2289 } 2290 } else if (se->data.compression == BTRFS_COMPRESSION_NONE) { 2291 uint64_t off, offset; 2292 uint8_t* buf; 2293 2294 buf = ExAllocatePoolWithTag(NonPagedPool, MAX_SEND_WRITE + (2 * context->Vcb->superblock.sector_size), ALLOC_TAG); 2295 if (!buf) { 2296 ERR("out of memory\n"); 2297 ExFreePool(se); 2298 if (se2) ExFreePool(se2); 2299 return STATUS_INSUFFICIENT_RESOURCES; 2300 } 2301 2302 for (off = ed2->offset; off < ed2->offset + ed2->num_bytes; off += MAX_SEND_WRITE) { 2303 uint16_t length = (uint16_t)min(ed2->offset + ed2->num_bytes - off, MAX_SEND_WRITE); 2304 ULONG skip_start; 2305 uint64_t addr = ed2->address + off; 2306 void* csum; 2307 2308 if (context->datalen > SEND_BUFFER_LENGTH) { 2309 Status = wait_for_flush(context, tp1, tp2); 2310 if (!NT_SUCCESS(Status)) { 2311 ERR("wait_for_flush returned %08lx\n", Status); 2312 ExFreePool(buf); 2313 ExFreePool(se); 2314 if (se2) ExFreePool(se2); 2315 return Status; 2316 } 2317 2318 if (context->send->cancelling) { 2319 ExFreePool(buf); 2320 ExFreePool(se); 2321 if (se2) ExFreePool(se2); 2322 return STATUS_SUCCESS; 2323 } 2324 } 2325 2326 skip_start = addr % context->Vcb->superblock.sector_size; 2327 addr -= skip_start; 2328 2329 if (context->lastinode.flags & BTRFS_INODE_NODATASUM) 2330 csum = NULL; 2331 else { 2332 uint32_t len; 2333 2334 len = (uint32_t)sector_align(length + skip_start, context->Vcb->superblock.sector_size) / context->Vcb->superblock.sector_size; 2335 2336 csum = ExAllocatePoolWithTag(PagedPool, len * context->Vcb->csum_size, ALLOC_TAG); 2337 if (!csum) { 2338 ERR("out of memory\n"); 2339 ExFreePool(buf); 2340 ExFreePool(se); 2341 if (se2) ExFreePool(se2); 2342 return STATUS_INSUFFICIENT_RESOURCES; 2343 } 2344 2345 Status = load_csum(context->Vcb, csum, addr, len, NULL); 2346 if (!NT_SUCCESS(Status)) { 2347 ERR("load_csum returned %08lx\n", Status); 2348 ExFreePool(csum); 2349 ExFreePool(buf); 2350 ExFreePool(se); 2351 if (se2) ExFreePool(se2); 2352 return STATUS_INSUFFICIENT_RESOURCES; 2353 } 2354 } 2355 2356 Status = read_data(context->Vcb, addr, (uint32_t)sector_align(length + skip_start, context->Vcb->superblock.sector_size), 2357 csum, false, buf, NULL, NULL, NULL, 0, false, NormalPagePriority); 2358 if (!NT_SUCCESS(Status)) { 2359 ERR("read_data returned %08lx\n", Status); 2360 ExFreePool(buf); 2361 ExFreePool(se); 2362 if (se2) ExFreePool(se2); 2363 if (csum) ExFreePool(csum); 2364 return Status; 2365 } 2366 2367 if (csum) 2368 ExFreePool(csum); 2369 2370 pos = context->datalen; 2371 2372 send_command(context, BTRFS_SEND_CMD_WRITE); 2373 2374 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2375 2376 offset = se->offset + off; 2377 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &offset, sizeof(uint64_t)); 2378 2379 length = (uint16_t)min(context->lastinode.size - se->offset - off, length); 2380 send_add_tlv(context, BTRFS_SEND_TLV_DATA, buf + skip_start, length); 2381 2382 send_command_finish(context, pos); 2383 } 2384 2385 ExFreePool(buf); 2386 } else { 2387 uint8_t *buf, *compbuf; 2388 uint64_t off; 2389 void* csum; 2390 2391 buf = ExAllocatePoolWithTag(PagedPool, (ULONG)se->data.decoded_size, ALLOC_TAG); 2392 if (!buf) { 2393 ERR("out of memory\n"); 2394 ExFreePool(se); 2395 if (se2) ExFreePool(se2); 2396 return STATUS_INSUFFICIENT_RESOURCES; 2397 } 2398 2399 compbuf = ExAllocatePoolWithTag(PagedPool, (ULONG)ed2->size, ALLOC_TAG); 2400 if (!compbuf) { 2401 ERR("out of memory\n"); 2402 ExFreePool(buf); 2403 ExFreePool(se); 2404 if (se2) ExFreePool(se2); 2405 return STATUS_INSUFFICIENT_RESOURCES; 2406 } 2407 2408 if (context->lastinode.flags & BTRFS_INODE_NODATASUM) 2409 csum = NULL; 2410 else { 2411 uint32_t len; 2412 2413 len = (uint32_t)(ed2->size / context->Vcb->superblock.sector_size); 2414 2415 csum = ExAllocatePoolWithTag(PagedPool, len * context->Vcb->csum_size, ALLOC_TAG); 2416 if (!csum) { 2417 ERR("out of memory\n"); 2418 ExFreePool(compbuf); 2419 ExFreePool(buf); 2420 ExFreePool(se); 2421 if (se2) ExFreePool(se2); 2422 return STATUS_INSUFFICIENT_RESOURCES; 2423 } 2424 2425 Status = load_csum(context->Vcb, csum, ed2->address, len, NULL); 2426 if (!NT_SUCCESS(Status)) { 2427 ERR("load_csum returned %08lx\n", Status); 2428 ExFreePool(csum); 2429 ExFreePool(compbuf); 2430 ExFreePool(buf); 2431 ExFreePool(se); 2432 if (se2) ExFreePool(se2); 2433 return Status; 2434 } 2435 } 2436 2437 Status = read_data(context->Vcb, ed2->address, (uint32_t)ed2->size, csum, false, compbuf, NULL, NULL, NULL, 0, false, NormalPagePriority); 2438 if (!NT_SUCCESS(Status)) { 2439 ERR("read_data returned %08lx\n", Status); 2440 ExFreePool(compbuf); 2441 ExFreePool(buf); 2442 ExFreePool(se); 2443 if (se2) ExFreePool(se2); 2444 if (csum) ExFreePool(csum); 2445 return Status; 2446 } 2447 2448 if (csum) 2449 ExFreePool(csum); 2450 2451 if (se->data.compression == BTRFS_COMPRESSION_ZLIB) { 2452 Status = zlib_decompress(compbuf, (uint32_t)ed2->size, buf, (uint32_t)se->data.decoded_size); 2453 if (!NT_SUCCESS(Status)) { 2454 ERR("zlib_decompress returned %08lx\n", Status); 2455 ExFreePool(compbuf); 2456 ExFreePool(buf); 2457 ExFreePool(se); 2458 if (se2) ExFreePool(se2); 2459 return Status; 2460 } 2461 } else if (se->data.compression == BTRFS_COMPRESSION_LZO) { 2462 Status = lzo_decompress(&compbuf[sizeof(uint32_t)], (uint32_t)ed2->size, buf, (uint32_t)se->data.decoded_size, sizeof(uint32_t)); 2463 if (!NT_SUCCESS(Status)) { 2464 ERR("lzo_decompress returned %08lx\n", Status); 2465 ExFreePool(compbuf); 2466 ExFreePool(buf); 2467 ExFreePool(se); 2468 if (se2) ExFreePool(se2); 2469 return Status; 2470 } 2471 } else if (se->data.compression == BTRFS_COMPRESSION_ZSTD) { 2472 Status = zstd_decompress(compbuf, (uint32_t)ed2->size, buf, (uint32_t)se->data.decoded_size); 2473 if (!NT_SUCCESS(Status)) { 2474 ERR("zstd_decompress returned %08lx\n", Status); 2475 ExFreePool(compbuf); 2476 ExFreePool(buf); 2477 ExFreePool(se); 2478 if (se2) ExFreePool(se2); 2479 return Status; 2480 } 2481 } 2482 2483 ExFreePool(compbuf); 2484 2485 for (off = ed2->offset; off < ed2->offset + ed2->num_bytes; off += MAX_SEND_WRITE) { 2486 uint16_t length = (uint16_t)min(ed2->offset + ed2->num_bytes - off, MAX_SEND_WRITE); 2487 uint64_t offset; 2488 2489 if (context->datalen > SEND_BUFFER_LENGTH) { 2490 Status = wait_for_flush(context, tp1, tp2); 2491 if (!NT_SUCCESS(Status)) { 2492 ERR("wait_for_flush returned %08lx\n", Status); 2493 ExFreePool(buf); 2494 ExFreePool(se); 2495 if (se2) ExFreePool(se2); 2496 return Status; 2497 } 2498 2499 if (context->send->cancelling) { 2500 ExFreePool(buf); 2501 ExFreePool(se); 2502 if (se2) ExFreePool(se2); 2503 return STATUS_SUCCESS; 2504 } 2505 } 2506 2507 pos = context->datalen; 2508 2509 send_command(context, BTRFS_SEND_CMD_WRITE); 2510 2511 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2512 2513 offset = se->offset + off; 2514 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &offset, sizeof(uint64_t)); 2515 2516 length = (uint16_t)min(context->lastinode.size - se->offset - off, length); 2517 send_add_tlv(context, BTRFS_SEND_TLV_DATA, &buf[off], length); 2518 2519 send_command_finish(context, pos); 2520 } 2521 2522 ExFreePool(buf); 2523 } 2524 2525 ExFreePool(se); 2526 if (se2) ExFreePool(se2); 2527 } 2528 2529 return STATUS_SUCCESS; 2530 } 2531 2532 static NTSTATUS finish_inode(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2) { 2533 LIST_ENTRY* le; 2534 2535 if (!IsListEmpty(&context->lastinode.refs) || !IsListEmpty(&context->lastinode.oldrefs)) { 2536 NTSTATUS Status = flush_refs(context, tp1, tp2); 2537 if (!NT_SUCCESS(Status)) { 2538 ERR("flush_refs returned %08lx\n", Status); 2539 return Status; 2540 } 2541 2542 if (context->send->cancelling) 2543 return STATUS_SUCCESS; 2544 } 2545 2546 if (!context->lastinode.deleting) { 2547 if (context->lastinode.file) { 2548 NTSTATUS Status = flush_extents(context, tp1, tp2); 2549 if (!NT_SUCCESS(Status)) { 2550 ERR("flush_extents returned %08lx\n", Status); 2551 return Status; 2552 } 2553 2554 if (context->send->cancelling) 2555 return STATUS_SUCCESS; 2556 2557 send_truncate_command(context, context->lastinode.path, context->lastinode.size); 2558 } 2559 2560 if (context->lastinode.new || context->lastinode.uid != context->lastinode.olduid || context->lastinode.gid != context->lastinode.oldgid) 2561 send_chown_command(context, context->lastinode.path, context->lastinode.uid, context->lastinode.gid); 2562 2563 if (((context->lastinode.mode & __S_IFLNK) != __S_IFLNK || ((context->lastinode.mode & 07777) != 0777)) && 2564 (context->lastinode.new || context->lastinode.mode != context->lastinode.oldmode)) 2565 send_chmod_command(context, context->lastinode.path, context->lastinode.mode); 2566 2567 send_utimes_command(context, context->lastinode.path, &context->lastinode.atime, &context->lastinode.mtime, &context->lastinode.ctime); 2568 } 2569 2570 while (!IsListEmpty(&context->lastinode.exts)) { 2571 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&context->lastinode.exts), send_ext, list_entry)); 2572 } 2573 2574 while (!IsListEmpty(&context->lastinode.oldexts)) { 2575 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldexts), send_ext, list_entry)); 2576 } 2577 2578 if (context->parent) { 2579 le = context->pending_rmdirs.Flink; 2580 2581 while (le != &context->pending_rmdirs) { 2582 pending_rmdir* pr = CONTAINING_RECORD(le, pending_rmdir, list_entry); 2583 2584 if (pr->last_child_inode <= context->lastinode.inode) { 2585 le = le->Flink; 2586 2587 send_rmdir_command(context, pr->sd->namelen, pr->sd->name); 2588 2589 RemoveEntryList(&pr->sd->list_entry); 2590 2591 if (pr->sd->name) 2592 ExFreePool(pr->sd->name); 2593 2594 while (!IsListEmpty(&pr->sd->deleted_children)) { 2595 deleted_child* dc = CONTAINING_RECORD(RemoveHeadList(&pr->sd->deleted_children), deleted_child, list_entry); 2596 ExFreePool(dc); 2597 } 2598 2599 ExFreePool(pr->sd); 2600 2601 RemoveEntryList(&pr->list_entry); 2602 ExFreePool(pr); 2603 } else 2604 break; 2605 } 2606 } 2607 2608 context->lastinode.inode = 0; 2609 context->lastinode.o = NULL; 2610 2611 if (context->lastinode.path) { 2612 ExFreePool(context->lastinode.path); 2613 context->lastinode.path = NULL; 2614 } 2615 2616 return STATUS_SUCCESS; 2617 } 2618 2619 static NTSTATUS send_extent_data(send_context* context, traverse_ptr* tp, traverse_ptr* tp2) { 2620 NTSTATUS Status; 2621 2622 if (tp && tp2 && tp->item->size == tp2->item->size && RtlCompareMemory(tp->item->data, tp2->item->data, tp->item->size) == tp->item->size) 2623 return STATUS_SUCCESS; 2624 2625 if (!IsListEmpty(&context->lastinode.refs) || !IsListEmpty(&context->lastinode.oldrefs)) { 2626 Status = flush_refs(context, tp, tp2); 2627 if (!NT_SUCCESS(Status)) { 2628 ERR("flush_refs returned %08lx\n", Status); 2629 return Status; 2630 } 2631 2632 if (context->send->cancelling) 2633 return STATUS_SUCCESS; 2634 } 2635 2636 if ((context->lastinode.mode & __S_IFLNK) == __S_IFLNK) 2637 return STATUS_SUCCESS; 2638 2639 if (tp) { 2640 EXTENT_DATA* ed; 2641 EXTENT_DATA2* ed2 = NULL; 2642 2643 if (tp->item->size < sizeof(EXTENT_DATA)) { 2644 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, 2645 tp->item->size, sizeof(EXTENT_DATA)); 2646 return STATUS_INTERNAL_ERROR; 2647 } 2648 2649 ed = (EXTENT_DATA*)tp->item->data; 2650 2651 if (ed->encryption != BTRFS_ENCRYPTION_NONE) { 2652 ERR("unknown encryption type %u\n", ed->encryption); 2653 return STATUS_INTERNAL_ERROR; 2654 } 2655 2656 if (ed->encoding != BTRFS_ENCODING_NONE) { 2657 ERR("unknown encoding type %u\n", ed->encoding); 2658 return STATUS_INTERNAL_ERROR; 2659 } 2660 2661 if (ed->compression != BTRFS_COMPRESSION_NONE && ed->compression != BTRFS_COMPRESSION_ZLIB && 2662 ed->compression != BTRFS_COMPRESSION_LZO && ed->compression != BTRFS_COMPRESSION_ZSTD) { 2663 ERR("unknown compression type %u\n", ed->compression); 2664 return STATUS_INTERNAL_ERROR; 2665 } 2666 2667 if (ed->type == EXTENT_TYPE_REGULAR) { 2668 if (tp->item->size < offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)) { 2669 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, 2670 tp->item->size, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)); 2671 return STATUS_INTERNAL_ERROR; 2672 } 2673 2674 ed2 = (EXTENT_DATA2*)ed->data; 2675 } else if (ed->type == EXTENT_TYPE_INLINE) { 2676 if (tp->item->size < offsetof(EXTENT_DATA, data[0]) + ed->decoded_size && ed->compression == BTRFS_COMPRESSION_NONE) { 2677 ERR("(%I64x,%x,%I64x) was %u bytes, expected %I64u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, 2678 tp->item->size, offsetof(EXTENT_DATA, data[0]) + ed->decoded_size); 2679 return STATUS_INTERNAL_ERROR; 2680 } 2681 } 2682 2683 if ((ed->type == EXTENT_TYPE_INLINE || (ed->type == EXTENT_TYPE_REGULAR && ed2->size != 0)) && ed->decoded_size != 0) { 2684 send_ext* se = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data) + tp->item->size, ALLOC_TAG); 2685 2686 if (!se) { 2687 ERR("out of memory\n"); 2688 return STATUS_INSUFFICIENT_RESOURCES; 2689 } 2690 2691 se->offset = tp->item->key.offset; 2692 se->datalen = tp->item->size; 2693 RtlCopyMemory(&se->data, tp->item->data, tp->item->size); 2694 InsertTailList(&context->lastinode.exts, &se->list_entry); 2695 } 2696 } 2697 2698 if (tp2) { 2699 EXTENT_DATA* ed; 2700 EXTENT_DATA2* ed2 = NULL; 2701 2702 if (tp2->item->size < sizeof(EXTENT_DATA)) { 2703 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset, 2704 tp2->item->size, sizeof(EXTENT_DATA)); 2705 return STATUS_INTERNAL_ERROR; 2706 } 2707 2708 ed = (EXTENT_DATA*)tp2->item->data; 2709 2710 if (ed->encryption != BTRFS_ENCRYPTION_NONE) { 2711 ERR("unknown encryption type %u\n", ed->encryption); 2712 return STATUS_INTERNAL_ERROR; 2713 } 2714 2715 if (ed->encoding != BTRFS_ENCODING_NONE) { 2716 ERR("unknown encoding type %u\n", ed->encoding); 2717 return STATUS_INTERNAL_ERROR; 2718 } 2719 2720 if (ed->compression != BTRFS_COMPRESSION_NONE && ed->compression != BTRFS_COMPRESSION_ZLIB && 2721 ed->compression != BTRFS_COMPRESSION_LZO && ed->compression != BTRFS_COMPRESSION_ZSTD) { 2722 ERR("unknown compression type %u\n", ed->compression); 2723 return STATUS_INTERNAL_ERROR; 2724 } 2725 2726 if (ed->type == EXTENT_TYPE_REGULAR) { 2727 if (tp2->item->size < offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)) { 2728 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset, 2729 tp2->item->size, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)); 2730 return STATUS_INTERNAL_ERROR; 2731 } 2732 2733 ed2 = (EXTENT_DATA2*)ed->data; 2734 } else if (ed->type == EXTENT_TYPE_INLINE) { 2735 if (tp2->item->size < offsetof(EXTENT_DATA, data[0]) + ed->decoded_size) { 2736 ERR("(%I64x,%x,%I64x) was %u bytes, expected %I64u\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset, 2737 tp2->item->size, offsetof(EXTENT_DATA, data[0]) + ed->decoded_size); 2738 return STATUS_INTERNAL_ERROR; 2739 } 2740 } 2741 2742 if ((ed->type == EXTENT_TYPE_INLINE || (ed->type == EXTENT_TYPE_REGULAR && ed2->size != 0)) && ed->decoded_size != 0) { 2743 send_ext* se = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data) + tp2->item->size, ALLOC_TAG); 2744 2745 if (!se) { 2746 ERR("out of memory\n"); 2747 return STATUS_INSUFFICIENT_RESOURCES; 2748 } 2749 2750 se->offset = tp2->item->key.offset; 2751 se->datalen = tp2->item->size; 2752 RtlCopyMemory(&se->data, tp2->item->data, tp2->item->size); 2753 InsertTailList(&context->lastinode.oldexts, &se->list_entry); 2754 } 2755 } 2756 2757 return STATUS_SUCCESS; 2758 } 2759 2760 typedef struct { 2761 uint16_t namelen; 2762 char* name; 2763 uint16_t value1len; 2764 char* value1; 2765 uint16_t value2len; 2766 char* value2; 2767 LIST_ENTRY list_entry; 2768 } xattr_cmp; 2769 2770 static NTSTATUS send_xattr(send_context* context, traverse_ptr* tp, traverse_ptr* tp2) { 2771 if (tp && tp2 && tp->item->size == tp2->item->size && RtlCompareMemory(tp->item->data, tp2->item->data, tp->item->size) == tp->item->size) 2772 return STATUS_SUCCESS; 2773 2774 if (!IsListEmpty(&context->lastinode.refs) || !IsListEmpty(&context->lastinode.oldrefs)) { 2775 NTSTATUS Status = flush_refs(context, tp, tp2); 2776 if (!NT_SUCCESS(Status)) { 2777 ERR("flush_refs returned %08lx\n", Status); 2778 return Status; 2779 } 2780 2781 if (context->send->cancelling) 2782 return STATUS_SUCCESS; 2783 } 2784 2785 if (tp && tp->item->size < sizeof(DIR_ITEM)) { 2786 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset, 2787 tp->item->size, sizeof(DIR_ITEM)); 2788 return STATUS_INTERNAL_ERROR; 2789 } 2790 2791 if (tp2 && tp2->item->size < sizeof(DIR_ITEM)) { 2792 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset, 2793 tp2->item->size, sizeof(DIR_ITEM)); 2794 return STATUS_INTERNAL_ERROR; 2795 } 2796 2797 if (tp && !tp2) { 2798 ULONG len; 2799 DIR_ITEM* di; 2800 2801 len = tp->item->size; 2802 di = (DIR_ITEM*)tp->item->data; 2803 2804 do { 2805 ULONG pos; 2806 2807 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) { 2808 ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset); 2809 return STATUS_INTERNAL_ERROR; 2810 } 2811 2812 pos = context->datalen; 2813 send_command(context, BTRFS_SEND_CMD_SET_XATTR); 2814 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2815 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, di->name, di->n); 2816 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_DATA, &di->name[di->n], di->m); 2817 send_command_finish(context, pos); 2818 2819 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n; 2820 di = (DIR_ITEM*)&di->name[di->m + di->n]; 2821 } while (len > 0); 2822 } else if (!tp && tp2) { 2823 ULONG len; 2824 DIR_ITEM* di; 2825 2826 len = tp2->item->size; 2827 di = (DIR_ITEM*)tp2->item->data; 2828 2829 do { 2830 ULONG pos; 2831 2832 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) { 2833 ERR("(%I64x,%x,%I64x) was truncated\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset); 2834 return STATUS_INTERNAL_ERROR; 2835 } 2836 2837 pos = context->datalen; 2838 send_command(context, BTRFS_SEND_CMD_REMOVE_XATTR); 2839 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2840 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, di->name, di->n); 2841 send_command_finish(context, pos); 2842 2843 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n; 2844 di = (DIR_ITEM*)&di->name[di->m + di->n]; 2845 } while (len > 0); 2846 } else { 2847 ULONG len; 2848 DIR_ITEM* di; 2849 LIST_ENTRY xattrs; 2850 2851 InitializeListHead(&xattrs); 2852 2853 len = tp->item->size; 2854 di = (DIR_ITEM*)tp->item->data; 2855 2856 do { 2857 xattr_cmp* xa; 2858 2859 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) { 2860 ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset); 2861 return STATUS_INTERNAL_ERROR; 2862 } 2863 2864 xa = ExAllocatePoolWithTag(PagedPool, sizeof(xattr_cmp), ALLOC_TAG); 2865 if (!xa) { 2866 ERR("out of memory\n"); 2867 2868 while (!IsListEmpty(&xattrs)) { 2869 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&xattrs), xattr_cmp, list_entry)); 2870 } 2871 2872 return STATUS_INSUFFICIENT_RESOURCES; 2873 } 2874 2875 xa->namelen = di->n; 2876 xa->name = di->name; 2877 xa->value1len = di->m; 2878 xa->value1 = di->name + di->n; 2879 xa->value2len = 0; 2880 xa->value2 = NULL; 2881 2882 InsertTailList(&xattrs, &xa->list_entry); 2883 2884 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n; 2885 di = (DIR_ITEM*)&di->name[di->m + di->n]; 2886 } while (len > 0); 2887 2888 len = tp2->item->size; 2889 di = (DIR_ITEM*)tp2->item->data; 2890 2891 do { 2892 xattr_cmp* xa; 2893 LIST_ENTRY* le; 2894 bool found = false; 2895 2896 if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) { 2897 ERR("(%I64x,%x,%I64x) was truncated\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset); 2898 return STATUS_INTERNAL_ERROR; 2899 } 2900 2901 le = xattrs.Flink; 2902 while (le != &xattrs) { 2903 xa = CONTAINING_RECORD(le, xattr_cmp, list_entry); 2904 2905 if (xa->namelen == di->n && RtlCompareMemory(xa->name, di->name, di->n) == di->n) { 2906 xa->value2len = di->m; 2907 xa->value2 = di->name + di->n; 2908 found = true; 2909 break; 2910 } 2911 2912 le = le->Flink; 2913 } 2914 2915 if (!found) { 2916 xa = ExAllocatePoolWithTag(PagedPool, sizeof(xattr_cmp), ALLOC_TAG); 2917 if (!xa) { 2918 ERR("out of memory\n"); 2919 2920 while (!IsListEmpty(&xattrs)) { 2921 ExFreePool(CONTAINING_RECORD(RemoveHeadList(&xattrs), xattr_cmp, list_entry)); 2922 } 2923 2924 return STATUS_INSUFFICIENT_RESOURCES; 2925 } 2926 2927 xa->namelen = di->n; 2928 xa->name = di->name; 2929 xa->value1len = 0; 2930 xa->value1 = NULL; 2931 xa->value2len = di->m; 2932 xa->value2 = di->name + di->n; 2933 2934 InsertTailList(&xattrs, &xa->list_entry); 2935 } 2936 2937 len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n; 2938 di = (DIR_ITEM*)&di->name[di->m + di->n]; 2939 } while (len > 0); 2940 2941 while (!IsListEmpty(&xattrs)) { 2942 xattr_cmp* xa = CONTAINING_RECORD(RemoveHeadList(&xattrs), xattr_cmp, list_entry); 2943 2944 if (xa->value1len != xa->value2len || !xa->value1 || !xa->value2 || RtlCompareMemory(xa->value1, xa->value2, xa->value1len) != xa->value1len) { 2945 ULONG pos; 2946 2947 if (!xa->value1) { 2948 pos = context->datalen; 2949 send_command(context, BTRFS_SEND_CMD_REMOVE_XATTR); 2950 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2951 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, xa->name, xa->namelen); 2952 send_command_finish(context, pos); 2953 } else { 2954 pos = context->datalen; 2955 send_command(context, BTRFS_SEND_CMD_SET_XATTR); 2956 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0); 2957 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, xa->name, xa->namelen); 2958 send_add_tlv(context, BTRFS_SEND_TLV_XATTR_DATA, xa->value1, xa->value1len); 2959 send_command_finish(context, pos); 2960 } 2961 } 2962 2963 ExFreePool(xa); 2964 } 2965 } 2966 2967 return STATUS_SUCCESS; 2968 } 2969 2970 _Function_class_(KSTART_ROUTINE) 2971 static void __stdcall send_thread(void* ctx) { 2972 send_context* context = (send_context*)ctx; 2973 NTSTATUS Status; 2974 KEY searchkey; 2975 traverse_ptr tp, tp2; 2976 2977 InterlockedIncrement(&context->root->send_ops); 2978 2979 if (context->parent) 2980 InterlockedIncrement(&context->parent->send_ops); 2981 2982 if (context->clones) { 2983 ULONG i; 2984 2985 for (i = 0; i < context->num_clones; i++) { 2986 InterlockedIncrement(&context->clones[i]->send_ops); 2987 } 2988 } 2989 2990 ExAcquireResourceExclusiveLite(&context->Vcb->tree_lock, true); 2991 2992 flush_subvol_fcbs(context->root); 2993 2994 if (context->parent) 2995 flush_subvol_fcbs(context->parent); 2996 2997 if (context->Vcb->need_write) 2998 Status = do_write(context->Vcb, NULL); 2999 else 3000 Status = STATUS_SUCCESS; 3001 3002 free_trees(context->Vcb); 3003 3004 if (!NT_SUCCESS(Status)) { 3005 ERR("do_write returned %08lx\n", Status); 3006 ExReleaseResourceLite(&context->Vcb->tree_lock); 3007 goto end; 3008 } 3009 3010 ExConvertExclusiveToSharedLite(&context->Vcb->tree_lock); 3011 3012 searchkey.obj_id = searchkey.offset = 0; 3013 searchkey.obj_type = 0; 3014 3015 Status = find_item(context->Vcb, context->root, &tp, &searchkey, false, NULL); 3016 if (!NT_SUCCESS(Status)) { 3017 ERR("find_item returned %08lx\n", Status); 3018 ExReleaseResourceLite(&context->Vcb->tree_lock); 3019 goto end; 3020 } 3021 3022 if (context->parent) { 3023 bool ended1 = false, ended2 = false; 3024 Status = find_item(context->Vcb, context->parent, &tp2, &searchkey, false, NULL); 3025 if (!NT_SUCCESS(Status)) { 3026 ERR("find_item returned %08lx\n", Status); 3027 ExReleaseResourceLite(&context->Vcb->tree_lock); 3028 goto end; 3029 } 3030 3031 do { 3032 traverse_ptr next_tp; 3033 3034 if (context->datalen > SEND_BUFFER_LENGTH) { 3035 KEY key1 = tp.item->key, key2 = tp2.item->key; 3036 3037 ExReleaseResourceLite(&context->Vcb->tree_lock); 3038 3039 KeClearEvent(&context->send->cleared_event); 3040 KeSetEvent(&context->buffer_event, 0, true); 3041 KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, false, NULL); 3042 3043 if (context->send->cancelling) 3044 goto end; 3045 3046 ExAcquireResourceSharedLite(&context->Vcb->tree_lock, true); 3047 3048 if (!ended1) { 3049 Status = find_item(context->Vcb, context->root, &tp, &key1, false, NULL); 3050 if (!NT_SUCCESS(Status)) { 3051 ERR("find_item returned %08lx\n", Status); 3052 ExReleaseResourceLite(&context->Vcb->tree_lock); 3053 goto end; 3054 } 3055 3056 if (keycmp(tp.item->key, key1)) { 3057 ERR("readonly subvolume changed\n"); 3058 ExReleaseResourceLite(&context->Vcb->tree_lock); 3059 Status = STATUS_INTERNAL_ERROR; 3060 goto end; 3061 } 3062 } 3063 3064 if (!ended2) { 3065 Status = find_item(context->Vcb, context->parent, &tp2, &key2, false, NULL); 3066 if (!NT_SUCCESS(Status)) { 3067 ERR("find_item returned %08lx\n", Status); 3068 ExReleaseResourceLite(&context->Vcb->tree_lock); 3069 goto end; 3070 } 3071 3072 if (keycmp(tp2.item->key, key2)) { 3073 ERR("readonly subvolume changed\n"); 3074 ExReleaseResourceLite(&context->Vcb->tree_lock); 3075 Status = STATUS_INTERNAL_ERROR; 3076 goto end; 3077 } 3078 } 3079 } 3080 3081 while (!ended1 && !ended2 && tp.tree->header.address == tp2.tree->header.address) { 3082 Status = skip_to_difference(context->Vcb, &tp, &tp2, &ended1, &ended2); 3083 if (!NT_SUCCESS(Status)) { 3084 ERR("skip_to_difference returned %08lx\n", Status); 3085 ExReleaseResourceLite(&context->Vcb->tree_lock); 3086 goto end; 3087 } 3088 } 3089 3090 if (!ended1 && !ended2 && !keycmp(tp.item->key, tp2.item->key)) { 3091 bool no_next = false, no_next2 = false; 3092 3093 TRACE("~ %I64x,%x,%I64x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 3094 3095 if (context->lastinode.inode != 0 && tp.item->key.obj_id > context->lastinode.inode) { 3096 Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2); 3097 if (!NT_SUCCESS(Status)) { 3098 ERR("finish_inode returned %08lx\n", Status); 3099 ExReleaseResourceLite(&context->Vcb->tree_lock); 3100 goto end; 3101 } 3102 3103 if (context->send->cancelling) { 3104 ExReleaseResourceLite(&context->Vcb->tree_lock); 3105 goto end; 3106 } 3107 } 3108 3109 if (tp.item->key.obj_type == TYPE_INODE_ITEM) { 3110 if (tp.item->size == tp2.item->size && tp.item->size > 0 && RtlCompareMemory(tp.item->data, tp2.item->data, tp.item->size) == tp.item->size) { 3111 uint64_t inode = tp.item->key.obj_id; 3112 3113 while (true) { 3114 if (!find_next_item(context->Vcb, &tp, &next_tp, false, NULL)) { 3115 ended1 = true; 3116 break; 3117 } 3118 3119 tp = next_tp; 3120 3121 if (tp.item->key.obj_id != inode) 3122 break; 3123 } 3124 3125 while (true) { 3126 if (!find_next_item(context->Vcb, &tp2, &next_tp, false, NULL)) { 3127 ended2 = true; 3128 break; 3129 } 3130 3131 tp2 = next_tp; 3132 3133 if (tp2.item->key.obj_id != inode) 3134 break; 3135 } 3136 3137 no_next = true; 3138 } else if (tp.item->size > sizeof(uint64_t) && tp2.item->size > sizeof(uint64_t) && *(uint64_t*)tp.item->data != *(uint64_t*)tp2.item->data) { 3139 uint64_t inode = tp.item->key.obj_id; 3140 3141 Status = send_inode(context, NULL, &tp2); 3142 if (!NT_SUCCESS(Status)) { 3143 ERR("send_inode returned %08lx\n", Status); 3144 ExReleaseResourceLite(&context->Vcb->tree_lock); 3145 goto end; 3146 } 3147 3148 while (true) { 3149 if (!find_next_item(context->Vcb, &tp2, &next_tp, false, NULL)) { 3150 ended2 = true; 3151 break; 3152 } 3153 3154 tp2 = next_tp; 3155 3156 if (tp2.item->key.obj_id != inode) 3157 break; 3158 3159 if (tp2.item->key.obj_type == TYPE_INODE_REF) { 3160 Status = send_inode_ref(context, &tp2, true); 3161 if (!NT_SUCCESS(Status)) { 3162 ERR("send_inode_ref returned %08lx\n", Status); 3163 ExReleaseResourceLite(&context->Vcb->tree_lock); 3164 goto end; 3165 } 3166 } else if (tp2.item->key.obj_type == TYPE_INODE_EXTREF) { 3167 Status = send_inode_extref(context, &tp2, true); 3168 if (!NT_SUCCESS(Status)) { 3169 ERR("send_inode_extref returned %08lx\n", Status); 3170 ExReleaseResourceLite(&context->Vcb->tree_lock); 3171 goto end; 3172 } 3173 } 3174 } 3175 3176 Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2); 3177 if (!NT_SUCCESS(Status)) { 3178 ERR("finish_inode returned %08lx\n", Status); 3179 ExReleaseResourceLite(&context->Vcb->tree_lock); 3180 goto end; 3181 } 3182 3183 if (context->send->cancelling) { 3184 ExReleaseResourceLite(&context->Vcb->tree_lock); 3185 goto end; 3186 } 3187 3188 no_next2 = true; 3189 3190 Status = send_inode(context, &tp, NULL); 3191 if (!NT_SUCCESS(Status)) { 3192 ERR("send_inode returned %08lx\n", Status); 3193 ExReleaseResourceLite(&context->Vcb->tree_lock); 3194 goto end; 3195 } 3196 } else { 3197 Status = send_inode(context, &tp, &tp2); 3198 if (!NT_SUCCESS(Status)) { 3199 ERR("send_inode returned %08lx\n", Status); 3200 ExReleaseResourceLite(&context->Vcb->tree_lock); 3201 goto end; 3202 } 3203 } 3204 } else if (tp.item->key.obj_type == TYPE_INODE_REF) { 3205 Status = send_inode_ref(context, &tp, false); 3206 if (!NT_SUCCESS(Status)) { 3207 ERR("send_inode_ref returned %08lx\n", Status); 3208 ExReleaseResourceLite(&context->Vcb->tree_lock); 3209 goto end; 3210 } 3211 3212 Status = send_inode_ref(context, &tp2, true); 3213 if (!NT_SUCCESS(Status)) { 3214 ERR("send_inode_ref returned %08lx\n", Status); 3215 ExReleaseResourceLite(&context->Vcb->tree_lock); 3216 goto end; 3217 } 3218 } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) { 3219 Status = send_inode_extref(context, &tp, false); 3220 if (!NT_SUCCESS(Status)) { 3221 ERR("send_inode_extref returned %08lx\n", Status); 3222 ExReleaseResourceLite(&context->Vcb->tree_lock); 3223 goto end; 3224 } 3225 3226 Status = send_inode_extref(context, &tp2, true); 3227 if (!NT_SUCCESS(Status)) { 3228 ERR("send_inode_extref returned %08lx\n", Status); 3229 ExReleaseResourceLite(&context->Vcb->tree_lock); 3230 goto end; 3231 } 3232 } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) { 3233 Status = send_extent_data(context, &tp, &tp2); 3234 if (!NT_SUCCESS(Status)) { 3235 ERR("send_extent_data returned %08lx\n", Status); 3236 ExReleaseResourceLite(&context->Vcb->tree_lock); 3237 goto end; 3238 } 3239 3240 if (context->send->cancelling) { 3241 ExReleaseResourceLite(&context->Vcb->tree_lock); 3242 goto end; 3243 } 3244 } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) { 3245 Status = send_xattr(context, &tp, &tp2); 3246 if (!NT_SUCCESS(Status)) { 3247 ERR("send_xattr returned %08lx\n", Status); 3248 ExReleaseResourceLite(&context->Vcb->tree_lock); 3249 goto end; 3250 } 3251 3252 if (context->send->cancelling) { 3253 ExReleaseResourceLite(&context->Vcb->tree_lock); 3254 goto end; 3255 } 3256 } 3257 3258 if (!no_next) { 3259 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL)) 3260 tp = next_tp; 3261 else 3262 ended1 = true; 3263 3264 if (!no_next2) { 3265 if (find_next_item(context->Vcb, &tp2, &next_tp, false, NULL)) 3266 tp2 = next_tp; 3267 else 3268 ended2 = true; 3269 } 3270 } 3271 } else if (ended2 || (!ended1 && !ended2 && keycmp(tp.item->key, tp2.item->key) == -1)) { 3272 TRACE("A %I64x,%x,%I64x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 3273 3274 if (context->lastinode.inode != 0 && tp.item->key.obj_id > context->lastinode.inode) { 3275 Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2); 3276 if (!NT_SUCCESS(Status)) { 3277 ERR("finish_inode returned %08lx\n", Status); 3278 ExReleaseResourceLite(&context->Vcb->tree_lock); 3279 goto end; 3280 } 3281 3282 if (context->send->cancelling) { 3283 ExReleaseResourceLite(&context->Vcb->tree_lock); 3284 goto end; 3285 } 3286 } 3287 3288 if (tp.item->key.obj_type == TYPE_INODE_ITEM) { 3289 Status = send_inode(context, &tp, NULL); 3290 if (!NT_SUCCESS(Status)) { 3291 ERR("send_inode returned %08lx\n", Status); 3292 ExReleaseResourceLite(&context->Vcb->tree_lock); 3293 goto end; 3294 } 3295 } else if (tp.item->key.obj_type == TYPE_INODE_REF) { 3296 Status = send_inode_ref(context, &tp, false); 3297 if (!NT_SUCCESS(Status)) { 3298 ERR("send_inode_ref returned %08lx\n", Status); 3299 ExReleaseResourceLite(&context->Vcb->tree_lock); 3300 goto end; 3301 } 3302 } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) { 3303 Status = send_inode_extref(context, &tp, false); 3304 if (!NT_SUCCESS(Status)) { 3305 ERR("send_inode_extref returned %08lx\n", Status); 3306 ExReleaseResourceLite(&context->Vcb->tree_lock); 3307 goto end; 3308 } 3309 } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) { 3310 Status = send_extent_data(context, &tp, NULL); 3311 if (!NT_SUCCESS(Status)) { 3312 ERR("send_extent_data returned %08lx\n", Status); 3313 ExReleaseResourceLite(&context->Vcb->tree_lock); 3314 goto end; 3315 } 3316 3317 if (context->send->cancelling) { 3318 ExReleaseResourceLite(&context->Vcb->tree_lock); 3319 goto end; 3320 } 3321 } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) { 3322 Status = send_xattr(context, &tp, NULL); 3323 if (!NT_SUCCESS(Status)) { 3324 ERR("send_xattr returned %08lx\n", Status); 3325 ExReleaseResourceLite(&context->Vcb->tree_lock); 3326 goto end; 3327 } 3328 3329 if (context->send->cancelling) { 3330 ExReleaseResourceLite(&context->Vcb->tree_lock); 3331 goto end; 3332 } 3333 } 3334 3335 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL)) 3336 tp = next_tp; 3337 else 3338 ended1 = true; 3339 } else if (ended1 || (!ended1 && !ended2 && keycmp(tp.item->key, tp2.item->key) == 1)) { 3340 TRACE("B %I64x,%x,%I64x\n", tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset); 3341 3342 if (context->lastinode.inode != 0 && tp2.item->key.obj_id > context->lastinode.inode) { 3343 Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2); 3344 if (!NT_SUCCESS(Status)) { 3345 ERR("finish_inode returned %08lx\n", Status); 3346 ExReleaseResourceLite(&context->Vcb->tree_lock); 3347 goto end; 3348 } 3349 3350 if (context->send->cancelling) { 3351 ExReleaseResourceLite(&context->Vcb->tree_lock); 3352 goto end; 3353 } 3354 } 3355 3356 if (tp2.item->key.obj_type == TYPE_INODE_ITEM) { 3357 Status = send_inode(context, NULL, &tp2); 3358 if (!NT_SUCCESS(Status)) { 3359 ERR("send_inode returned %08lx\n", Status); 3360 ExReleaseResourceLite(&context->Vcb->tree_lock); 3361 goto end; 3362 } 3363 } else if (tp2.item->key.obj_type == TYPE_INODE_REF) { 3364 Status = send_inode_ref(context, &tp2, true); 3365 if (!NT_SUCCESS(Status)) { 3366 ERR("send_inode_ref returned %08lx\n", Status); 3367 ExReleaseResourceLite(&context->Vcb->tree_lock); 3368 goto end; 3369 } 3370 } else if (tp2.item->key.obj_type == TYPE_INODE_EXTREF) { 3371 Status = send_inode_extref(context, &tp2, true); 3372 if (!NT_SUCCESS(Status)) { 3373 ERR("send_inode_extref returned %08lx\n", Status); 3374 ExReleaseResourceLite(&context->Vcb->tree_lock); 3375 goto end; 3376 } 3377 } else if (tp2.item->key.obj_type == TYPE_EXTENT_DATA && !context->lastinode.deleting) { 3378 Status = send_extent_data(context, NULL, &tp2); 3379 if (!NT_SUCCESS(Status)) { 3380 ERR("send_extent_data returned %08lx\n", Status); 3381 ExReleaseResourceLite(&context->Vcb->tree_lock); 3382 goto end; 3383 } 3384 3385 if (context->send->cancelling) { 3386 ExReleaseResourceLite(&context->Vcb->tree_lock); 3387 goto end; 3388 } 3389 } else if (tp2.item->key.obj_type == TYPE_XATTR_ITEM && !context->lastinode.deleting) { 3390 Status = send_xattr(context, NULL, &tp2); 3391 if (!NT_SUCCESS(Status)) { 3392 ERR("send_xattr returned %08lx\n", Status); 3393 ExReleaseResourceLite(&context->Vcb->tree_lock); 3394 goto end; 3395 } 3396 3397 if (context->send->cancelling) { 3398 ExReleaseResourceLite(&context->Vcb->tree_lock); 3399 goto end; 3400 } 3401 } 3402 3403 if (find_next_item(context->Vcb, &tp2, &next_tp, false, NULL)) 3404 tp2 = next_tp; 3405 else 3406 ended2 = true; 3407 } 3408 } while (!ended1 || !ended2); 3409 } else { 3410 do { 3411 traverse_ptr next_tp; 3412 3413 if (context->datalen > SEND_BUFFER_LENGTH) { 3414 KEY key = tp.item->key; 3415 3416 ExReleaseResourceLite(&context->Vcb->tree_lock); 3417 3418 KeClearEvent(&context->send->cleared_event); 3419 KeSetEvent(&context->buffer_event, 0, true); 3420 KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, false, NULL); 3421 3422 if (context->send->cancelling) 3423 goto end; 3424 3425 ExAcquireResourceSharedLite(&context->Vcb->tree_lock, true); 3426 3427 Status = find_item(context->Vcb, context->root, &tp, &key, false, NULL); 3428 if (!NT_SUCCESS(Status)) { 3429 ERR("find_item returned %08lx\n", Status); 3430 ExReleaseResourceLite(&context->Vcb->tree_lock); 3431 goto end; 3432 } 3433 3434 if (keycmp(tp.item->key, key)) { 3435 ERR("readonly subvolume changed\n"); 3436 ExReleaseResourceLite(&context->Vcb->tree_lock); 3437 Status = STATUS_INTERNAL_ERROR; 3438 goto end; 3439 } 3440 } 3441 3442 if (context->lastinode.inode != 0 && tp.item->key.obj_id > context->lastinode.inode) { 3443 Status = finish_inode(context, &tp, NULL); 3444 if (!NT_SUCCESS(Status)) { 3445 ERR("finish_inode returned %08lx\n", Status); 3446 ExReleaseResourceLite(&context->Vcb->tree_lock); 3447 goto end; 3448 } 3449 3450 if (context->send->cancelling) { 3451 ExReleaseResourceLite(&context->Vcb->tree_lock); 3452 goto end; 3453 } 3454 } 3455 3456 if (tp.item->key.obj_type == TYPE_INODE_ITEM) { 3457 Status = send_inode(context, &tp, NULL); 3458 if (!NT_SUCCESS(Status)) { 3459 ERR("send_inode returned %08lx\n", Status); 3460 ExReleaseResourceLite(&context->Vcb->tree_lock); 3461 goto end; 3462 } 3463 } else if (tp.item->key.obj_type == TYPE_INODE_REF) { 3464 Status = send_inode_ref(context, &tp, false); 3465 if (!NT_SUCCESS(Status)) { 3466 ERR("send_inode_ref returned %08lx\n", Status); 3467 ExReleaseResourceLite(&context->Vcb->tree_lock); 3468 goto end; 3469 } 3470 } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) { 3471 Status = send_inode_extref(context, &tp, false); 3472 if (!NT_SUCCESS(Status)) { 3473 ERR("send_inode_extref returned %08lx\n", Status); 3474 ExReleaseResourceLite(&context->Vcb->tree_lock); 3475 goto end; 3476 } 3477 } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) { 3478 Status = send_extent_data(context, &tp, NULL); 3479 if (!NT_SUCCESS(Status)) { 3480 ERR("send_extent_data returned %08lx\n", Status); 3481 ExReleaseResourceLite(&context->Vcb->tree_lock); 3482 goto end; 3483 } 3484 3485 if (context->send->cancelling) { 3486 ExReleaseResourceLite(&context->Vcb->tree_lock); 3487 goto end; 3488 } 3489 } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) { 3490 Status = send_xattr(context, &tp, NULL); 3491 if (!NT_SUCCESS(Status)) { 3492 ERR("send_xattr returned %08lx\n", Status); 3493 ExReleaseResourceLite(&context->Vcb->tree_lock); 3494 goto end; 3495 } 3496 3497 if (context->send->cancelling) { 3498 ExReleaseResourceLite(&context->Vcb->tree_lock); 3499 goto end; 3500 } 3501 } 3502 3503 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL)) 3504 tp = next_tp; 3505 else 3506 break; 3507 } while (true); 3508 } 3509 3510 if (context->lastinode.inode != 0) { 3511 Status = finish_inode(context, NULL, NULL); 3512 if (!NT_SUCCESS(Status)) { 3513 ERR("finish_inode returned %08lx\n", Status); 3514 ExReleaseResourceLite(&context->Vcb->tree_lock); 3515 goto end; 3516 } 3517 3518 ExReleaseResourceLite(&context->Vcb->tree_lock); 3519 3520 if (context->send->cancelling) 3521 goto end; 3522 } else 3523 ExReleaseResourceLite(&context->Vcb->tree_lock); 3524 3525 KeClearEvent(&context->send->cleared_event); 3526 KeSetEvent(&context->buffer_event, 0, true); 3527 KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, false, NULL); 3528 3529 Status = STATUS_SUCCESS; 3530 3531 end: 3532 if (!NT_SUCCESS(Status)) { 3533 KeSetEvent(&context->buffer_event, 0, false); 3534 3535 if (context->send->ccb) 3536 context->send->ccb->send_status = Status; 3537 } 3538 3539 ExAcquireResourceExclusiveLite(&context->Vcb->send_load_lock, true); 3540 3541 while (!IsListEmpty(&context->orphans)) { 3542 orphan* o = CONTAINING_RECORD(RemoveHeadList(&context->orphans), orphan, list_entry); 3543 ExFreePool(o); 3544 } 3545 3546 while (!IsListEmpty(&context->dirs)) { 3547 send_dir* sd = CONTAINING_RECORD(RemoveHeadList(&context->dirs), send_dir, list_entry); 3548 3549 if (sd->name) 3550 ExFreePool(sd->name); 3551 3552 while (!IsListEmpty(&sd->deleted_children)) { 3553 deleted_child* dc = CONTAINING_RECORD(RemoveHeadList(&sd->deleted_children), deleted_child, list_entry); 3554 ExFreePool(dc); 3555 } 3556 3557 ExFreePool(sd); 3558 } 3559 3560 ZwClose(context->send->thread); 3561 context->send->thread = NULL; 3562 3563 if (context->send->ccb) 3564 context->send->ccb->send = NULL; 3565 3566 RemoveEntryList(&context->send->list_entry); 3567 ExFreePool(context->send); 3568 ExFreePool(context->data); 3569 3570 InterlockedDecrement(&context->Vcb->running_sends); 3571 InterlockedDecrement(&context->root->send_ops); 3572 3573 if (context->parent) 3574 InterlockedDecrement(&context->parent->send_ops); 3575 3576 ExReleaseResourceLite(&context->Vcb->send_load_lock); 3577 3578 if (context->clones) { 3579 ULONG i; 3580 3581 for (i = 0; i < context->num_clones; i++) { 3582 InterlockedDecrement(&context->clones[i]->send_ops); 3583 } 3584 3585 ExFreePool(context->clones); 3586 } 3587 3588 ExFreePool(context); 3589 3590 PsTerminateSystemThread(STATUS_SUCCESS); 3591 } 3592 3593 NTSTATUS send_subvol(device_extension* Vcb, void* data, ULONG datalen, PFILE_OBJECT FileObject, PIRP Irp) { 3594 NTSTATUS Status; 3595 fcb* fcb; 3596 ccb* ccb; 3597 root* parsubvol = NULL; 3598 send_context* context; 3599 send_info* send; 3600 ULONG num_clones = 0; 3601 root** clones = NULL; 3602 OBJECT_ATTRIBUTES oa; 3603 3604 if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb) 3605 return STATUS_INVALID_PARAMETER; 3606 3607 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode)) 3608 return STATUS_PRIVILEGE_NOT_HELD; 3609 3610 fcb = FileObject->FsContext; 3611 ccb = FileObject->FsContext2; 3612 3613 if (fcb->inode != SUBVOL_ROOT_INODE || fcb == Vcb->root_fileref->fcb) 3614 return STATUS_INVALID_PARAMETER; 3615 3616 if (!Vcb->readonly && !(fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY)) 3617 return STATUS_INVALID_PARAMETER; 3618 3619 if (data) { 3620 btrfs_send_subvol* bss = (btrfs_send_subvol*)data; 3621 HANDLE parent; 3622 3623 #if defined(_WIN64) 3624 if (IoIs32bitProcess(Irp)) { 3625 btrfs_send_subvol32* bss32 = (btrfs_send_subvol32*)data; 3626 3627 if (datalen < offsetof(btrfs_send_subvol32, num_clones)) 3628 return STATUS_INVALID_PARAMETER; 3629 3630 parent = Handle32ToHandle(bss32->parent); 3631 3632 if (datalen >= offsetof(btrfs_send_subvol32, clones[0])) 3633 num_clones = bss32->num_clones; 3634 3635 if (datalen < offsetof(btrfs_send_subvol32, clones[0]) + (num_clones * sizeof(uint32_t))) 3636 return STATUS_INVALID_PARAMETER; 3637 } else { 3638 #endif 3639 if (datalen < offsetof(btrfs_send_subvol, num_clones)) 3640 return STATUS_INVALID_PARAMETER; 3641 3642 parent = bss->parent; 3643 3644 if (datalen >= offsetof(btrfs_send_subvol, clones[0])) 3645 num_clones = bss->num_clones; 3646 3647 if (datalen < offsetof(btrfs_send_subvol, clones[0]) + (num_clones * sizeof(HANDLE))) 3648 return STATUS_INVALID_PARAMETER; 3649 #if defined(_WIN64) 3650 } 3651 #endif 3652 3653 if (parent) { 3654 PFILE_OBJECT fileobj; 3655 struct _fcb* parfcb; 3656 3657 Status = ObReferenceObjectByHandle(parent, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL); 3658 if (!NT_SUCCESS(Status)) { 3659 ERR("ObReferenceObjectByHandle returned %08lx\n", Status); 3660 return Status; 3661 } 3662 3663 if (fileobj->DeviceObject != FileObject->DeviceObject) { 3664 ObDereferenceObject(fileobj); 3665 return STATUS_INVALID_PARAMETER; 3666 } 3667 3668 parfcb = fileobj->FsContext; 3669 3670 if (!parfcb || parfcb == Vcb->root_fileref->fcb || parfcb == Vcb->volume_fcb || parfcb->inode != SUBVOL_ROOT_INODE) { 3671 ObDereferenceObject(fileobj); 3672 return STATUS_INVALID_PARAMETER; 3673 } 3674 3675 parsubvol = parfcb->subvol; 3676 ObDereferenceObject(fileobj); 3677 3678 if (!Vcb->readonly && !(parsubvol->root_item.flags & BTRFS_SUBVOL_READONLY)) 3679 return STATUS_INVALID_PARAMETER; 3680 3681 if (parsubvol == fcb->subvol) 3682 return STATUS_INVALID_PARAMETER; 3683 } 3684 3685 if (num_clones > 0) { 3686 ULONG i; 3687 3688 clones = ExAllocatePoolWithTag(PagedPool, sizeof(root*) * num_clones, ALLOC_TAG); 3689 if (!clones) { 3690 ERR("out of memory\n"); 3691 return STATUS_INSUFFICIENT_RESOURCES; 3692 } 3693 3694 for (i = 0; i < num_clones; i++) { 3695 HANDLE h; 3696 PFILE_OBJECT fileobj; 3697 struct _fcb* clonefcb; 3698 3699 #if defined(_WIN64) 3700 if (IoIs32bitProcess(Irp)) { 3701 btrfs_send_subvol32* bss32 = (btrfs_send_subvol32*)data; 3702 3703 h = Handle32ToHandle(bss32->clones[i]); 3704 } else 3705 #endif 3706 h = bss->clones[i]; 3707 3708 Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL); 3709 if (!NT_SUCCESS(Status)) { 3710 ERR("ObReferenceObjectByHandle returned %08lx\n", Status); 3711 ExFreePool(clones); 3712 return Status; 3713 } 3714 3715 if (fileobj->DeviceObject != FileObject->DeviceObject) { 3716 ObDereferenceObject(fileobj); 3717 ExFreePool(clones); 3718 return STATUS_INVALID_PARAMETER; 3719 } 3720 3721 clonefcb = fileobj->FsContext; 3722 3723 if (!clonefcb || clonefcb == Vcb->root_fileref->fcb || clonefcb == Vcb->volume_fcb || clonefcb->inode != SUBVOL_ROOT_INODE) { 3724 ObDereferenceObject(fileobj); 3725 ExFreePool(clones); 3726 return STATUS_INVALID_PARAMETER; 3727 } 3728 3729 clones[i] = clonefcb->subvol; 3730 ObDereferenceObject(fileobj); 3731 3732 if (!Vcb->readonly && !(clones[i]->root_item.flags & BTRFS_SUBVOL_READONLY)) { 3733 ExFreePool(clones); 3734 return STATUS_INVALID_PARAMETER; 3735 } 3736 } 3737 } 3738 } 3739 3740 ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, true); 3741 3742 if (ccb->send) { 3743 WARN("send operation already running\n"); 3744 ExReleaseResourceLite(&Vcb->send_load_lock); 3745 return STATUS_DEVICE_NOT_READY; 3746 } 3747 3748 context = ExAllocatePoolWithTag(NonPagedPool, sizeof(send_context), ALLOC_TAG); 3749 if (!context) { 3750 ERR("out of memory\n"); 3751 3752 if (clones) 3753 ExFreePool(clones); 3754 3755 ExReleaseResourceLite(&Vcb->send_load_lock); 3756 return STATUS_INSUFFICIENT_RESOURCES; 3757 } 3758 3759 context->Vcb = Vcb; 3760 context->root = fcb->subvol; 3761 context->parent = parsubvol; 3762 InitializeListHead(&context->orphans); 3763 InitializeListHead(&context->dirs); 3764 InitializeListHead(&context->pending_rmdirs); 3765 context->lastinode.inode = 0; 3766 context->lastinode.path = NULL; 3767 context->lastinode.sd = NULL; 3768 context->root_dir = NULL; 3769 context->num_clones = num_clones; 3770 context->clones = clones; 3771 InitializeListHead(&context->lastinode.refs); 3772 InitializeListHead(&context->lastinode.oldrefs); 3773 InitializeListHead(&context->lastinode.exts); 3774 InitializeListHead(&context->lastinode.oldexts); 3775 3776 context->data = ExAllocatePoolWithTag(PagedPool, SEND_BUFFER_LENGTH + (2 * MAX_SEND_WRITE), ALLOC_TAG); // give ourselves some wiggle room 3777 if (!context->data) { 3778 ExFreePool(context); 3779 ExReleaseResourceLite(&Vcb->send_load_lock); 3780 return STATUS_INSUFFICIENT_RESOURCES; 3781 } 3782 3783 context->datalen = 0; 3784 3785 send_subvol_header(context, fcb->subvol, ccb->fileref); // FIXME - fileref needs some sort of lock here 3786 3787 KeInitializeEvent(&context->buffer_event, NotificationEvent, false); 3788 3789 send = ExAllocatePoolWithTag(NonPagedPool, sizeof(send_info), ALLOC_TAG); 3790 if (!send) { 3791 ERR("out of memory\n"); 3792 ExFreePool(context->data); 3793 ExFreePool(context); 3794 3795 if (clones) 3796 ExFreePool(clones); 3797 3798 ExReleaseResourceLite(&Vcb->send_load_lock); 3799 return STATUS_INSUFFICIENT_RESOURCES; 3800 } 3801 3802 KeInitializeEvent(&send->cleared_event, NotificationEvent, false); 3803 3804 send->context = context; 3805 context->send = send; 3806 3807 ccb->send = send; 3808 send->ccb = ccb; 3809 ccb->send_status = STATUS_SUCCESS; 3810 3811 send->cancelling = false; 3812 3813 InterlockedIncrement(&Vcb->running_sends); 3814 3815 InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); 3816 3817 Status = PsCreateSystemThread(&send->thread, 0, &oa, NULL, NULL, send_thread, context); 3818 if (!NT_SUCCESS(Status)) { 3819 ERR("PsCreateSystemThread returned %08lx\n", Status); 3820 ccb->send = NULL; 3821 InterlockedDecrement(&Vcb->running_sends); 3822 ExFreePool(send); 3823 ExFreePool(context->data); 3824 ExFreePool(context); 3825 3826 if (clones) 3827 ExFreePool(clones); 3828 3829 ExReleaseResourceLite(&Vcb->send_load_lock); 3830 return Status; 3831 } 3832 3833 InsertTailList(&Vcb->send_ops, &send->list_entry); 3834 ExReleaseResourceLite(&Vcb->send_load_lock); 3835 3836 return STATUS_SUCCESS; 3837 } 3838 3839 NTSTATUS read_send_buffer(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, ULONG_PTR* retlen, KPROCESSOR_MODE processor_mode) { 3840 ccb* ccb; 3841 send_context* context; 3842 3843 ccb = FileObject ? FileObject->FsContext2 : NULL; 3844 if (!ccb) 3845 return STATUS_INVALID_PARAMETER; 3846 3847 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode)) 3848 return STATUS_PRIVILEGE_NOT_HELD; 3849 3850 ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, true); 3851 3852 if (!ccb->send) { 3853 ExReleaseResourceLite(&Vcb->send_load_lock); 3854 return !NT_SUCCESS(ccb->send_status) ? ccb->send_status : STATUS_END_OF_FILE; 3855 } 3856 3857 context = (send_context*)ccb->send->context; 3858 3859 KeWaitForSingleObject(&context->buffer_event, Executive, KernelMode, false, NULL); 3860 3861 if (datalen == 0) { 3862 ExReleaseResourceLite(&Vcb->send_load_lock); 3863 return STATUS_SUCCESS; 3864 } 3865 3866 RtlCopyMemory(data, context->data, min(datalen, context->datalen)); 3867 3868 if (datalen < context->datalen) { // not empty yet 3869 *retlen = datalen; 3870 RtlMoveMemory(context->data, &context->data[datalen], context->datalen - datalen); 3871 context->datalen -= datalen; 3872 ExReleaseResourceLite(&Vcb->send_load_lock); 3873 } else { 3874 *retlen = context->datalen; 3875 context->datalen = 0; 3876 ExReleaseResourceLite(&Vcb->send_load_lock); 3877 3878 KeClearEvent(&context->buffer_event); 3879 KeSetEvent(&ccb->send->cleared_event, 0, false); 3880 } 3881 3882 return STATUS_SUCCESS; 3883 } 3884