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