ReactOS  0.4.15-dev-1177-g6cb3b62
send.c
Go to the documentation of this file.
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 {
24  bool dummy;
28  struct send_dir* parent;
30  char* name;
32 } send_dir;
33 
34 typedef struct {
37  bool dir;
39  char tmpname[64];
40 } orphan;
41 
42 typedef struct {
45  char name[1];
47 
48 typedef struct {
52  char name[1];
53 } ref;
54 
55 typedef struct {
60 
61 typedef struct {
66 } send_ext;
67 
68 typedef struct {
82 
83  struct {
85  bool deleting;
86  bool new;
99  bool file;
100  char* path;
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);
115 
118 
119  bsc->cmd = cmd;
120  bsc->csum = 0;
121 
122  context->datalen += sizeof(btrfs_send_command);
123 }
124 
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 
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 
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++;
174  *ptr = '-'; ptr++;
175  ptr2 = ptr;
176 
177  searchkey.obj_id = SUBVOL_ROOT_INODE;
178  searchkey.obj_type = TYPE_DIR_ITEM;
179 
180  do {
183 
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 
218  LIST_ENTRY* le;
219 
220  le = context->orphans.Flink;
221  while (le != &context->orphans) {
223 
224  if (o2->inode > o->inode) {
226  return;
227  }
228 
229  le = le->Flink;
230  }
231 
232  InsertTailList(&context->orphans, &o->list_entry);
233 }
234 
237  KEY searchkey;
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,
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 
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) {
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 
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) {
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)
414  else if ((ii->st_mode & __S_IFLNK) == __S_IFLNK)
416  else if ((ii->st_mode & __S_IFCHR) == __S_IFCHR || (ii->st_mode & __S_IFBLK) == __S_IFBLK)
418  else if ((ii->st_mode & __S_IFDIR) == __S_IFDIR)
420  else if ((ii->st_mode & __S_IFIFO) == __S_IFIFO)
422  else {
424  context->lastinode.file = true;
425  }
426 
428 
430  if (!NT_SUCCESS(Status)) {
431  ERR("get_orphan_name returned %08lx\n", Status);
432  return Status;
433  }
434 
437 
439  uint64_t rdev = makedev((ii->st_rdev & 0xFFFFFFFFFFF) >> 20, ii->st_rdev & 0xFFFFF), mode = ii->st_mode;
440 
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 
454  }
455 
457 
458  if (ii->st_mode & __S_IFDIR) {
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 
472  if (!o) {
473  ERR("out of memory\n");
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");
487  }
488 
489  strcpy(context->lastinode.path, o->tmpname);
490 
491  context->lastinode.o = o;
492  }
493 
494  return STATUS_SUCCESS;
495 }
496 
498  LIST_ENTRY* le;
500 
501  if (!sd) {
502  ERR("out of memory\n");
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) {
518  if (!sd->name) {
519  ERR("out of memory\n");
520  ExFreePool(sd);
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) {
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 
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 
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 
588 
590 
591  if (len > 0)
592  find_path((char*)&context->data[context->datalen - len], parent, name, namelen);
593 }
594 
596  ULONG pos = context->datalen;
597 
598  if (context->lastinode.o) {
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 
604 
606  } else {
608 
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 
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");
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");
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 
657  ULONG pos = context->datalen;
658 
660 
661  send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, sd->parent, sd->name, sd->namelen);
662 
666 
668 }
669 
672  LIST_ENTRY* le;
673  char name[64];
674 
675  le = context->dirs.Flink;
676  while (le != &context->dirs) {
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;
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 
730  parent = context->root_dir;
731  else {
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 
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 
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) {
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 
818 
820 
822 
824 
826  if (!o2) {
827  ERR("out of memory\n");
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 
854  if (!r) {
855  ERR("out of memory\n");
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 
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) {
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) {
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 
927 
930 
932 
934  if (!o2) {
935  ERR("out of memory\n");
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 
951  if (!r) {
952  ERR("out of memory\n");
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 
970  ULONG pos = context->datalen;
971 
973 
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) {
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 
986 }
987 
989  ULONG pos = context->datalen;
990 
992 
996 
998 }
999 
1001  ULONG pos = context->datalen;
1002 
1004 
1005  mode &= 07777;
1006 
1009 
1011 }
1012 
1014  ULONG pos = context->datalen;
1015 
1017 
1022 
1024 }
1025 
1027  ULONG pos = context->datalen;
1028 
1030 
1033 
1035 }
1036 
1038  ULONG pos = context->datalen;
1039  uint16_t pathlen;
1040 
1042 
1043  pathlen = find_path_len(parent, namelen);
1045 
1046  find_path((char*)&context->data[context->datalen - pathlen], parent, name, namelen);
1047 
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 
1059 }
1060 
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 
1104  pending_rmdir* pr;
1105  LIST_ENTRY* le;
1106 
1108  if (!pr) {
1109  ERR("out of memory\n");
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) {
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 
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;
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 
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 
1187  if (!dc) {
1188  ERR("out of memory\n");
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) {
1200 
1201  if (o2->inode == inode) {
1203 
1204  send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, r->sd, r->name, r->namelen);
1205 
1207 
1208  return STATUS_SUCCESS;
1209  } else if (o2->inode > inode)
1210  break;
1211 
1212  le = le->Flink;
1213  }
1214 
1216  if (!NT_SUCCESS(Status)) {
1217  ERR("get_orphan_name returned %08lx\n", Status);
1218  return Status;
1219  }
1220 
1221  if (dir) {
1223  if (!NT_SUCCESS(Status)) {
1224  ERR("find_send_dir returned %08lx\n", Status);
1225  return Status;
1226  }
1227 
1228  sd->dummy = true;
1229 
1231 
1232  send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, r->sd, r->name, r->namelen);
1234 
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");
1245  }
1246 
1247  RtlCopyMemory(sd->name, name, sd->namelen);
1248  sd->parent = context->root_dir;
1249  } else {
1251 
1252  send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, r->sd, r->name, r->namelen);
1253 
1255 
1257  }
1258 
1260  if (!o) {
1261  ERR("out of memory\n");
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 
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");
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);
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 
1335 
1336  send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, (uint16_t)strlen(context->lastinode.path));
1337 
1339 
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");
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");
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 
1404  send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, (uint16_t)strlen(context->lastinode.path));
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");
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");
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;
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);
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) {
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");
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 
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 
1653  uint64_t lastoff = 0;
1654  LIST_ENTRY* le;
1655 
1656  le = exts->Flink;
1657  while (le != exts) {
1659 
1660  if (ext->offset > lastoff) {
1662  EXTENT_DATA2* ed2;
1663 
1664  if (!ext2) {
1665  ERR("out of memory\n");
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) {
1692  EXTENT_DATA2* ed2;
1693 
1694  if (!ext2) {
1695  ERR("out of memory\n");
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 
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");
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) {
1747 
1748  if (!ext2) {
1749  ERR("out of memory\n");
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 
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 
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 
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 
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 {
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 {
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 
1995 
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 
2003  context->datalen = pos;
2004  else {
2005  send_add_tlv(context, BTRFS_SEND_TLV_CLONE_OFFSET, &clone_offset, sizeof(uint64_t));
2006 
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 
2029  NTSTATUS Status;
2030  KEY searchkey;
2031  traverse_ptr tp;
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 {
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 
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 
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)) {
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 &&
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 
2185 
2186  send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2188 
2192  ULONG inlen = se->datalen - (ULONG)offsetof(EXTENT_DATA, data[0]);
2193 
2195  RtlZeroMemory(&context->data[context->datalen - se->data.decoded_size], (ULONG)se->data.decoded_size);
2196 
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 
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 
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;
2284 
2286  RtlZeroMemory(&context->data[context->datalen - length], length);
2287 
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);
2300  }
2301 
2302  for (off = ed2->offset; off < 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);
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);
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 
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;
2378 
2379  length = (uint16_t)min(context->lastinode.size - se->offset - off, length);
2381 
2383  }
2384 
2385  ExFreePool(buf);
2386  } else {
2387  uint8_t *buf, *compbuf;
2388  uint64_t off;
2389  void* csum;
2390 
2392  if (!buf) {
2393  ERR("out of memory\n");
2394  ExFreePool(se);
2395  if (se2) ExFreePool(se2);
2397  }
2398 
2400  if (!compbuf) {
2401  ERR("out of memory\n");
2402  ExFreePool(buf);
2403  ExFreePool(se);
2404  if (se2) ExFreePool(se2);
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);
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 
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) {
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) {
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 
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;
2515 
2516  length = (uint16_t)min(context->lastinode.size - se->offset - off, length);
2518 
2520  }
2521 
2522  ExFreePool(buf);
2523  }
2524 
2525  ExFreePool(se);
2526  if (se2) ExFreePool(se2);
2527  }
2528 
2529  return STATUS_SUCCESS;
2530 }
2531 
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)) {
2572  }
2573 
2574  while (!IsListEmpty(&context->lastinode.oldexts)) {
2576  }
2577 
2578  if (context->parent) {
2579  le = context->pending_rmdirs.Flink;
2580 
2581  while (le != &context->pending_rmdirs) {
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)) {
2596  ExFreePool(dc);
2597  }
2598 
2599  ExFreePool(pr->sd);
2600 
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 
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 
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 
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) {
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,
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) {
2685 
2686  if (!se) {
2687  ERR("out of memory\n");
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 
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 
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) {
2744 
2745  if (!se) {
2746  ERR("out of memory\n");
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 {
2762  char* name;
2764  char* value1;
2766  char* value2;
2768 } xattr_cmp;
2769 
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;
2814  send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
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;
2839  send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
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 
2865  if (!xa) {
2866  ERR("out of memory\n");
2867 
2868  while (!IsListEmpty(&xattrs)) {
2870  }
2871 
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) {
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) {
2917  if (!xa) {
2918  ERR("out of memory\n");
2919 
2920  while (!IsListEmpty(&xattrs)) {
2922  }
2923 
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)) {
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;
2950  send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2953  } else {
2954  pos = context->datalen;
2956  send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
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) {
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
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);
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);
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) {
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) {
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 
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) {
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) {
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) {
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(