xref: /reactos/drivers/filesystems/btrfs/send.c (revision 174dfab6)
1c2c66affSColin Finck /* Copyright (c) Mark Harmstone 2017
2c2c66affSColin Finck  *
3c2c66affSColin Finck  * This file is part of WinBtrfs.
4c2c66affSColin Finck  *
5c2c66affSColin Finck  * WinBtrfs is free software: you can redistribute it and/or modify
6c2c66affSColin Finck  * it under the terms of the GNU Lesser General Public Licence as published by
7c2c66affSColin Finck  * the Free Software Foundation, either version 3 of the Licence, or
8c2c66affSColin Finck  * (at your option) any later version.
9c2c66affSColin Finck  *
10c2c66affSColin Finck  * WinBtrfs is distributed in the hope that it will be useful,
11c2c66affSColin Finck  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12c2c66affSColin Finck  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13c2c66affSColin Finck  * GNU Lesser General Public Licence for more details.
14c2c66affSColin Finck  *
15c2c66affSColin Finck  * You should have received a copy of the GNU Lesser General Public Licence
16c2c66affSColin Finck  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
17c2c66affSColin Finck 
18c2c66affSColin Finck #include "btrfs_drv.h"
19194ea909SVictor Perevertkin #include "crc32c.h"
20c2c66affSColin Finck 
21c2c66affSColin Finck typedef struct send_dir {
22c2c66affSColin Finck     LIST_ENTRY list_entry;
23318da0c1SPierre Schweitzer     uint64_t inode;
24318da0c1SPierre Schweitzer     bool dummy;
25c2c66affSColin Finck     BTRFS_TIME atime;
26c2c66affSColin Finck     BTRFS_TIME mtime;
27c2c66affSColin Finck     BTRFS_TIME ctime;
28c2c66affSColin Finck     struct send_dir* parent;
29318da0c1SPierre Schweitzer     uint16_t namelen;
30c2c66affSColin Finck     char* name;
31c2c66affSColin Finck     LIST_ENTRY deleted_children;
32c2c66affSColin Finck } send_dir;
33c2c66affSColin Finck 
34c2c66affSColin Finck typedef struct {
35c2c66affSColin Finck     LIST_ENTRY list_entry;
36318da0c1SPierre Schweitzer     uint64_t inode;
37318da0c1SPierre Schweitzer     bool dir;
38c2c66affSColin Finck     send_dir* sd;
39c2c66affSColin Finck     char tmpname[64];
40c2c66affSColin Finck } orphan;
41c2c66affSColin Finck 
42c2c66affSColin Finck typedef struct {
43c2c66affSColin Finck     LIST_ENTRY list_entry;
44c2c66affSColin Finck     ULONG namelen;
45c2c66affSColin Finck     char name[1];
46c2c66affSColin Finck } deleted_child;
47c2c66affSColin Finck 
48c2c66affSColin Finck typedef struct {
49c2c66affSColin Finck     LIST_ENTRY list_entry;
50c2c66affSColin Finck     send_dir* sd;
51318da0c1SPierre Schweitzer     uint16_t namelen;
52c2c66affSColin Finck     char name[1];
53c2c66affSColin Finck } ref;
54c2c66affSColin Finck 
55c2c66affSColin Finck typedef struct {
56c2c66affSColin Finck     send_dir* sd;
57318da0c1SPierre Schweitzer     uint64_t last_child_inode;
58c2c66affSColin Finck     LIST_ENTRY list_entry;
59c2c66affSColin Finck } pending_rmdir;
60c2c66affSColin Finck 
61c2c66affSColin Finck typedef struct {
62318da0c1SPierre Schweitzer     uint64_t offset;
63c2c66affSColin Finck     LIST_ENTRY list_entry;
64c2c66affSColin Finck     ULONG datalen;
65c2c66affSColin Finck     EXTENT_DATA data;
66c2c66affSColin Finck } send_ext;
67c2c66affSColin Finck 
68c2c66affSColin Finck typedef struct {
69c2c66affSColin Finck     device_extension* Vcb;
70c2c66affSColin Finck     root* root;
71c2c66affSColin Finck     root* parent;
72318da0c1SPierre Schweitzer     uint8_t* data;
73c2c66affSColin Finck     ULONG datalen;
74c2c66affSColin Finck     ULONG num_clones;
75c2c66affSColin Finck     root** clones;
76c2c66affSColin Finck     LIST_ENTRY orphans;
77c2c66affSColin Finck     LIST_ENTRY dirs;
78c2c66affSColin Finck     LIST_ENTRY pending_rmdirs;
79c2c66affSColin Finck     KEVENT buffer_event;
80c2c66affSColin Finck     send_dir* root_dir;
81c2c66affSColin Finck     send_info* send;
82c2c66affSColin Finck 
83c2c66affSColin Finck     struct {
84318da0c1SPierre Schweitzer         uint64_t inode;
85318da0c1SPierre Schweitzer         bool deleting;
86318da0c1SPierre Schweitzer         bool new;
87318da0c1SPierre Schweitzer         uint64_t gen;
88318da0c1SPierre Schweitzer         uint64_t uid;
89318da0c1SPierre Schweitzer         uint64_t olduid;
90318da0c1SPierre Schweitzer         uint64_t gid;
91318da0c1SPierre Schweitzer         uint64_t oldgid;
92318da0c1SPierre Schweitzer         uint64_t mode;
93318da0c1SPierre Schweitzer         uint64_t oldmode;
94318da0c1SPierre Schweitzer         uint64_t size;
95318da0c1SPierre Schweitzer         uint64_t flags;
96c2c66affSColin Finck         BTRFS_TIME atime;
97c2c66affSColin Finck         BTRFS_TIME mtime;
98c2c66affSColin Finck         BTRFS_TIME ctime;
99318da0c1SPierre Schweitzer         bool file;
100c2c66affSColin Finck         char* path;
101c2c66affSColin Finck         orphan* o;
102c2c66affSColin Finck         send_dir* sd;
103c2c66affSColin Finck         LIST_ENTRY refs;
104c2c66affSColin Finck         LIST_ENTRY oldrefs;
105c2c66affSColin Finck         LIST_ENTRY exts;
106c2c66affSColin Finck         LIST_ENTRY oldexts;
107c2c66affSColin Finck     } lastinode;
108c2c66affSColin Finck } send_context;
109c2c66affSColin Finck 
110c2c66affSColin Finck #define MAX_SEND_WRITE 0xc000 // 48 KB
111c2c66affSColin Finck #define SEND_BUFFER_LENGTH 0x100000 // 1 MB
112c2c66affSColin Finck 
113318da0c1SPierre Schweitzer static NTSTATUS find_send_dir(send_context* context, uint64_t dir, uint64_t generation, send_dir** psd, bool* added_dummy);
114c2c66affSColin Finck static NTSTATUS wait_for_flush(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2);
115c2c66affSColin Finck 
send_command(send_context * context,uint16_t cmd)116318da0c1SPierre Schweitzer static void send_command(send_context* context, uint16_t cmd) {
117c2c66affSColin Finck     btrfs_send_command* bsc = (btrfs_send_command*)&context->data[context->datalen];
118c2c66affSColin Finck 
119c2c66affSColin Finck     bsc->cmd = cmd;
120c2c66affSColin Finck     bsc->csum = 0;
121c2c66affSColin Finck 
122c2c66affSColin Finck     context->datalen += sizeof(btrfs_send_command);
123c2c66affSColin Finck }
124c2c66affSColin Finck 
send_command_finish(send_context * context,ULONG pos)125c2c66affSColin Finck static void send_command_finish(send_context* context, ULONG pos) {
126c2c66affSColin Finck     btrfs_send_command* bsc = (btrfs_send_command*)&context->data[pos];
127c2c66affSColin Finck 
128c2c66affSColin Finck     bsc->length = context->datalen - pos - sizeof(btrfs_send_command);
129318da0c1SPierre Schweitzer     bsc->csum = calc_crc32c(0, (uint8_t*)bsc, context->datalen - pos);
130c2c66affSColin Finck }
131c2c66affSColin Finck 
send_add_tlv(send_context * context,uint16_t type,void * data,uint16_t length)132318da0c1SPierre Schweitzer static void send_add_tlv(send_context* context, uint16_t type, void* data, uint16_t length) {
133c2c66affSColin Finck     btrfs_send_tlv* tlv = (btrfs_send_tlv*)&context->data[context->datalen];
134c2c66affSColin Finck 
135c2c66affSColin Finck     tlv->type = type;
136c2c66affSColin Finck     tlv->length = length;
137c2c66affSColin Finck 
138c2c66affSColin Finck     if (length > 0 && data)
139c2c66affSColin Finck         RtlCopyMemory(&tlv[1], data, length);
140c2c66affSColin Finck 
141c2c66affSColin Finck     context->datalen += sizeof(btrfs_send_tlv) + length;
142c2c66affSColin Finck }
143c2c66affSColin Finck 
uint64_to_char(uint64_t num,char * buf)144318da0c1SPierre Schweitzer static char* uint64_to_char(uint64_t num, char* buf) {
145c2c66affSColin Finck     char *tmp, tmp2[20];
146c2c66affSColin Finck 
147c2c66affSColin Finck     if (num == 0) {
148c2c66affSColin Finck         buf[0] = '0';
149c2c66affSColin Finck         return buf + 1;
150c2c66affSColin Finck     }
151c2c66affSColin Finck 
152c2c66affSColin Finck     tmp = &tmp2[20];
153c2c66affSColin Finck     while (num > 0) {
154c2c66affSColin Finck         tmp--;
155c2c66affSColin Finck         *tmp = (num % 10) + '0';
156c2c66affSColin Finck         num /= 10;
157c2c66affSColin Finck     }
158c2c66affSColin Finck 
159c2c66affSColin Finck     RtlCopyMemory(buf, tmp, tmp2 + sizeof(tmp2) - tmp);
160c2c66affSColin Finck 
161c2c66affSColin Finck     return &buf[tmp2 + sizeof(tmp2) - tmp];
162c2c66affSColin Finck }
163c2c66affSColin Finck 
get_orphan_name(send_context * context,uint64_t inode,uint64_t generation,char * name)164318da0c1SPierre Schweitzer static NTSTATUS get_orphan_name(send_context* context, uint64_t inode, uint64_t generation, char* name) {
165c2c66affSColin Finck     char *ptr, *ptr2;
166318da0c1SPierre Schweitzer     uint64_t index = 0;
167c2c66affSColin Finck     KEY searchkey;
168c2c66affSColin Finck 
169c2c66affSColin Finck     name[0] = 'o';
170c2c66affSColin Finck 
171c2c66affSColin Finck     ptr = uint64_to_char(inode, &name[1]);
172c2c66affSColin Finck     *ptr = '-'; ptr++;
173c2c66affSColin Finck     ptr = uint64_to_char(generation, ptr);
174c2c66affSColin Finck     *ptr = '-'; ptr++;
175c2c66affSColin Finck     ptr2 = ptr;
176c2c66affSColin Finck 
177c2c66affSColin Finck     searchkey.obj_id = SUBVOL_ROOT_INODE;
178c2c66affSColin Finck     searchkey.obj_type = TYPE_DIR_ITEM;
179c2c66affSColin Finck 
180c2c66affSColin Finck     do {
181c2c66affSColin Finck         NTSTATUS Status;
182c2c66affSColin Finck         traverse_ptr tp;
183c2c66affSColin Finck 
184c2c66affSColin Finck         ptr = uint64_to_char(index, ptr);
185c2c66affSColin Finck         *ptr = 0;
186c2c66affSColin Finck 
187318da0c1SPierre Schweitzer         searchkey.offset = calc_crc32c(0xfffffffe, (uint8_t*)name, (ULONG)(ptr - name));
188c2c66affSColin Finck 
189318da0c1SPierre Schweitzer         Status = find_item(context->Vcb, context->root, &tp, &searchkey, false, NULL);
190c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
191194ea909SVictor Perevertkin             ERR("find_item returned %08lx\n", Status);
192c2c66affSColin Finck             return Status;
193c2c66affSColin Finck         }
194c2c66affSColin Finck 
195c2c66affSColin Finck         if (!keycmp(searchkey, tp.item->key))
196c2c66affSColin Finck             goto cont;
197c2c66affSColin Finck 
198c2c66affSColin Finck         if (context->parent) {
199318da0c1SPierre Schweitzer             Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL);
200c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
201194ea909SVictor Perevertkin                 ERR("find_item returned %08lx\n", Status);
202c2c66affSColin Finck                 return Status;
203c2c66affSColin Finck             }
204c2c66affSColin Finck 
205c2c66affSColin Finck             if (!keycmp(searchkey, tp.item->key))
206c2c66affSColin Finck                 goto cont;
207c2c66affSColin Finck         }
208c2c66affSColin Finck 
209c2c66affSColin Finck         return STATUS_SUCCESS;
210c2c66affSColin Finck 
211c2c66affSColin Finck cont:
212c2c66affSColin Finck         index++;
213c2c66affSColin Finck         ptr = ptr2;
214318da0c1SPierre Schweitzer     } while (true);
215c2c66affSColin Finck }
216c2c66affSColin Finck 
add_orphan(send_context * context,orphan * o)217c2c66affSColin Finck static void add_orphan(send_context* context, orphan* o) {
218c2c66affSColin Finck     LIST_ENTRY* le;
219c2c66affSColin Finck 
220c2c66affSColin Finck     le = context->orphans.Flink;
221c2c66affSColin Finck     while (le != &context->orphans) {
222c2c66affSColin Finck         orphan* o2 = CONTAINING_RECORD(le, orphan, list_entry);
223c2c66affSColin Finck 
224c2c66affSColin Finck         if (o2->inode > o->inode) {
225c2c66affSColin Finck             InsertHeadList(o2->list_entry.Blink, &o->list_entry);
226c2c66affSColin Finck             return;
227c2c66affSColin Finck         }
228c2c66affSColin Finck 
229c2c66affSColin Finck         le = le->Flink;
230c2c66affSColin Finck     }
231c2c66affSColin Finck 
232c2c66affSColin Finck     InsertTailList(&context->orphans, &o->list_entry);
233c2c66affSColin Finck }
234c2c66affSColin Finck 
send_read_symlink(send_context * context,uint64_t inode,char ** link,uint16_t * linklen)235318da0c1SPierre Schweitzer static NTSTATUS send_read_symlink(send_context* context, uint64_t inode, char** link, uint16_t* linklen) {
236c2c66affSColin Finck     NTSTATUS Status;
237c2c66affSColin Finck     KEY searchkey;
238c2c66affSColin Finck     traverse_ptr tp;
239c2c66affSColin Finck     EXTENT_DATA* ed;
240c2c66affSColin Finck 
241c2c66affSColin Finck     searchkey.obj_id = inode;
242c2c66affSColin Finck     searchkey.obj_type = TYPE_EXTENT_DATA;
243c2c66affSColin Finck     searchkey.offset = 0;
244c2c66affSColin Finck 
245318da0c1SPierre Schweitzer     Status = find_item(context->Vcb, context->root, &tp, &searchkey, false, NULL);
246c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
247194ea909SVictor Perevertkin         ERR("find_item returned %08lx\n", Status);
248c2c66affSColin Finck         return Status;
249c2c66affSColin Finck     }
250c2c66affSColin Finck 
251c2c66affSColin Finck     if (keycmp(tp.item->key, searchkey)) {
252318da0c1SPierre Schweitzer         ERR("could not find (%I64x,%x,%I64x)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
253c2c66affSColin Finck         return STATUS_INTERNAL_ERROR;
254c2c66affSColin Finck     }
255c2c66affSColin Finck 
256c2c66affSColin Finck     if (tp.item->size < sizeof(EXTENT_DATA)) {
257194ea909SVictor Perevertkin         ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
258c2c66affSColin Finck             tp.item->size, sizeof(EXTENT_DATA));
259c2c66affSColin Finck         return STATUS_INTERNAL_ERROR;
260c2c66affSColin Finck     }
261c2c66affSColin Finck 
262c2c66affSColin Finck     ed = (EXTENT_DATA*)tp.item->data;
263c2c66affSColin Finck 
264c2c66affSColin Finck     if (ed->type != EXTENT_TYPE_INLINE) {
265c2c66affSColin Finck         WARN("symlink data was not inline, returning blank string\n");
266c2c66affSColin Finck         *link = NULL;
267c2c66affSColin Finck         *linklen = 0;
268c2c66affSColin Finck         return STATUS_SUCCESS;
269c2c66affSColin Finck     }
270c2c66affSColin Finck 
271c2c66affSColin Finck     if (tp.item->size < offsetof(EXTENT_DATA, data[0]) + ed->decoded_size) {
272194ea909SVictor Perevertkin         ERR("(%I64x,%x,%I64x) was %u bytes, expected %I64u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
273c2c66affSColin Finck             tp.item->size, offsetof(EXTENT_DATA, data[0]) + ed->decoded_size);
274c2c66affSColin Finck         return STATUS_INTERNAL_ERROR;
275c2c66affSColin Finck     }
276c2c66affSColin Finck 
277c2c66affSColin Finck     *link = (char*)ed->data;
278318da0c1SPierre Schweitzer     *linklen = (uint16_t)ed->decoded_size;
279c2c66affSColin Finck 
280c2c66affSColin Finck     return STATUS_SUCCESS;
281c2c66affSColin Finck }
282c2c66affSColin Finck 
send_inode(send_context * context,traverse_ptr * tp,traverse_ptr * tp2)283c2c66affSColin Finck static NTSTATUS send_inode(send_context* context, traverse_ptr* tp, traverse_ptr* tp2) {
284c2c66affSColin Finck     NTSTATUS Status;
285c2c66affSColin Finck     INODE_ITEM* ii;
286c2c66affSColin Finck 
287c2c66affSColin Finck     if (tp2 && !tp) {
288c2c66affSColin Finck         INODE_ITEM* ii2 = (INODE_ITEM*)tp2->item->data;
289c2c66affSColin Finck 
290c2c66affSColin Finck         if (tp2->item->size < sizeof(INODE_ITEM)) {
291194ea909SVictor Perevertkin             ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
292c2c66affSColin Finck                 tp2->item->size, sizeof(INODE_ITEM));
293c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
294c2c66affSColin Finck         }
295c2c66affSColin Finck 
296c2c66affSColin Finck         context->lastinode.inode = tp2->item->key.obj_id;
297318da0c1SPierre Schweitzer         context->lastinode.deleting = true;
298c2c66affSColin Finck         context->lastinode.gen = ii2->generation;
299c2c66affSColin Finck         context->lastinode.mode = ii2->st_mode;
300c2c66affSColin Finck         context->lastinode.flags = ii2->flags;
301c2c66affSColin Finck         context->lastinode.o = NULL;
302c2c66affSColin Finck         context->lastinode.sd = NULL;
303c2c66affSColin Finck 
304c2c66affSColin Finck         return STATUS_SUCCESS;
305c2c66affSColin Finck     }
306c2c66affSColin Finck 
307c2c66affSColin Finck     ii = (INODE_ITEM*)tp->item->data;
308c2c66affSColin Finck 
309c2c66affSColin Finck     if (tp->item->size < sizeof(INODE_ITEM)) {
310194ea909SVictor Perevertkin         ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
311c2c66affSColin Finck             tp->item->size, sizeof(INODE_ITEM));
312c2c66affSColin Finck         return STATUS_INTERNAL_ERROR;
313c2c66affSColin Finck     }
314c2c66affSColin Finck 
315c2c66affSColin Finck     context->lastinode.inode = tp->item->key.obj_id;
316318da0c1SPierre Schweitzer     context->lastinode.deleting = false;
317c2c66affSColin Finck     context->lastinode.gen = ii->generation;
318c2c66affSColin Finck     context->lastinode.uid = ii->st_uid;
319c2c66affSColin Finck     context->lastinode.gid = ii->st_gid;
320c2c66affSColin Finck     context->lastinode.mode = ii->st_mode;
321c2c66affSColin Finck     context->lastinode.size = ii->st_size;
322c2c66affSColin Finck     context->lastinode.atime = ii->st_atime;
323c2c66affSColin Finck     context->lastinode.mtime = ii->st_mtime;
324c2c66affSColin Finck     context->lastinode.ctime = ii->st_ctime;
325c2c66affSColin Finck     context->lastinode.flags = ii->flags;
326318da0c1SPierre Schweitzer     context->lastinode.file = false;
327c2c66affSColin Finck     context->lastinode.o = NULL;
328c2c66affSColin Finck     context->lastinode.sd = NULL;
329c2c66affSColin Finck 
330c2c66affSColin Finck     if (context->lastinode.path) {
331c2c66affSColin Finck         ExFreePool(context->lastinode.path);
332c2c66affSColin Finck         context->lastinode.path = NULL;
333c2c66affSColin Finck     }
334c2c66affSColin Finck 
335c2c66affSColin Finck     if (tp2) {
336c2c66affSColin Finck         INODE_ITEM* ii2 = (INODE_ITEM*)tp2->item->data;
337c2c66affSColin Finck         LIST_ENTRY* le;
338c2c66affSColin Finck 
339c2c66affSColin Finck         if (tp2->item->size < sizeof(INODE_ITEM)) {
340194ea909SVictor Perevertkin             ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
341c2c66affSColin Finck                 tp2->item->size, sizeof(INODE_ITEM));
342c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
343c2c66affSColin Finck         }
344c2c66affSColin Finck 
345c2c66affSColin Finck         context->lastinode.oldmode = ii2->st_mode;
346c2c66affSColin Finck         context->lastinode.olduid = ii2->st_uid;
347c2c66affSColin Finck         context->lastinode.oldgid = ii2->st_gid;
348c2c66affSColin Finck 
349c2c66affSColin Finck         if ((ii2->st_mode & __S_IFREG) == __S_IFREG && (ii2->st_mode & __S_IFLNK) != __S_IFLNK && (ii2->st_mode & __S_IFSOCK) != __S_IFSOCK)
350318da0c1SPierre Schweitzer             context->lastinode.file = true;
351c2c66affSColin Finck 
352318da0c1SPierre Schweitzer         context->lastinode.new = false;
353c2c66affSColin Finck 
354c2c66affSColin Finck         le = context->orphans.Flink;
355c2c66affSColin Finck         while (le != &context->orphans) {
356c2c66affSColin Finck             orphan* o2 = CONTAINING_RECORD(le, orphan, list_entry);
357c2c66affSColin Finck 
358c2c66affSColin Finck             if (o2->inode == tp->item->key.obj_id) {
359c2c66affSColin Finck                 context->lastinode.o = o2;
360c2c66affSColin Finck                 break;
361c2c66affSColin Finck             } else if (o2->inode > tp->item->key.obj_id)
362c2c66affSColin Finck                 break;
363c2c66affSColin Finck 
364c2c66affSColin Finck             le = le->Flink;
365c2c66affSColin Finck         }
366c2c66affSColin Finck     } else
367318da0c1SPierre Schweitzer         context->lastinode.new = true;
368c2c66affSColin Finck 
369c2c66affSColin Finck     if (tp->item->key.obj_id == SUBVOL_ROOT_INODE) {
370c2c66affSColin Finck         send_dir* sd;
371c2c66affSColin Finck 
372c2c66affSColin Finck         Status = find_send_dir(context, tp->item->key.obj_id, ii->generation, &sd, NULL);
373c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
374194ea909SVictor Perevertkin             ERR("find_send_dir returned %08lx\n", Status);
375c2c66affSColin Finck             return Status;
376c2c66affSColin Finck         }
377c2c66affSColin Finck 
378c2c66affSColin Finck         sd->atime = ii->st_atime;
379c2c66affSColin Finck         sd->mtime = ii->st_mtime;
380c2c66affSColin Finck         sd->ctime = ii->st_ctime;
381c2c66affSColin Finck         context->root_dir = sd;
382c2c66affSColin Finck     } else if (!tp2) {
383c2c66affSColin Finck         ULONG pos = context->datalen;
384318da0c1SPierre Schweitzer         uint16_t cmd;
385c2c66affSColin Finck         send_dir* sd;
386c2c66affSColin Finck 
387c2c66affSColin Finck         char name[64];
388c2c66affSColin Finck         orphan* o;
389c2c66affSColin Finck 
390c2c66affSColin Finck         // skip creating orphan directory if we've already done so
391c2c66affSColin Finck         if (ii->st_mode & __S_IFDIR) {
392c2c66affSColin Finck             LIST_ENTRY* le;
393c2c66affSColin Finck 
394c2c66affSColin Finck             le = context->orphans.Flink;
395c2c66affSColin Finck             while (le != &context->orphans) {
396c2c66affSColin Finck                 orphan* o2 = CONTAINING_RECORD(le, orphan, list_entry);
397c2c66affSColin Finck 
398c2c66affSColin Finck                 if (o2->inode == tp->item->key.obj_id) {
399c2c66affSColin Finck                     context->lastinode.o = o2;
400c2c66affSColin Finck                     o2->sd->atime = ii->st_atime;
401c2c66affSColin Finck                     o2->sd->mtime = ii->st_mtime;
402c2c66affSColin Finck                     o2->sd->ctime = ii->st_ctime;
403318da0c1SPierre Schweitzer                     o2->sd->dummy = false;
404c2c66affSColin Finck                     return STATUS_SUCCESS;
405c2c66affSColin Finck                 } else if (o2->inode > tp->item->key.obj_id)
406c2c66affSColin Finck                     break;
407c2c66affSColin Finck 
408c2c66affSColin Finck                 le = le->Flink;
409c2c66affSColin Finck             }
410c2c66affSColin Finck         }
411c2c66affSColin Finck 
412c2c66affSColin Finck         if ((ii->st_mode & __S_IFSOCK) == __S_IFSOCK)
413c2c66affSColin Finck             cmd = BTRFS_SEND_CMD_MKSOCK;
414c2c66affSColin Finck         else if ((ii->st_mode & __S_IFLNK) == __S_IFLNK)
415c2c66affSColin Finck             cmd = BTRFS_SEND_CMD_SYMLINK;
416c2c66affSColin Finck         else if ((ii->st_mode & __S_IFCHR) == __S_IFCHR || (ii->st_mode & __S_IFBLK) == __S_IFBLK)
417c2c66affSColin Finck             cmd = BTRFS_SEND_CMD_MKNOD;
418c2c66affSColin Finck         else if ((ii->st_mode & __S_IFDIR) == __S_IFDIR)
419c2c66affSColin Finck             cmd = BTRFS_SEND_CMD_MKDIR;
420c2c66affSColin Finck         else if ((ii->st_mode & __S_IFIFO) == __S_IFIFO)
421c2c66affSColin Finck             cmd = BTRFS_SEND_CMD_MKFIFO;
422c2c66affSColin Finck         else {
423c2c66affSColin Finck             cmd = BTRFS_SEND_CMD_MKFILE;
424318da0c1SPierre Schweitzer             context->lastinode.file = true;
425c2c66affSColin Finck         }
426c2c66affSColin Finck 
427c2c66affSColin Finck         send_command(context, cmd);
428c2c66affSColin Finck 
429c2c66affSColin Finck         Status = get_orphan_name(context, tp->item->key.obj_id, ii->generation, name);
430c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
431194ea909SVictor Perevertkin             ERR("get_orphan_name returned %08lx\n", Status);
432c2c66affSColin Finck             return Status;
433c2c66affSColin Finck         }
434c2c66affSColin Finck 
435318da0c1SPierre Schweitzer         send_add_tlv(context, BTRFS_SEND_TLV_PATH, name, (uint16_t)strlen(name));
436318da0c1SPierre Schweitzer         send_add_tlv(context, BTRFS_SEND_TLV_INODE, &tp->item->key.obj_id, sizeof(uint64_t));
437c2c66affSColin Finck 
438c2c66affSColin Finck         if (cmd == BTRFS_SEND_CMD_MKNOD || cmd == BTRFS_SEND_CMD_MKFIFO || cmd == BTRFS_SEND_CMD_MKSOCK) {
439318da0c1SPierre Schweitzer             uint64_t rdev = makedev((ii->st_rdev & 0xFFFFFFFFFFF) >> 20, ii->st_rdev & 0xFFFFF), mode = ii->st_mode;
440c2c66affSColin Finck 
441318da0c1SPierre Schweitzer             send_add_tlv(context, BTRFS_SEND_TLV_RDEV, &rdev, sizeof(uint64_t));
442318da0c1SPierre Schweitzer             send_add_tlv(context, BTRFS_SEND_TLV_MODE, &mode, sizeof(uint64_t));
443c2c66affSColin Finck         } else if (cmd == BTRFS_SEND_CMD_SYMLINK && ii->st_size > 0) {
444c2c66affSColin Finck             char* link;
445318da0c1SPierre Schweitzer             uint16_t linklen;
446c2c66affSColin Finck 
447c2c66affSColin Finck             Status = send_read_symlink(context, tp->item->key.obj_id, &link, &linklen);
448c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
449194ea909SVictor Perevertkin                 ERR("send_read_symlink returned %08lx\n", Status);
450c2c66affSColin Finck                 return Status;
451c2c66affSColin Finck             }
452c2c66affSColin Finck 
453c2c66affSColin Finck             send_add_tlv(context, BTRFS_SEND_TLV_PATH_LINK, link, linklen);
454c2c66affSColin Finck         }
455c2c66affSColin Finck 
456c2c66affSColin Finck         send_command_finish(context, pos);
457c2c66affSColin Finck 
458c2c66affSColin Finck         if (ii->st_mode & __S_IFDIR) {
459c2c66affSColin Finck             Status = find_send_dir(context, tp->item->key.obj_id, ii->generation, &sd, NULL);
460c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
461194ea909SVictor Perevertkin                 ERR("find_send_dir returned %08lx\n", Status);
462c2c66affSColin Finck                 return Status;
463c2c66affSColin Finck             }
464c2c66affSColin Finck 
465318da0c1SPierre Schweitzer             sd->dummy = false;
466c2c66affSColin Finck         } else
467c2c66affSColin Finck             sd = NULL;
468c2c66affSColin Finck 
469c2c66affSColin Finck         context->lastinode.sd = sd;
470c2c66affSColin Finck 
471c2c66affSColin Finck         o = ExAllocatePoolWithTag(PagedPool, sizeof(orphan), ALLOC_TAG);
472c2c66affSColin Finck         if (!o) {
473c2c66affSColin Finck             ERR("out of memory\n");
474c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
475c2c66affSColin Finck         }
476c2c66affSColin Finck 
477c2c66affSColin Finck         o->inode = tp->item->key.obj_id;
478318da0c1SPierre Schweitzer         o->dir = (ii->st_mode & __S_IFDIR && ii->st_size > 0) ? true : false;
479c2c66affSColin Finck         strcpy(o->tmpname, name);
480c2c66affSColin Finck         o->sd = sd;
481c2c66affSColin Finck         add_orphan(context, o);
482c2c66affSColin Finck 
483c2c66affSColin Finck         context->lastinode.path = ExAllocatePoolWithTag(PagedPool, strlen(o->tmpname) + 1, ALLOC_TAG);
484c2c66affSColin Finck         if (!context->lastinode.path) {
485c2c66affSColin Finck             ERR("out of memory\n");
486c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
487c2c66affSColin Finck         }
488c2c66affSColin Finck 
489c2c66affSColin Finck         strcpy(context->lastinode.path, o->tmpname);
490c2c66affSColin Finck 
491c2c66affSColin Finck         context->lastinode.o = o;
492c2c66affSColin Finck     }
493c2c66affSColin Finck 
494c2c66affSColin Finck     return STATUS_SUCCESS;
495c2c66affSColin Finck }
496c2c66affSColin Finck 
send_add_dir(send_context * context,uint64_t inode,send_dir * parent,char * name,uint16_t namelen,bool dummy,LIST_ENTRY * lastentry,send_dir ** psd)497318da0c1SPierre Schweitzer static NTSTATUS send_add_dir(send_context* context, uint64_t inode, send_dir* parent, char* name, uint16_t namelen, bool dummy, LIST_ENTRY* lastentry, send_dir** psd) {
498c2c66affSColin Finck     LIST_ENTRY* le;
499c2c66affSColin Finck     send_dir* sd = ExAllocatePoolWithTag(PagedPool, sizeof(send_dir), ALLOC_TAG);
500c2c66affSColin Finck 
501c2c66affSColin Finck     if (!sd) {
502c2c66affSColin Finck         ERR("out of memory\n");
503c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
504c2c66affSColin Finck     }
505c2c66affSColin Finck 
506c2c66affSColin Finck     sd->inode = inode;
507c2c66affSColin Finck     sd->dummy = dummy;
508c2c66affSColin Finck     sd->parent = parent;
509c2c66affSColin Finck 
510c2c66affSColin Finck     if (!dummy) {
511c2c66affSColin Finck         sd->atime = context->lastinode.atime;
512c2c66affSColin Finck         sd->mtime = context->lastinode.mtime;
513c2c66affSColin Finck         sd->ctime = context->lastinode.ctime;
514c2c66affSColin Finck     }
515c2c66affSColin Finck 
516c2c66affSColin Finck     if (namelen > 0) {
517c2c66affSColin Finck         sd->name = ExAllocatePoolWithTag(PagedPool, namelen, ALLOC_TAG);
518c2c66affSColin Finck         if (!sd->name) {
519c2c66affSColin Finck             ERR("out of memory\n");
520c2c66affSColin Finck             ExFreePool(sd);
521c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
522c2c66affSColin Finck         }
523c2c66affSColin Finck 
524c2c66affSColin Finck         memcpy(sd->name, name, namelen);
525c2c66affSColin Finck     } else
526c2c66affSColin Finck         sd->name = NULL;
527c2c66affSColin Finck 
528c2c66affSColin Finck     sd->namelen = namelen;
529c2c66affSColin Finck 
530c2c66affSColin Finck     InitializeListHead(&sd->deleted_children);
531c2c66affSColin Finck 
532c2c66affSColin Finck     if (lastentry)
533c2c66affSColin Finck         InsertHeadList(lastentry, &sd->list_entry);
534c2c66affSColin Finck     else {
535c2c66affSColin Finck         le = context->dirs.Flink;
536c2c66affSColin Finck         while (le != &context->dirs) {
537c2c66affSColin Finck             send_dir* sd2 = CONTAINING_RECORD(le, send_dir, list_entry);
538c2c66affSColin Finck 
539c2c66affSColin Finck             if (sd2->inode > sd->inode) {
540c2c66affSColin Finck                 InsertHeadList(sd2->list_entry.Blink, &sd->list_entry);
541c2c66affSColin Finck 
542c2c66affSColin Finck                 if (psd)
543c2c66affSColin Finck                     *psd = sd;
544c2c66affSColin Finck 
545c2c66affSColin Finck                 return STATUS_SUCCESS;
546c2c66affSColin Finck             }
547c2c66affSColin Finck 
548c2c66affSColin Finck             le = le->Flink;
549c2c66affSColin Finck         }
550c2c66affSColin Finck 
551c2c66affSColin Finck         InsertTailList(&context->dirs, &sd->list_entry);
552c2c66affSColin Finck     }
553c2c66affSColin Finck 
554c2c66affSColin Finck     if (psd)
555c2c66affSColin Finck         *psd = sd;
556c2c66affSColin Finck 
557c2c66affSColin Finck     return STATUS_SUCCESS;
558c2c66affSColin Finck }
559c2c66affSColin Finck 
find_path_len(send_dir * parent,uint16_t namelen)560318da0c1SPierre Schweitzer static __inline uint16_t find_path_len(send_dir* parent, uint16_t namelen) {
561318da0c1SPierre Schweitzer     uint16_t len = namelen;
562c2c66affSColin Finck 
563c2c66affSColin Finck     while (parent && parent->namelen > 0) {
564c2c66affSColin Finck         len += parent->namelen + 1;
565c2c66affSColin Finck         parent = parent->parent;
566c2c66affSColin Finck     }
567c2c66affSColin Finck 
568c2c66affSColin Finck     return len;
569c2c66affSColin Finck }
570c2c66affSColin Finck 
find_path(char * path,send_dir * parent,char * name,ULONG namelen)571c2c66affSColin Finck static void find_path(char* path, send_dir* parent, char* name, ULONG namelen) {
572c2c66affSColin Finck     ULONG len = namelen;
573c2c66affSColin Finck 
574c2c66affSColin Finck     RtlCopyMemory(path, name, namelen);
575c2c66affSColin Finck 
576c2c66affSColin Finck     while (parent && parent->namelen > 0) {
577c2c66affSColin Finck         RtlMoveMemory(path + parent->namelen + 1, path, len);
578c2c66affSColin Finck         RtlCopyMemory(path, parent->name, parent->namelen);
579c2c66affSColin Finck         path[parent->namelen] = '/';
580c2c66affSColin Finck         len += parent->namelen + 1;
581c2c66affSColin Finck 
582c2c66affSColin Finck         parent = parent->parent;
583c2c66affSColin Finck     }
584c2c66affSColin Finck }
585c2c66affSColin Finck 
send_add_tlv_path(send_context * context,uint16_t type,send_dir * parent,char * name,uint16_t namelen)586318da0c1SPierre Schweitzer static void send_add_tlv_path(send_context* context, uint16_t type, send_dir* parent, char* name, uint16_t namelen) {
587318da0c1SPierre Schweitzer     uint16_t len = find_path_len(parent, namelen);
588c2c66affSColin Finck 
589c2c66affSColin Finck     send_add_tlv(context, type, NULL, len);
590c2c66affSColin Finck 
591c2c66affSColin Finck     if (len > 0)
592c2c66affSColin Finck         find_path((char*)&context->data[context->datalen - len], parent, name, namelen);
593c2c66affSColin Finck }
594c2c66affSColin Finck 
found_path(send_context * context,send_dir * parent,char * name,uint16_t namelen)595318da0c1SPierre Schweitzer static NTSTATUS found_path(send_context* context, send_dir* parent, char* name, uint16_t namelen) {
596c2c66affSColin Finck     ULONG pos = context->datalen;
597c2c66affSColin Finck 
598c2c66affSColin Finck     if (context->lastinode.o) {
599c2c66affSColin Finck         send_command(context, BTRFS_SEND_CMD_RENAME);
600c2c66affSColin Finck 
601318da0c1SPierre Schweitzer         send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, context->root_dir, context->lastinode.o->tmpname, (uint16_t)strlen(context->lastinode.o->tmpname));
602c2c66affSColin Finck 
603c2c66affSColin Finck         send_add_tlv_path(context, BTRFS_SEND_TLV_PATH_TO, parent, name, namelen);
604c2c66affSColin Finck 
605c2c66affSColin Finck         send_command_finish(context, pos);
606c2c66affSColin Finck     } else {
607c2c66affSColin Finck         send_command(context, BTRFS_SEND_CMD_LINK);
608c2c66affSColin Finck 
609c2c66affSColin Finck         send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, parent, name, namelen);
610c2c66affSColin Finck 
611318da0c1SPierre Schweitzer         send_add_tlv(context, BTRFS_SEND_TLV_PATH_LINK, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
612c2c66affSColin Finck 
613c2c66affSColin Finck         send_command_finish(context, pos);
614c2c66affSColin Finck     }
615c2c66affSColin Finck 
616c2c66affSColin Finck     if (context->lastinode.o) {
617318da0c1SPierre Schweitzer         uint16_t pathlen;
618c2c66affSColin Finck 
619c2c66affSColin Finck         if (context->lastinode.o->sd) {
620c2c66affSColin Finck             if (context->lastinode.o->sd->name)
621c2c66affSColin Finck                 ExFreePool(context->lastinode.o->sd->name);
622c2c66affSColin Finck 
623c2c66affSColin Finck             context->lastinode.o->sd->name = ExAllocatePoolWithTag(PagedPool, namelen, ALLOC_TAG);
624c2c66affSColin Finck             if (!context->lastinode.o->sd->name) {
625c2c66affSColin Finck                 ERR("out of memory\n");
626c2c66affSColin Finck                 return STATUS_INSUFFICIENT_RESOURCES;
627c2c66affSColin Finck             }
628c2c66affSColin Finck 
629c2c66affSColin Finck             RtlCopyMemory(context->lastinode.o->sd->name, name, namelen);
630c2c66affSColin Finck             context->lastinode.o->sd->namelen = namelen;
631c2c66affSColin Finck             context->lastinode.o->sd->parent = parent;
632c2c66affSColin Finck         }
633c2c66affSColin Finck 
634c2c66affSColin Finck         if (context->lastinode.path)
635c2c66affSColin Finck             ExFreePool(context->lastinode.path);
636c2c66affSColin Finck 
637c2c66affSColin Finck         pathlen = find_path_len(parent, namelen);
638c2c66affSColin Finck         context->lastinode.path = ExAllocatePoolWithTag(PagedPool, pathlen + 1, ALLOC_TAG);
639c2c66affSColin Finck         if (!context->lastinode.path) {
640c2c66affSColin Finck             ERR("out of memory\n");
641c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
642c2c66affSColin Finck         }
643c2c66affSColin Finck 
644c2c66affSColin Finck         find_path(context->lastinode.path, parent, name, namelen);
645c2c66affSColin Finck         context->lastinode.path[pathlen] = 0;
646c2c66affSColin Finck 
647c2c66affSColin Finck         RemoveEntryList(&context->lastinode.o->list_entry);
648c2c66affSColin Finck         ExFreePool(context->lastinode.o);
649c2c66affSColin Finck 
650c2c66affSColin Finck         context->lastinode.o = NULL;
651c2c66affSColin Finck     }
652c2c66affSColin Finck 
653c2c66affSColin Finck     return STATUS_SUCCESS;
654c2c66affSColin Finck }
655c2c66affSColin Finck 
send_utimes_command_dir(send_context * context,send_dir * sd,BTRFS_TIME * atime,BTRFS_TIME * mtime,BTRFS_TIME * ctime)656c2c66affSColin Finck static void send_utimes_command_dir(send_context* context, send_dir* sd, BTRFS_TIME* atime, BTRFS_TIME* mtime, BTRFS_TIME* ctime) {
657c2c66affSColin Finck     ULONG pos = context->datalen;
658c2c66affSColin Finck 
659c2c66affSColin Finck     send_command(context, BTRFS_SEND_CMD_UTIMES);
660c2c66affSColin Finck 
661c2c66affSColin Finck     send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, sd->parent, sd->name, sd->namelen);
662c2c66affSColin Finck 
663c2c66affSColin Finck     send_add_tlv(context, BTRFS_SEND_TLV_ATIME, atime, sizeof(BTRFS_TIME));
664c2c66affSColin Finck     send_add_tlv(context, BTRFS_SEND_TLV_MTIME, mtime, sizeof(BTRFS_TIME));
665c2c66affSColin Finck     send_add_tlv(context, BTRFS_SEND_TLV_CTIME, ctime, sizeof(BTRFS_TIME));
666c2c66affSColin Finck 
667c2c66affSColin Finck     send_command_finish(context, pos);
668c2c66affSColin Finck }
669c2c66affSColin Finck 
find_send_dir(send_context * context,uint64_t dir,uint64_t generation,send_dir ** psd,bool * added_dummy)670318da0c1SPierre Schweitzer static NTSTATUS find_send_dir(send_context* context, uint64_t dir, uint64_t generation, send_dir** psd, bool* added_dummy) {
671c2c66affSColin Finck     NTSTATUS Status;
672c2c66affSColin Finck     LIST_ENTRY* le;
673c2c66affSColin Finck     char name[64];
674c2c66affSColin Finck 
675c2c66affSColin Finck     le = context->dirs.Flink;
676c2c66affSColin Finck     while (le != &context->dirs) {
677c2c66affSColin Finck         send_dir* sd2 = CONTAINING_RECORD(le, send_dir, list_entry);
678c2c66affSColin Finck 
679c2c66affSColin Finck         if (sd2->inode > dir)
680c2c66affSColin Finck             break;
681c2c66affSColin Finck         else if (sd2->inode == dir) {
682c2c66affSColin Finck             *psd = sd2;
683c2c66affSColin Finck 
684c2c66affSColin Finck             if (added_dummy)
685318da0c1SPierre Schweitzer                 *added_dummy = false;
686c2c66affSColin Finck 
687c2c66affSColin Finck             return STATUS_SUCCESS;
688c2c66affSColin Finck         }
689c2c66affSColin Finck 
690c2c66affSColin Finck         le = le->Flink;
691c2c66affSColin Finck     }
692c2c66affSColin Finck 
693c2c66affSColin Finck     if (dir == SUBVOL_ROOT_INODE) {
694318da0c1SPierre Schweitzer         Status = send_add_dir(context, dir, NULL, NULL, 0, false, le, psd);
695c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
696194ea909SVictor Perevertkin             ERR("send_add_dir returned %08lx\n", Status);
697c2c66affSColin Finck             return Status;
698c2c66affSColin Finck         }
699c2c66affSColin Finck 
700c2c66affSColin Finck         if (added_dummy)
701318da0c1SPierre Schweitzer             *added_dummy = false;
702c2c66affSColin Finck 
703c2c66affSColin Finck         return STATUS_SUCCESS;
704c2c66affSColin Finck     }
705c2c66affSColin Finck 
706c2c66affSColin Finck     if (context->parent) {
707c2c66affSColin Finck         KEY searchkey;
708c2c66affSColin Finck         traverse_ptr tp;
709c2c66affSColin Finck 
710c2c66affSColin Finck         searchkey.obj_id = dir;
711c2c66affSColin Finck         searchkey.obj_type = TYPE_INODE_REF; // directories should never have an extiref
712c2c66affSColin Finck         searchkey.offset = 0xffffffffffffffff;
713c2c66affSColin Finck 
714318da0c1SPierre Schweitzer         Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL);
715c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
716194ea909SVictor Perevertkin             ERR("find_item returned %08lx\n", Status);
717c2c66affSColin Finck             return Status;
718c2c66affSColin Finck         }
719c2c66affSColin Finck 
720c2c66affSColin Finck         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
721c2c66affSColin Finck             INODE_REF* ir = (INODE_REF*)tp.item->data;
722c2c66affSColin Finck             send_dir* parent;
723c2c66affSColin Finck 
724c2c66affSColin Finck             if (tp.item->size < sizeof(INODE_REF) || tp.item->size < offsetof(INODE_REF, name[0]) + ir->n) {
725318da0c1SPierre Schweitzer                 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
726c2c66affSColin Finck                 return STATUS_INTERNAL_ERROR;
727c2c66affSColin Finck             }
728c2c66affSColin Finck 
729c2c66affSColin Finck             if (tp.item->key.offset == SUBVOL_ROOT_INODE)
730c2c66affSColin Finck                 parent = context->root_dir;
731c2c66affSColin Finck             else {
732c2c66affSColin Finck                 Status = find_send_dir(context, tp.item->key.offset, generation, &parent, NULL);
733c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
734194ea909SVictor Perevertkin                     ERR("find_send_dir returned %08lx\n", Status);
735c2c66affSColin Finck                     return Status;
736c2c66affSColin Finck                 }
737c2c66affSColin Finck             }
738c2c66affSColin Finck 
739318da0c1SPierre Schweitzer             Status = send_add_dir(context, dir, parent, ir->name, ir->n, true, NULL, psd);
740c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
741194ea909SVictor Perevertkin                 ERR("send_add_dir returned %08lx\n", Status);
742c2c66affSColin Finck                 return Status;
743c2c66affSColin Finck             }
744c2c66affSColin Finck 
745c2c66affSColin Finck             if (added_dummy)
746318da0c1SPierre Schweitzer                 *added_dummy = false;
747c2c66affSColin Finck 
748c2c66affSColin Finck             return STATUS_SUCCESS;
749c2c66affSColin Finck         }
750c2c66affSColin Finck     }
751c2c66affSColin Finck 
752c2c66affSColin Finck     Status = get_orphan_name(context, dir, generation, name);
753c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
754194ea909SVictor Perevertkin         ERR("get_orphan_name returned %08lx\n", Status);
755c2c66affSColin Finck         return Status;
756c2c66affSColin Finck     }
757c2c66affSColin Finck 
758318da0c1SPierre Schweitzer     Status = send_add_dir(context, dir, NULL, name, (uint16_t)strlen(name), true, le, psd);
759c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
760194ea909SVictor Perevertkin         ERR("send_add_dir returned %08lx\n", Status);
761c2c66affSColin Finck         return Status;
762c2c66affSColin Finck     }
763c2c66affSColin Finck 
764c2c66affSColin Finck     if (added_dummy)
765318da0c1SPierre Schweitzer         *added_dummy = true;
766c2c66affSColin Finck 
767c2c66affSColin Finck     return STATUS_SUCCESS;
768c2c66affSColin Finck }
769c2c66affSColin Finck 
send_inode_ref(send_context * context,traverse_ptr * tp,bool tree2)770318da0c1SPierre Schweitzer static NTSTATUS send_inode_ref(send_context* context, traverse_ptr* tp, bool tree2) {
771c2c66affSColin Finck     NTSTATUS Status;
772318da0c1SPierre Schweitzer     uint64_t inode = tp ? tp->item->key.obj_id : 0, dir = tp ? tp->item->key.offset : 0;
773c2c66affSColin Finck     LIST_ENTRY* le;
774c2c66affSColin Finck     INODE_REF* ir;
775318da0c1SPierre Schweitzer     uint16_t len;
776c2c66affSColin Finck     send_dir* sd = NULL;
777c2c66affSColin Finck     orphan* o2 = NULL;
778c2c66affSColin Finck 
779c2c66affSColin Finck     if (inode == dir) // root
780c2c66affSColin Finck         return STATUS_SUCCESS;
781c2c66affSColin Finck 
782c2c66affSColin Finck     if (tp->item->size < sizeof(INODE_REF)) {
783194ea909SVictor Perevertkin         ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
784c2c66affSColin Finck             tp->item->size, sizeof(INODE_REF));
785c2c66affSColin Finck         return STATUS_INTERNAL_ERROR;
786c2c66affSColin Finck     }
787c2c66affSColin Finck 
788c2c66affSColin Finck     if (dir != SUBVOL_ROOT_INODE) {
789318da0c1SPierre Schweitzer         bool added_dummy;
790c2c66affSColin Finck 
791c2c66affSColin Finck         Status = find_send_dir(context, dir, context->root->root_item.ctransid, &sd, &added_dummy);
792c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
793194ea909SVictor Perevertkin             ERR("find_send_dir returned %08lx\n", Status);
794c2c66affSColin Finck             return Status;
795c2c66affSColin Finck         }
796c2c66affSColin Finck 
797c2c66affSColin Finck         // directory has higher inode number than file, so might need to be created
798c2c66affSColin Finck         if (added_dummy) {
799318da0c1SPierre Schweitzer             bool found = false;
800c2c66affSColin Finck 
801c2c66affSColin Finck             le = context->orphans.Flink;
802c2c66affSColin Finck             while (le != &context->orphans) {
803c2c66affSColin Finck                 o2 = CONTAINING_RECORD(le, orphan, list_entry);
804c2c66affSColin Finck 
805c2c66affSColin Finck                 if (o2->inode == dir) {
806318da0c1SPierre Schweitzer                     found = true;
807c2c66affSColin Finck                     break;
808c2c66affSColin Finck                 } else if (o2->inode > dir)
809c2c66affSColin Finck                     break;
810c2c66affSColin Finck 
811c2c66affSColin Finck                 le = le->Flink;
812c2c66affSColin Finck             }
813c2c66affSColin Finck 
814c2c66affSColin Finck             if (!found) {
815c2c66affSColin Finck                 ULONG pos = context->datalen;
816c2c66affSColin Finck 
817c2c66affSColin Finck                 send_command(context, BTRFS_SEND_CMD_MKDIR);
818c2c66affSColin Finck 
819c2c66affSColin Finck                 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, NULL, sd->name, sd->namelen);
820c2c66affSColin Finck 
821318da0c1SPierre Schweitzer                 send_add_tlv(context, BTRFS_SEND_TLV_INODE, &dir, sizeof(uint64_t));
822c2c66affSColin Finck 
823c2c66affSColin Finck                 send_command_finish(context, pos);
824c2c66affSColin Finck 
825c2c66affSColin Finck                 o2 = ExAllocatePoolWithTag(PagedPool, sizeof(orphan), ALLOC_TAG);
826c2c66affSColin Finck                 if (!o2) {
827c2c66affSColin Finck                     ERR("out of memory\n");
828c2c66affSColin Finck                     return STATUS_INSUFFICIENT_RESOURCES;
829c2c66affSColin Finck                 }
830c2c66affSColin Finck 
831c2c66affSColin Finck                 o2->inode = dir;
832318da0c1SPierre Schweitzer                 o2->dir = true;
833c2c66affSColin Finck                 memcpy(o2->tmpname, sd->name, sd->namelen);
834c2c66affSColin Finck                 o2->tmpname[sd->namelen] = 0;
835c2c66affSColin Finck                 o2->sd = sd;
836c2c66affSColin Finck                 add_orphan(context, o2);
837c2c66affSColin Finck             }
838c2c66affSColin Finck         }
839c2c66affSColin Finck     } else
840c2c66affSColin Finck         sd = context->root_dir;
841c2c66affSColin Finck 
842c2c66affSColin Finck     len = tp->item->size;
843c2c66affSColin Finck     ir = (INODE_REF*)tp->item->data;
844c2c66affSColin Finck 
845c2c66affSColin Finck     while (len > 0) {
846c2c66affSColin Finck         ref* r;
847c2c66affSColin Finck 
848c2c66affSColin Finck         if (len < sizeof(INODE_REF) || len < offsetof(INODE_REF, name[0]) + ir->n) {
849318da0c1SPierre Schweitzer             ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
850c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
851c2c66affSColin Finck         }
852c2c66affSColin Finck 
853c2c66affSColin Finck         r = ExAllocatePoolWithTag(PagedPool, offsetof(ref, name[0]) + ir->n, ALLOC_TAG);
854c2c66affSColin Finck         if (!r) {
855c2c66affSColin Finck             ERR("out of memory\n");
856c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
857c2c66affSColin Finck         }
858c2c66affSColin Finck 
859c2c66affSColin Finck         r->sd = sd;
860c2c66affSColin Finck         r->namelen = ir->n;
861c2c66affSColin Finck         RtlCopyMemory(r->name, ir->name, ir->n);
862c2c66affSColin Finck 
863c2c66affSColin Finck         InsertTailList(tree2 ? &context->lastinode.oldrefs : &context->lastinode.refs, &r->list_entry);
864c2c66affSColin Finck 
865318da0c1SPierre Schweitzer         len -= (uint16_t)offsetof(INODE_REF, name[0]) + ir->n;
866c2c66affSColin Finck         ir = (INODE_REF*)&ir->name[ir->n];
867c2c66affSColin Finck     }
868c2c66affSColin Finck 
869c2c66affSColin Finck     return STATUS_SUCCESS;
870c2c66affSColin Finck }
871c2c66affSColin Finck 
send_inode_extref(send_context * context,traverse_ptr * tp,bool tree2)872318da0c1SPierre Schweitzer static NTSTATUS send_inode_extref(send_context* context, traverse_ptr* tp, bool tree2) {
873c2c66affSColin Finck     INODE_EXTREF* ier;
874318da0c1SPierre Schweitzer     uint16_t len;
875c2c66affSColin Finck 
876c2c66affSColin Finck     if (tp->item->size < sizeof(INODE_EXTREF)) {
877194ea909SVictor Perevertkin         ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
878c2c66affSColin Finck             tp->item->size, sizeof(INODE_EXTREF));
879c2c66affSColin Finck         return STATUS_INTERNAL_ERROR;
880c2c66affSColin Finck     }
881c2c66affSColin Finck 
882c2c66affSColin Finck     len = tp->item->size;
883c2c66affSColin Finck     ier = (INODE_EXTREF*)tp->item->data;
884c2c66affSColin Finck 
885c2c66affSColin Finck     while (len > 0) {
886c2c66affSColin Finck         NTSTATUS Status;
887c2c66affSColin Finck         send_dir* sd = NULL;
888c2c66affSColin Finck         orphan* o2 = NULL;
889c2c66affSColin Finck         ref* r;
890c2c66affSColin Finck 
891c2c66affSColin Finck         if (len < sizeof(INODE_EXTREF) || len < offsetof(INODE_EXTREF, name[0]) + ier->n) {
892318da0c1SPierre Schweitzer             ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
893c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
894c2c66affSColin Finck         }
895c2c66affSColin Finck 
896c2c66affSColin Finck         if (ier->dir != SUBVOL_ROOT_INODE) {
897c2c66affSColin Finck             LIST_ENTRY* le;
898318da0c1SPierre Schweitzer             bool added_dummy;
899c2c66affSColin Finck 
900c2c66affSColin Finck             Status = find_send_dir(context, ier->dir, context->root->root_item.ctransid, &sd, &added_dummy);
901c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
902194ea909SVictor Perevertkin                 ERR("find_send_dir returned %08lx\n", Status);
903c2c66affSColin Finck                 return Status;
904c2c66affSColin Finck             }
905c2c66affSColin Finck 
906c2c66affSColin Finck             // directory has higher inode number than file, so might need to be created
907c2c66affSColin Finck             if (added_dummy) {
908318da0c1SPierre Schweitzer                 bool found = false;
909c2c66affSColin Finck 
910c2c66affSColin Finck                 le = context->orphans.Flink;
911c2c66affSColin Finck                 while (le != &context->orphans) {
912c2c66affSColin Finck                     o2 = CONTAINING_RECORD(le, orphan, list_entry);
913c2c66affSColin Finck 
914c2c66affSColin Finck                     if (o2->inode == ier->dir) {
915318da0c1SPierre Schweitzer                         found = true;
916c2c66affSColin Finck                         break;
917c2c66affSColin Finck                     } else if (o2->inode > ier->dir)
918c2c66affSColin Finck                         break;
919c2c66affSColin Finck 
920c2c66affSColin Finck                     le = le->Flink;
921c2c66affSColin Finck                 }
922c2c66affSColin Finck 
923c2c66affSColin Finck                 if (!found) {
924c2c66affSColin Finck                     ULONG pos = context->datalen;
925c2c66affSColin Finck 
926c2c66affSColin Finck                     send_command(context, BTRFS_SEND_CMD_MKDIR);
927c2c66affSColin Finck 
928c2c66affSColin Finck                     send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, NULL, sd->name, sd->namelen);
929318da0c1SPierre Schweitzer                     send_add_tlv(context, BTRFS_SEND_TLV_INODE, &ier->dir, sizeof(uint64_t));
930c2c66affSColin Finck 
931c2c66affSColin Finck                     send_command_finish(context, pos);
932c2c66affSColin Finck 
933c2c66affSColin Finck                     o2 = ExAllocatePoolWithTag(PagedPool, sizeof(orphan), ALLOC_TAG);
934c2c66affSColin Finck                     if (!o2) {
935c2c66affSColin Finck                         ERR("out of memory\n");
936c2c66affSColin Finck                         return STATUS_INSUFFICIENT_RESOURCES;
937c2c66affSColin Finck                     }
938c2c66affSColin Finck 
939c2c66affSColin Finck                     o2->inode = ier->dir;
940318da0c1SPierre Schweitzer                     o2->dir = true;
941c2c66affSColin Finck                     memcpy(o2->tmpname, sd->name, sd->namelen);
942c2c66affSColin Finck                     o2->tmpname[sd->namelen] = 0;
943c2c66affSColin Finck                     o2->sd = sd;
944c2c66affSColin Finck                     add_orphan(context, o2);
945c2c66affSColin Finck                 }
946c2c66affSColin Finck             }
947c2c66affSColin Finck         } else
948c2c66affSColin Finck             sd = context->root_dir;
949c2c66affSColin Finck 
950c2c66affSColin Finck         r = ExAllocatePoolWithTag(PagedPool, offsetof(ref, name[0]) + ier->n, ALLOC_TAG);
951c2c66affSColin Finck         if (!r) {
952c2c66affSColin Finck             ERR("out of memory\n");
953c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
954c2c66affSColin Finck         }
955c2c66affSColin Finck 
956c2c66affSColin Finck         r->sd = sd;
957c2c66affSColin Finck         r->namelen = ier->n;
958c2c66affSColin Finck         RtlCopyMemory(r->name, ier->name, ier->n);
959c2c66affSColin Finck 
960c2c66affSColin Finck         InsertTailList(tree2 ? &context->lastinode.oldrefs : &context->lastinode.refs, &r->list_entry);
961c2c66affSColin Finck 
962318da0c1SPierre Schweitzer         len -= (uint16_t)offsetof(INODE_EXTREF, name[0]) + ier->n;
963c2c66affSColin Finck         ier = (INODE_EXTREF*)&ier->name[ier->n];
964c2c66affSColin Finck     }
965c2c66affSColin Finck 
966c2c66affSColin Finck     return STATUS_SUCCESS;
967c2c66affSColin Finck }
968c2c66affSColin Finck 
send_subvol_header(send_context * context,root * r,file_ref * fr)969c2c66affSColin Finck static void send_subvol_header(send_context* context, root* r, file_ref* fr) {
970c2c66affSColin Finck     ULONG pos = context->datalen;
971c2c66affSColin Finck 
972c2c66affSColin Finck     send_command(context, context->parent ? BTRFS_SEND_CMD_SNAPSHOT : BTRFS_SEND_CMD_SUBVOL);
973c2c66affSColin Finck 
974c2c66affSColin Finck     send_add_tlv(context, BTRFS_SEND_TLV_PATH, fr->dc->utf8.Buffer, fr->dc->utf8.Length);
975c2c66affSColin Finck 
976c2c66affSColin Finck     send_add_tlv(context, BTRFS_SEND_TLV_UUID, r->root_item.rtransid == 0 ? &r->root_item.uuid : &r->root_item.received_uuid, sizeof(BTRFS_UUID));
977318da0c1SPierre Schweitzer     send_add_tlv(context, BTRFS_SEND_TLV_TRANSID, &r->root_item.ctransid, sizeof(uint64_t));
978c2c66affSColin Finck 
979c2c66affSColin Finck     if (context->parent) {
980c2c66affSColin Finck         send_add_tlv(context, BTRFS_SEND_TLV_CLONE_UUID,
981c2c66affSColin Finck                      context->parent->root_item.rtransid == 0 ? &context->parent->root_item.uuid : &context->parent->root_item.received_uuid, sizeof(BTRFS_UUID));
982318da0c1SPierre Schweitzer         send_add_tlv(context, BTRFS_SEND_TLV_CLONE_CTRANSID, &context->parent->root_item.ctransid, sizeof(uint64_t));
983c2c66affSColin Finck     }
984c2c66affSColin Finck 
985c2c66affSColin Finck     send_command_finish(context, pos);
986c2c66affSColin Finck }
987c2c66affSColin Finck 
send_chown_command(send_context * context,char * path,uint64_t uid,uint64_t gid)988318da0c1SPierre Schweitzer static void send_chown_command(send_context* context, char* path, uint64_t uid, uint64_t gid) {
989c2c66affSColin Finck     ULONG pos = context->datalen;
990c2c66affSColin Finck 
991c2c66affSColin Finck     send_command(context, BTRFS_SEND_CMD_CHOWN);
992c2c66affSColin Finck 
993318da0c1SPierre Schweitzer     send_add_tlv(context, BTRFS_SEND_TLV_PATH, path, path ? (uint16_t)strlen(path) : 0);
994318da0c1SPierre Schweitzer     send_add_tlv(context, BTRFS_SEND_TLV_UID, &uid, sizeof(uint64_t));
995318da0c1SPierre Schweitzer     send_add_tlv(context, BTRFS_SEND_TLV_GID, &gid, sizeof(uint64_t));
996c2c66affSColin Finck 
997c2c66affSColin Finck     send_command_finish(context, pos);
998c2c66affSColin Finck }
999c2c66affSColin Finck 
send_chmod_command(send_context * context,char * path,uint64_t mode)1000318da0c1SPierre Schweitzer static void send_chmod_command(send_context* context, char* path, uint64_t mode) {
1001c2c66affSColin Finck     ULONG pos = context->datalen;
1002c2c66affSColin Finck 
1003c2c66affSColin Finck     send_command(context, BTRFS_SEND_CMD_CHMOD);
1004c2c66affSColin Finck 
1005c2c66affSColin Finck     mode &= 07777;
1006c2c66affSColin Finck 
1007318da0c1SPierre Schweitzer     send_add_tlv(context, BTRFS_SEND_TLV_PATH, path, path ? (uint16_t)strlen(path) : 0);
1008318da0c1SPierre Schweitzer     send_add_tlv(context, BTRFS_SEND_TLV_MODE, &mode, sizeof(uint64_t));
1009c2c66affSColin Finck 
1010c2c66affSColin Finck     send_command_finish(context, pos);
1011c2c66affSColin Finck }
1012c2c66affSColin Finck 
send_utimes_command(send_context * context,char * path,BTRFS_TIME * atime,BTRFS_TIME * mtime,BTRFS_TIME * ctime)1013c2c66affSColin Finck static void send_utimes_command(send_context* context, char* path, BTRFS_TIME* atime, BTRFS_TIME* mtime, BTRFS_TIME* ctime) {
1014c2c66affSColin Finck     ULONG pos = context->datalen;
1015c2c66affSColin Finck 
1016c2c66affSColin Finck     send_command(context, BTRFS_SEND_CMD_UTIMES);
1017c2c66affSColin Finck 
1018318da0c1SPierre Schweitzer     send_add_tlv(context, BTRFS_SEND_TLV_PATH, path, path ? (uint16_t)strlen(path) : 0);
1019c2c66affSColin Finck     send_add_tlv(context, BTRFS_SEND_TLV_ATIME, atime, sizeof(BTRFS_TIME));
1020c2c66affSColin Finck     send_add_tlv(context, BTRFS_SEND_TLV_MTIME, mtime, sizeof(BTRFS_TIME));
1021c2c66affSColin Finck     send_add_tlv(context, BTRFS_SEND_TLV_CTIME, ctime, sizeof(BTRFS_TIME));
1022c2c66affSColin Finck 
1023c2c66affSColin Finck     send_command_finish(context, pos);
1024c2c66affSColin Finck }
1025c2c66affSColin Finck 
send_truncate_command(send_context * context,char * path,uint64_t size)1026318da0c1SPierre Schweitzer static void send_truncate_command(send_context* context, char* path, uint64_t size) {
1027c2c66affSColin Finck     ULONG pos = context->datalen;
1028c2c66affSColin Finck 
1029c2c66affSColin Finck     send_command(context, BTRFS_SEND_CMD_TRUNCATE);
1030c2c66affSColin Finck 
1031318da0c1SPierre Schweitzer     send_add_tlv(context, BTRFS_SEND_TLV_PATH, path, path ? (uint16_t)strlen(path) : 0);
1032318da0c1SPierre Schweitzer     send_add_tlv(context, BTRFS_SEND_TLV_SIZE, &size, sizeof(uint64_t));
1033c2c66affSColin Finck 
1034c2c66affSColin Finck     send_command_finish(context, pos);
1035c2c66affSColin Finck }
1036c2c66affSColin Finck 
send_unlink_command(send_context * context,send_dir * parent,uint16_t namelen,char * name)1037318da0c1SPierre Schweitzer static NTSTATUS send_unlink_command(send_context* context, send_dir* parent, uint16_t namelen, char* name) {
1038c2c66affSColin Finck     ULONG pos = context->datalen;
1039318da0c1SPierre Schweitzer     uint16_t pathlen;
1040c2c66affSColin Finck 
1041c2c66affSColin Finck     send_command(context, BTRFS_SEND_CMD_UNLINK);
1042c2c66affSColin Finck 
1043c2c66affSColin Finck     pathlen = find_path_len(parent, namelen);
1044c2c66affSColin Finck     send_add_tlv(context, BTRFS_SEND_TLV_PATH, NULL, pathlen);
1045c2c66affSColin Finck 
1046c2c66affSColin Finck     find_path((char*)&context->data[context->datalen - pathlen], parent, name, namelen);
1047c2c66affSColin Finck 
1048c2c66affSColin Finck     send_command_finish(context, pos);
1049c2c66affSColin Finck 
1050c2c66affSColin Finck     return STATUS_SUCCESS;
1051c2c66affSColin Finck }
1052c2c66affSColin Finck 
send_rmdir_command(send_context * context,uint16_t pathlen,char * path)1053318da0c1SPierre Schweitzer static void send_rmdir_command(send_context* context, uint16_t pathlen, char* path) {
1054c2c66affSColin Finck     ULONG pos = context->datalen;
1055c2c66affSColin Finck 
1056c2c66affSColin Finck     send_command(context, BTRFS_SEND_CMD_RMDIR);
1057c2c66affSColin Finck     send_add_tlv(context, BTRFS_SEND_TLV_PATH, path, pathlen);
1058c2c66affSColin Finck     send_command_finish(context, pos);
1059c2c66affSColin Finck }
1060c2c66affSColin Finck 
get_dir_last_child(send_context * context,uint64_t * last_inode)1061318da0c1SPierre Schweitzer static NTSTATUS get_dir_last_child(send_context* context, uint64_t* last_inode) {
1062c2c66affSColin Finck     NTSTATUS Status;
1063c2c66affSColin Finck     KEY searchkey;
1064c2c66affSColin Finck     traverse_ptr tp;
1065c2c66affSColin Finck 
1066c2c66affSColin Finck     *last_inode = 0;
1067c2c66affSColin Finck 
1068c2c66affSColin Finck     searchkey.obj_id = context->lastinode.inode;
1069c2c66affSColin Finck     searchkey.obj_type = TYPE_DIR_INDEX;
1070c2c66affSColin Finck     searchkey.offset = 2;
1071c2c66affSColin Finck 
1072318da0c1SPierre Schweitzer     Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL);
1073c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
1074194ea909SVictor Perevertkin         ERR("find_item returned %08lx\n", Status);
1075c2c66affSColin Finck         return Status;
1076c2c66affSColin Finck     }
1077c2c66affSColin Finck 
1078c2c66affSColin Finck     do {
1079c2c66affSColin Finck         traverse_ptr next_tp;
1080c2c66affSColin Finck 
1081c2c66affSColin Finck         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
1082c2c66affSColin Finck             DIR_ITEM* di = (DIR_ITEM*)tp.item->data;
1083c2c66affSColin Finck 
1084c2c66affSColin Finck             if (tp.item->size < sizeof(DIR_ITEM) || tp.item->size < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
1085318da0c1SPierre Schweitzer                 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1086c2c66affSColin Finck                 return STATUS_INTERNAL_ERROR;
1087c2c66affSColin Finck             }
1088c2c66affSColin Finck 
1089c2c66affSColin Finck             if (di->key.obj_type == TYPE_INODE_ITEM)
1090c2c66affSColin Finck                 *last_inode = max(*last_inode, di->key.obj_id);
1091c2c66affSColin Finck         } else
1092c2c66affSColin Finck             break;
1093c2c66affSColin Finck 
1094318da0c1SPierre Schweitzer         if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL))
1095c2c66affSColin Finck             tp = next_tp;
1096c2c66affSColin Finck         else
1097c2c66affSColin Finck             break;
1098318da0c1SPierre Schweitzer     } while (true);
1099c2c66affSColin Finck 
1100c2c66affSColin Finck     return STATUS_SUCCESS;
1101c2c66affSColin Finck }
1102c2c66affSColin Finck 
add_pending_rmdir(send_context * context,uint64_t last_inode)1103318da0c1SPierre Schweitzer static NTSTATUS add_pending_rmdir(send_context* context, uint64_t last_inode) {
1104c2c66affSColin Finck     pending_rmdir* pr;
1105c2c66affSColin Finck     LIST_ENTRY* le;
1106c2c66affSColin Finck 
1107c2c66affSColin Finck     pr = ExAllocatePoolWithTag(PagedPool, sizeof(pending_rmdir), ALLOC_TAG);
1108c2c66affSColin Finck     if (!pr) {
1109c2c66affSColin Finck         ERR("out of memory\n");
1110c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
1111c2c66affSColin Finck     }
1112c2c66affSColin Finck 
1113c2c66affSColin Finck     pr->sd = context->lastinode.sd;
1114c2c66affSColin Finck     pr->last_child_inode = last_inode;
1115c2c66affSColin Finck 
1116c2c66affSColin Finck     le = context->pending_rmdirs.Flink;
1117c2c66affSColin Finck     while (le != &context->pending_rmdirs) {
1118c2c66affSColin Finck         pending_rmdir* pr2 = CONTAINING_RECORD(le, pending_rmdir, list_entry);
1119c2c66affSColin Finck 
1120c2c66affSColin Finck         if (pr2->last_child_inode > pr->last_child_inode) {
1121c2c66affSColin Finck             InsertHeadList(pr2->list_entry.Blink, &pr->list_entry);
1122c2c66affSColin Finck             return STATUS_SUCCESS;
1123c2c66affSColin Finck         }
1124c2c66affSColin Finck 
1125c2c66affSColin Finck         le = le->Flink;
1126c2c66affSColin Finck     }
1127c2c66affSColin Finck 
1128c2c66affSColin Finck     InsertTailList(&context->pending_rmdirs, &pr->list_entry);
1129c2c66affSColin Finck 
1130c2c66affSColin Finck     return STATUS_SUCCESS;
1131c2c66affSColin Finck }
1132c2c66affSColin Finck 
look_for_collision(send_context * context,send_dir * sd,char * name,ULONG namelen,uint64_t * inode,bool * dir)1133318da0c1SPierre Schweitzer static NTSTATUS look_for_collision(send_context* context, send_dir* sd, char* name, ULONG namelen, uint64_t* inode, bool* dir) {
1134c2c66affSColin Finck     NTSTATUS Status;
1135c2c66affSColin Finck     KEY searchkey;
1136c2c66affSColin Finck     traverse_ptr tp;
1137c2c66affSColin Finck     DIR_ITEM* di;
1138318da0c1SPierre Schweitzer     uint16_t len;
1139c2c66affSColin Finck 
1140c2c66affSColin Finck     searchkey.obj_id = sd->inode;
1141c2c66affSColin Finck     searchkey.obj_type = TYPE_DIR_ITEM;
1142318da0c1SPierre Schweitzer     searchkey.offset = calc_crc32c(0xfffffffe, (uint8_t*)name, namelen);
1143c2c66affSColin Finck 
1144318da0c1SPierre Schweitzer     Status = find_item(context->Vcb, context->parent, &tp, &searchkey, false, NULL);
1145c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
1146194ea909SVictor Perevertkin         ERR("find_item returned %08lx\n", Status);
1147c2c66affSColin Finck         return Status;
1148c2c66affSColin Finck     }
1149c2c66affSColin Finck 
1150c2c66affSColin Finck     if (keycmp(tp.item->key, searchkey))
1151c2c66affSColin Finck         return STATUS_SUCCESS;
1152c2c66affSColin Finck 
1153c2c66affSColin Finck     di = (DIR_ITEM*)tp.item->data;
1154c2c66affSColin Finck     len = tp.item->size;
1155c2c66affSColin Finck 
1156c2c66affSColin Finck     do {
1157c2c66affSColin Finck         if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
1158318da0c1SPierre Schweitzer             ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1159c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
1160c2c66affSColin Finck         }
1161c2c66affSColin Finck 
1162c2c66affSColin Finck         if (di->n == namelen && RtlCompareMemory(di->name, name, namelen) == namelen) {
1163c2c66affSColin Finck             *inode = di->key.obj_type == TYPE_INODE_ITEM ? di->key.obj_id : 0;
1164318da0c1SPierre Schweitzer             *dir = di->type == BTRFS_TYPE_DIRECTORY ? true: false;
1165c2c66affSColin Finck             return STATUS_OBJECT_NAME_COLLISION;
1166c2c66affSColin Finck         }
1167c2c66affSColin Finck 
1168c2c66affSColin Finck         di = (DIR_ITEM*)&di->name[di->m + di->n];
1169318da0c1SPierre Schweitzer         len -= (uint16_t)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
1170c2c66affSColin Finck     } while (len > 0);
1171c2c66affSColin Finck 
1172c2c66affSColin Finck     return STATUS_SUCCESS;
1173c2c66affSColin Finck }
1174c2c66affSColin Finck 
make_file_orphan(send_context * context,uint64_t inode,bool dir,uint64_t generation,ref * r)1175318da0c1SPierre Schweitzer static NTSTATUS make_file_orphan(send_context* context, uint64_t inode, bool dir, uint64_t generation, ref* r) {
1176c2c66affSColin Finck     NTSTATUS Status;
1177c2c66affSColin Finck     ULONG pos = context->datalen;
1178c2c66affSColin Finck     send_dir* sd = NULL;
1179c2c66affSColin Finck     orphan* o;
1180c2c66affSColin Finck     LIST_ENTRY* le;
1181c2c66affSColin Finck     char name[64];
1182c2c66affSColin Finck 
1183c2c66affSColin Finck     if (!dir) {
1184c2c66affSColin Finck         deleted_child* dc;
1185c2c66affSColin Finck 
1186c2c66affSColin Finck         dc = ExAllocatePoolWithTag(PagedPool, offsetof(deleted_child, name[0]) + r->namelen, ALLOC_TAG);
1187c2c66affSColin Finck         if (!dc) {
1188c2c66affSColin Finck             ERR("out of memory\n");
1189c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
1190c2c66affSColin Finck         }
1191c2c66affSColin Finck 
1192c2c66affSColin Finck         dc->namelen = r->namelen;
1193c2c66affSColin Finck         RtlCopyMemory(dc->name, r->name, r->namelen);
1194c2c66affSColin Finck         InsertTailList(&r->sd->deleted_children, &dc->list_entry);
1195c2c66affSColin Finck     }
1196c2c66affSColin Finck 
1197c2c66affSColin Finck     le = context->orphans.Flink;
1198c2c66affSColin Finck     while (le != &context->orphans) {
1199c2c66affSColin Finck         orphan* o2 = CONTAINING_RECORD(le, orphan, list_entry);
1200c2c66affSColin Finck 
1201c2c66affSColin Finck         if (o2->inode == inode) {
1202c2c66affSColin Finck             send_command(context, BTRFS_SEND_CMD_UNLINK);
1203c2c66affSColin Finck 
1204c2c66affSColin Finck             send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, r->sd, r->name, r->namelen);
1205c2c66affSColin Finck 
1206c2c66affSColin Finck             send_command_finish(context, pos);
1207c2c66affSColin Finck 
1208c2c66affSColin Finck             return STATUS_SUCCESS;
1209c2c66affSColin Finck         } else if (o2->inode > inode)
1210c2c66affSColin Finck             break;
1211c2c66affSColin Finck 
1212c2c66affSColin Finck         le = le->Flink;
1213c2c66affSColin Finck     }
1214c2c66affSColin Finck 
1215c2c66affSColin Finck     Status = get_orphan_name(context, inode, generation, name);
1216c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
1217194ea909SVictor Perevertkin         ERR("get_orphan_name returned %08lx\n", Status);
1218c2c66affSColin Finck         return Status;
1219c2c66affSColin Finck     }
1220c2c66affSColin Finck 
1221c2c66affSColin Finck     if (dir) {
1222c2c66affSColin Finck         Status = find_send_dir(context, inode, generation, &sd, NULL);
1223c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
1224194ea909SVictor Perevertkin             ERR("find_send_dir returned %08lx\n", Status);
1225c2c66affSColin Finck             return Status;
1226c2c66affSColin Finck         }
1227c2c66affSColin Finck 
1228318da0c1SPierre Schweitzer         sd->dummy = true;
1229c2c66affSColin Finck 
1230c2c66affSColin Finck         send_command(context, BTRFS_SEND_CMD_RENAME);
1231c2c66affSColin Finck 
1232c2c66affSColin Finck         send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, r->sd, r->name, r->namelen);
1233318da0c1SPierre Schweitzer         send_add_tlv_path(context, BTRFS_SEND_TLV_PATH_TO, context->root_dir, name, (uint16_t)strlen(name));
1234c2c66affSColin Finck 
1235c2c66affSColin Finck         send_command_finish(context, pos);
1236c2c66affSColin Finck 
1237c2c66affSColin Finck         if (sd->name)
1238c2c66affSColin Finck             ExFreePool(sd->name);
1239c2c66affSColin Finck 
1240318da0c1SPierre Schweitzer         sd->namelen = (uint16_t)strlen(name);
1241c2c66affSColin Finck         sd->name = ExAllocatePoolWithTag(PagedPool, sd->namelen, ALLOC_TAG);
1242c2c66affSColin Finck         if (!sd->name) {
1243c2c66affSColin Finck             ERR("out of memory\n");
1244c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
1245c2c66affSColin Finck         }
1246c2c66affSColin Finck 
1247c2c66affSColin Finck         RtlCopyMemory(sd->name, name, sd->namelen);
1248c2c66affSColin Finck         sd->parent = context->root_dir;
1249c2c66affSColin Finck     } else {
1250c2c66affSColin Finck         send_command(context, BTRFS_SEND_CMD_RENAME);
1251c2c66affSColin Finck 
1252c2c66affSColin Finck         send_add_tlv_path(context, BTRFS_SEND_TLV_PATH, r->sd, r->name, r->namelen);
1253c2c66affSColin Finck 
1254318da0c1SPierre Schweitzer         send_add_tlv_path(context, BTRFS_SEND_TLV_PATH_TO, context->root_dir, name, (uint16_t)strlen(name));
1255c2c66affSColin Finck 
1256c2c66affSColin Finck         send_command_finish(context, pos);
1257c2c66affSColin Finck     }
1258c2c66affSColin Finck 
1259c2c66affSColin Finck     o = ExAllocatePoolWithTag(PagedPool, sizeof(orphan), ALLOC_TAG);
1260c2c66affSColin Finck     if (!o) {
1261c2c66affSColin Finck         ERR("out of memory\n");
1262c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
1263c2c66affSColin Finck     }
1264c2c66affSColin Finck 
1265c2c66affSColin Finck     o->inode = inode;
1266318da0c1SPierre Schweitzer     o->dir = true;
1267c2c66affSColin Finck     strcpy(o->tmpname, name);
1268c2c66affSColin Finck     o->sd = sd;
1269c2c66affSColin Finck     add_orphan(context, o);
1270c2c66affSColin Finck 
1271c2c66affSColin Finck     return STATUS_SUCCESS;
1272c2c66affSColin Finck }
1273c2c66affSColin Finck 
flush_refs(send_context * context,traverse_ptr * tp1,traverse_ptr * tp2)1274c2c66affSColin Finck static NTSTATUS flush_refs(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2) {
1275c2c66affSColin Finck     NTSTATUS Status;
1276c2c66affSColin Finck     LIST_ENTRY* le;
1277c2c66affSColin Finck     ref *nameref = NULL, *nameref2 = NULL;
1278c2c66affSColin Finck 
1279c2c66affSColin Finck     if (context->lastinode.mode & __S_IFDIR) { // directory
1280c2c66affSColin Finck         ref* r = IsListEmpty(&context->lastinode.refs) ? NULL : CONTAINING_RECORD(context->lastinode.refs.Flink, ref, list_entry);
1281c2c66affSColin Finck         ref* or = IsListEmpty(&context->lastinode.oldrefs) ? NULL : CONTAINING_RECORD(context->lastinode.oldrefs.Flink, ref, list_entry);
1282c2c66affSColin Finck 
1283c2c66affSColin Finck         if (or && !context->lastinode.o) {
1284c2c66affSColin Finck             ULONG len = find_path_len(or->sd, or->namelen);
1285c2c66affSColin Finck 
1286c2c66affSColin Finck             context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG);
1287c2c66affSColin Finck             if (!context->lastinode.path) {
1288c2c66affSColin Finck                 ERR("out of memory\n");
1289c2c66affSColin Finck                 return STATUS_INSUFFICIENT_RESOURCES;
1290c2c66affSColin Finck             }
1291c2c66affSColin Finck 
1292c2c66affSColin Finck             find_path(context->lastinode.path, or->sd, or->name, or->namelen);
1293c2c66affSColin Finck             context->lastinode.path[len] = 0;
1294c2c66affSColin Finck 
1295c2c66affSColin Finck             if (!context->lastinode.sd) {
1296318da0c1SPierre Schweitzer                 Status = find_send_dir(context, context->lastinode.inode, context->lastinode.gen, &context->lastinode.sd, false);
1297c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
1298194ea909SVictor Perevertkin                     ERR("find_send_dir returned %08lx\n", Status);
1299c2c66affSColin Finck                     return Status;
1300c2c66affSColin Finck                 }
1301c2c66affSColin Finck             }
1302c2c66affSColin Finck         }
1303c2c66affSColin Finck 
1304c2c66affSColin Finck         if (r && or) {
1305318da0c1SPierre Schweitzer             uint64_t inode;
1306318da0c1SPierre Schweitzer             bool dir;
1307c2c66affSColin Finck 
1308c2c66affSColin Finck             Status = look_for_collision(context, r->sd, r->name, r->namelen, &inode, &dir);
1309c2c66affSColin Finck             if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION) {
1310194ea909SVictor Perevertkin                 ERR("look_for_collision returned %08lx\n", Status);
1311c2c66affSColin Finck                 return Status;
1312c2c66affSColin Finck             }
1313c2c66affSColin Finck 
1314c2c66affSColin Finck             if (Status == STATUS_OBJECT_NAME_COLLISION && inode > context->lastinode.inode) {
1315c2c66affSColin Finck                 Status = make_file_orphan(context, inode, dir, context->parent->root_item.ctransid, r);
1316c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
1317194ea909SVictor Perevertkin                     ERR("make_file_orphan returned %08lx\n", Status);
1318c2c66affSColin Finck                     return Status;
1319c2c66affSColin Finck                 }
1320c2c66affSColin Finck             }
1321c2c66affSColin Finck 
1322c2c66affSColin Finck             if (context->lastinode.o) {
1323c2c66affSColin Finck                 Status = found_path(context, r->sd, r->name, r->namelen);
1324c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
1325194ea909SVictor Perevertkin                     ERR("found_path returned %08lx\n", Status);
1326c2c66affSColin Finck                     return Status;
1327c2c66affSColin Finck                 }
1328c2c66affSColin Finck 
1329c2c66affSColin Finck                 if (!r->sd->dummy)
1330c2c66affSColin Finck                     send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime);
1331c2c66affSColin Finck             } else if (r->sd != or->sd || r->namelen != or->namelen || RtlCompareMemory(r->name, or->name, r->namelen) != r->namelen) { // moved or renamed
1332c2c66affSColin Finck                 ULONG pos = context->datalen, len;
1333c2c66affSColin Finck 
1334c2c66affSColin Finck                 send_command(context, BTRFS_SEND_CMD_RENAME);
1335c2c66affSColin Finck 
1336318da0c1SPierre Schweitzer                 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, (uint16_t)strlen(context->lastinode.path));
1337c2c66affSColin Finck 
1338c2c66affSColin Finck                 send_add_tlv_path(context, BTRFS_SEND_TLV_PATH_TO, r->sd, r->name, r->namelen);
1339c2c66affSColin Finck 
1340c2c66affSColin Finck                 send_command_finish(context, pos);
1341c2c66affSColin Finck 
1342c2c66affSColin Finck                 if (!r->sd->dummy)
1343c2c66affSColin Finck                     send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime);
1344c2c66affSColin Finck 
1345c2c66affSColin Finck                 if (context->lastinode.sd->name)
1346c2c66affSColin Finck                     ExFreePool(context->lastinode.sd->name);
1347c2c66affSColin Finck 
1348c2c66affSColin Finck                 context->lastinode.sd->name = ExAllocatePoolWithTag(PagedPool, r->namelen, ALLOC_TAG);
1349c2c66affSColin Finck                 if (!context->lastinode.sd->name) {
1350c2c66affSColin Finck                     ERR("out of memory\n");
1351c2c66affSColin Finck                     return STATUS_INSUFFICIENT_RESOURCES;
1352c2c66affSColin Finck                 }
1353c2c66affSColin Finck 
1354c2c66affSColin Finck                 RtlCopyMemory(context->lastinode.sd->name, r->name, r->namelen);
1355c2c66affSColin Finck                 context->lastinode.sd->parent = r->sd;
1356c2c66affSColin Finck 
1357c2c66affSColin Finck                 if (context->lastinode.path)
1358c2c66affSColin Finck                     ExFreePool(context->lastinode.path);
1359c2c66affSColin Finck 
1360c2c66affSColin Finck                 len = find_path_len(r->sd, r->namelen);
1361c2c66affSColin Finck                 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG);
1362c2c66affSColin Finck                 if (!context->lastinode.path) {
1363c2c66affSColin Finck                     ERR("out of memory\n");
1364c2c66affSColin Finck                     return STATUS_INSUFFICIENT_RESOURCES;
1365c2c66affSColin Finck                 }
1366c2c66affSColin Finck 
1367c2c66affSColin Finck                 find_path(context->lastinode.path, r->sd, r->name, r->namelen);
1368c2c66affSColin Finck                 context->lastinode.path[len] = 0;
1369c2c66affSColin Finck             }
1370c2c66affSColin Finck         } else if (r && !or) { // new
1371c2c66affSColin Finck             Status = found_path(context, r->sd, r->name, r->namelen);
1372c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
1373194ea909SVictor Perevertkin                 ERR("found_path returned %08lx\n", Status);
1374c2c66affSColin Finck                 return Status;
1375c2c66affSColin Finck             }
1376c2c66affSColin Finck 
1377c2c66affSColin Finck             if (!r->sd->dummy)
1378c2c66affSColin Finck                 send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime);
1379c2c66affSColin Finck         } else { // deleted
1380318da0c1SPierre Schweitzer             uint64_t last_inode;
1381c2c66affSColin Finck 
1382c2c66affSColin Finck             Status = get_dir_last_child(context, &last_inode);
1383c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
1384194ea909SVictor Perevertkin                 ERR("get_dir_last_child returned %08lx\n", Status);
1385c2c66affSColin Finck                 return Status;
1386c2c66affSColin Finck             }
1387c2c66affSColin Finck 
1388c2c66affSColin Finck             if (last_inode <= context->lastinode.inode) {
1389318da0c1SPierre Schweitzer                 send_rmdir_command(context, (uint16_t)strlen(context->lastinode.path), context->lastinode.path);
1390c2c66affSColin Finck 
1391c2c66affSColin Finck                 if (!or->sd->dummy)
1392c2c66affSColin Finck                     send_utimes_command_dir(context, or->sd, &or->sd->atime, &or->sd->mtime, &or->sd->ctime);
1393c2c66affSColin Finck             } else {
1394c2c66affSColin Finck                 char name[64];
1395c2c66affSColin Finck                 ULONG pos = context->datalen;
1396c2c66affSColin Finck 
1397c2c66affSColin Finck                 Status = get_orphan_name(context, context->lastinode.inode, context->lastinode.gen, name);
1398c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
1399194ea909SVictor Perevertkin                     ERR("get_orphan_name returned %08lx\n", Status);
1400c2c66affSColin Finck                     return Status;
1401c2c66affSColin Finck                 }
1402c2c66affSColin Finck 
1403c2c66affSColin Finck                 send_command(context, BTRFS_SEND_CMD_RENAME);
1404318da0c1SPierre Schweitzer                 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, (uint16_t)strlen(context->lastinode.path));
1405318da0c1SPierre Schweitzer                 send_add_tlv(context, BTRFS_SEND_TLV_PATH_TO, name, (uint16_t)strlen(name));
1406c2c66affSColin Finck                 send_command_finish(context, pos);
1407c2c66affSColin Finck 
1408c2c66affSColin Finck                 if (context->lastinode.sd->name)
1409c2c66affSColin Finck                     ExFreePool(context->lastinode.sd->name);
1410c2c66affSColin Finck 
1411c2c66affSColin Finck                 context->lastinode.sd->name = ExAllocatePoolWithTag(PagedPool, strlen(name), ALLOC_TAG);
1412c2c66affSColin Finck                 if (!context->lastinode.sd->name) {
1413c2c66affSColin Finck                     ERR("out of memory\n");
1414c2c66affSColin Finck                     return STATUS_INSUFFICIENT_RESOURCES;
1415c2c66affSColin Finck                 }
1416c2c66affSColin Finck 
1417c2c66affSColin Finck                 RtlCopyMemory(context->lastinode.sd->name, name, strlen(name));
1418318da0c1SPierre Schweitzer                 context->lastinode.sd->namelen = (uint16_t)strlen(name);
1419318da0c1SPierre Schweitzer                 context->lastinode.sd->dummy = true;
1420c2c66affSColin Finck                 context->lastinode.sd->parent = NULL;
1421c2c66affSColin Finck 
1422c2c66affSColin Finck                 send_utimes_command(context, NULL, &context->root_dir->atime, &context->root_dir->mtime, &context->root_dir->ctime);
1423c2c66affSColin Finck 
1424c2c66affSColin Finck                 Status = add_pending_rmdir(context, last_inode);
1425c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
1426194ea909SVictor Perevertkin                     ERR("add_pending_rmdir returned %08lx\n", Status);
1427c2c66affSColin Finck                     return Status;
1428c2c66affSColin Finck                 }
1429c2c66affSColin Finck             }
1430c2c66affSColin Finck         }
1431c2c66affSColin Finck 
1432c2c66affSColin Finck         while (!IsListEmpty(&context->lastinode.refs)) {
1433c2c66affSColin Finck             r = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.refs), ref, list_entry);
1434c2c66affSColin Finck             ExFreePool(r);
1435c2c66affSColin Finck         }
1436c2c66affSColin Finck 
1437c2c66affSColin Finck         while (!IsListEmpty(&context->lastinode.oldrefs)) {
1438c2c66affSColin Finck             or = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldrefs), ref, list_entry);
1439c2c66affSColin Finck             ExFreePool(or);
1440c2c66affSColin Finck         }
1441c2c66affSColin Finck 
1442c2c66affSColin Finck         return STATUS_SUCCESS;
1443c2c66affSColin Finck     } else {
1444c2c66affSColin Finck         if (!IsListEmpty(&context->lastinode.oldrefs)) {
1445c2c66affSColin Finck             ref* or = CONTAINING_RECORD(context->lastinode.oldrefs.Flink, ref, list_entry);
1446c2c66affSColin Finck             ULONG len = find_path_len(or->sd, or->namelen);
1447c2c66affSColin Finck 
1448c2c66affSColin Finck             context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG);
1449c2c66affSColin Finck             if (!context->lastinode.path) {
1450c2c66affSColin Finck                 ERR("out of memory\n");
1451c2c66affSColin Finck                 return STATUS_INSUFFICIENT_RESOURCES;
1452c2c66affSColin Finck             }
1453c2c66affSColin Finck 
1454c2c66affSColin Finck             find_path(context->lastinode.path, or->sd, or->name, or->namelen);
1455c2c66affSColin Finck             context->lastinode.path[len] = 0;
1456c2c66affSColin Finck             nameref = or;
1457c2c66affSColin Finck         }
1458c2c66affSColin Finck 
1459c2c66affSColin Finck         // remove unchanged refs
1460c2c66affSColin Finck         le = context->lastinode.oldrefs.Flink;
1461c2c66affSColin Finck         while (le != &context->lastinode.oldrefs) {
1462c2c66affSColin Finck             ref* or = CONTAINING_RECORD(le, ref, list_entry);
1463c2c66affSColin Finck             LIST_ENTRY* le2;
1464318da0c1SPierre Schweitzer             bool matched = false;
1465c2c66affSColin Finck 
1466c2c66affSColin Finck             le2 = context->lastinode.refs.Flink;
1467c2c66affSColin Finck             while (le2 != &context->lastinode.refs) {
1468c2c66affSColin Finck                 ref* r = CONTAINING_RECORD(le2, ref, list_entry);
1469c2c66affSColin Finck 
1470c2c66affSColin Finck                 if (r->sd == or->sd && r->namelen == or->namelen && RtlCompareMemory(r->name, or->name, r->namelen) == r->namelen) {
1471c2c66affSColin Finck                     RemoveEntryList(&r->list_entry);
1472c2c66affSColin Finck                     ExFreePool(r);
1473318da0c1SPierre Schweitzer                     matched = true;
1474c2c66affSColin Finck                     break;
1475c2c66affSColin Finck                 }
1476c2c66affSColin Finck 
1477c2c66affSColin Finck                 le2 = le2->Flink;
1478c2c66affSColin Finck             }
1479c2c66affSColin Finck 
1480c2c66affSColin Finck             if (matched) {
1481c2c66affSColin Finck                 le = le->Flink;
1482c2c66affSColin Finck                 RemoveEntryList(&or->list_entry);
1483c2c66affSColin Finck                 ExFreePool(or);
1484c2c66affSColin Finck                 continue;
1485c2c66affSColin Finck             }
1486c2c66affSColin Finck 
1487c2c66affSColin Finck             le = le->Flink;
1488c2c66affSColin Finck         }
1489c2c66affSColin Finck 
1490c2c66affSColin Finck         while (!IsListEmpty(&context->lastinode.refs)) {
1491c2c66affSColin Finck             ref* r = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.refs), ref, list_entry);
1492318da0c1SPierre Schweitzer             uint64_t inode;
1493318da0c1SPierre Schweitzer             bool dir;
1494c2c66affSColin Finck 
1495c2c66affSColin Finck             if (context->parent) {
1496c2c66affSColin Finck                 Status = look_for_collision(context, r->sd, r->name, r->namelen, &inode, &dir);
1497c2c66affSColin Finck                 if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION) {
1498194ea909SVictor Perevertkin                     ERR("look_for_collision returned %08lx\n", Status);
1499c2c66affSColin Finck                     return Status;
1500c2c66affSColin Finck                 }
1501c2c66affSColin Finck 
1502c2c66affSColin Finck                 if (Status == STATUS_OBJECT_NAME_COLLISION && inode > context->lastinode.inode) {
1503c2c66affSColin Finck                     Status = make_file_orphan(context, inode, dir, context->lastinode.gen, r);
1504c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
1505194ea909SVictor Perevertkin                         ERR("make_file_orphan returned %08lx\n", Status);
1506c2c66affSColin Finck                         return Status;
1507c2c66affSColin Finck                     }
1508c2c66affSColin Finck                 }
1509c2c66affSColin Finck             }
1510c2c66affSColin Finck 
1511c2c66affSColin Finck             if (context->datalen > SEND_BUFFER_LENGTH) {
1512c2c66affSColin Finck                 Status = wait_for_flush(context, tp1, tp2);
1513c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
1514194ea909SVictor Perevertkin                     ERR("wait_for_flush returned %08lx\n", Status);
1515c2c66affSColin Finck                     return Status;
1516c2c66affSColin Finck                 }
1517c2c66affSColin Finck 
1518c2c66affSColin Finck                 if (context->send->cancelling)
1519c2c66affSColin Finck                     return STATUS_SUCCESS;
1520c2c66affSColin Finck             }
1521c2c66affSColin Finck 
1522c2c66affSColin Finck             Status = found_path(context, r->sd, r->name, r->namelen);
1523c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
1524194ea909SVictor Perevertkin                 ERR("found_path returned %08lx\n", Status);
1525c2c66affSColin Finck                 return Status;
1526c2c66affSColin Finck             }
1527c2c66affSColin Finck 
1528c2c66affSColin Finck             if (!r->sd->dummy)
1529c2c66affSColin Finck                 send_utimes_command_dir(context, r->sd, &r->sd->atime, &r->sd->mtime, &r->sd->ctime);
1530c2c66affSColin Finck 
1531c2c66affSColin Finck             if (nameref && !nameref2)
1532c2c66affSColin Finck                 nameref2 = r;
1533c2c66affSColin Finck             else
1534c2c66affSColin Finck                 ExFreePool(r);
1535c2c66affSColin Finck         }
1536c2c66affSColin Finck 
1537c2c66affSColin Finck         while (!IsListEmpty(&context->lastinode.oldrefs)) {
1538c2c66affSColin Finck             ref* or = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldrefs), ref, list_entry);
1539318da0c1SPierre Schweitzer             bool deleted = false;
1540c2c66affSColin Finck 
1541c2c66affSColin Finck             le = or->sd->deleted_children.Flink;
1542c2c66affSColin Finck             while (le != &or->sd->deleted_children) {
1543c2c66affSColin Finck                 deleted_child* dc = CONTAINING_RECORD(le, deleted_child, list_entry);
1544c2c66affSColin Finck 
1545c2c66affSColin Finck                 if (dc->namelen == or->namelen && RtlCompareMemory(dc->name, or->name, or->namelen) == or->namelen) {
1546c2c66affSColin Finck                     RemoveEntryList(&dc->list_entry);
1547c2c66affSColin Finck                     ExFreePool(dc);
1548318da0c1SPierre Schweitzer                     deleted = true;
1549c2c66affSColin Finck                     break;
1550c2c66affSColin Finck                 }
1551c2c66affSColin Finck 
1552c2c66affSColin Finck                 le = le->Flink;
1553c2c66affSColin Finck             }
1554c2c66affSColin Finck 
1555c2c66affSColin Finck             if (!deleted) {
1556c2c66affSColin Finck                 if (context->datalen > SEND_BUFFER_LENGTH) {
1557c2c66affSColin Finck                     Status = wait_for_flush(context, tp1, tp2);
1558c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
1559194ea909SVictor Perevertkin                         ERR("wait_for_flush returned %08lx\n", Status);
1560c2c66affSColin Finck                         return Status;
1561c2c66affSColin Finck                     }
1562c2c66affSColin Finck 
1563c2c66affSColin Finck                     if (context->send->cancelling)
1564c2c66affSColin Finck                         return STATUS_SUCCESS;
1565c2c66affSColin Finck                 }
1566c2c66affSColin Finck 
1567c2c66affSColin Finck                 Status = send_unlink_command(context, or->sd, or->namelen, or->name);
1568c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
1569194ea909SVictor Perevertkin                     ERR("send_unlink_command returned %08lx\n", Status);
1570c2c66affSColin Finck                     return Status;
1571c2c66affSColin Finck                 }
1572c2c66affSColin Finck 
1573c2c66affSColin Finck                 if (!or->sd->dummy)
1574c2c66affSColin Finck                     send_utimes_command_dir(context, or->sd, &or->sd->atime, &or->sd->mtime, &or->sd->ctime);
1575c2c66affSColin Finck             }
1576c2c66affSColin Finck 
1577c2c66affSColin Finck             if (or == nameref && nameref2) {
1578318da0c1SPierre Schweitzer                 uint16_t len = find_path_len(nameref2->sd, nameref2->namelen);
1579c2c66affSColin Finck 
1580c2c66affSColin Finck                 if (context->lastinode.path)
1581c2c66affSColin Finck                     ExFreePool(context->lastinode.path);
1582c2c66affSColin Finck 
1583c2c66affSColin Finck                 context->lastinode.path = ExAllocatePoolWithTag(PagedPool, len + 1, ALLOC_TAG);
1584c2c66affSColin Finck                 if (!context->lastinode.path) {
1585c2c66affSColin Finck                     ERR("out of memory\n");
1586c2c66affSColin Finck                     return STATUS_INSUFFICIENT_RESOURCES;
1587c2c66affSColin Finck                 }
1588c2c66affSColin Finck 
1589c2c66affSColin Finck                 find_path(context->lastinode.path, nameref2->sd, nameref2->name, nameref2->namelen);
1590c2c66affSColin Finck                 context->lastinode.path[len] = 0;
1591c2c66affSColin Finck 
1592c2c66affSColin Finck                 ExFreePool(nameref2);
1593c2c66affSColin Finck             }
1594c2c66affSColin Finck 
1595c2c66affSColin Finck             ExFreePool(or);
1596c2c66affSColin Finck         }
1597c2c66affSColin Finck     }
1598c2c66affSColin Finck 
1599c2c66affSColin Finck     return STATUS_SUCCESS;
1600c2c66affSColin Finck }
1601c2c66affSColin Finck 
wait_for_flush(send_context * context,traverse_ptr * tp1,traverse_ptr * tp2)1602c2c66affSColin Finck static NTSTATUS wait_for_flush(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2) {
1603c2c66affSColin Finck     NTSTATUS Status;
1604c2c66affSColin Finck     KEY key1, key2;
1605c2c66affSColin Finck 
1606c2c66affSColin Finck     if (tp1)
1607c2c66affSColin Finck         key1 = tp1->item->key;
1608c2c66affSColin Finck 
1609c2c66affSColin Finck     if (tp2)
1610c2c66affSColin Finck         key2 = tp2->item->key;
1611c2c66affSColin Finck 
1612c2c66affSColin Finck     ExReleaseResourceLite(&context->Vcb->tree_lock);
1613c2c66affSColin Finck 
1614c2c66affSColin Finck     KeClearEvent(&context->send->cleared_event);
1615318da0c1SPierre Schweitzer     KeSetEvent(&context->buffer_event, 0, true);
1616318da0c1SPierre Schweitzer     KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, false, NULL);
1617c2c66affSColin Finck 
1618318da0c1SPierre Schweitzer     ExAcquireResourceSharedLite(&context->Vcb->tree_lock, true);
1619c2c66affSColin Finck 
1620c2c66affSColin Finck     if (context->send->cancelling)
1621c2c66affSColin Finck         return STATUS_SUCCESS;
1622c2c66affSColin Finck 
1623c2c66affSColin Finck     if (tp1) {
1624318da0c1SPierre Schweitzer         Status = find_item(context->Vcb, context->root, tp1, &key1, false, NULL);
1625c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
1626194ea909SVictor Perevertkin             ERR("find_item returned %08lx\n", Status);
1627c2c66affSColin Finck             return Status;
1628c2c66affSColin Finck         }
1629c2c66affSColin Finck 
1630c2c66affSColin Finck         if (keycmp(tp1->item->key, key1)) {
1631c2c66affSColin Finck             ERR("readonly subvolume changed\n");
1632c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
1633c2c66affSColin Finck         }
1634c2c66affSColin Finck     }
1635c2c66affSColin Finck 
1636c2c66affSColin Finck     if (tp2) {
1637318da0c1SPierre Schweitzer         Status = find_item(context->Vcb, context->parent, tp2, &key2, false, NULL);
1638c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
1639194ea909SVictor Perevertkin             ERR("find_item returned %08lx\n", Status);
1640c2c66affSColin Finck             return Status;
1641c2c66affSColin Finck         }
1642c2c66affSColin Finck 
1643c2c66affSColin Finck         if (keycmp(tp2->item->key, key2)) {
1644c2c66affSColin Finck             ERR("readonly subvolume changed\n");
1645c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
1646c2c66affSColin Finck         }
1647c2c66affSColin Finck     }
1648c2c66affSColin Finck 
1649c2c66affSColin Finck     return STATUS_SUCCESS;
1650c2c66affSColin Finck }
1651c2c66affSColin Finck 
add_ext_holes(device_extension * Vcb,LIST_ENTRY * exts,uint64_t size)1652318da0c1SPierre Schweitzer static NTSTATUS add_ext_holes(device_extension* Vcb, LIST_ENTRY* exts, uint64_t size) {
1653318da0c1SPierre Schweitzer     uint64_t lastoff = 0;
1654c2c66affSColin Finck     LIST_ENTRY* le;
1655c2c66affSColin Finck 
1656c2c66affSColin Finck     le = exts->Flink;
1657c2c66affSColin Finck     while (le != exts) {
1658c2c66affSColin Finck         send_ext* ext = CONTAINING_RECORD(le, send_ext, list_entry);
1659c2c66affSColin Finck 
1660c2c66affSColin Finck         if (ext->offset > lastoff) {
1661c2c66affSColin Finck             send_ext* ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data.data) + sizeof(EXTENT_DATA2), ALLOC_TAG);
1662c2c66affSColin Finck             EXTENT_DATA2* ed2;
1663c2c66affSColin Finck 
1664c2c66affSColin Finck             if (!ext2) {
1665c2c66affSColin Finck                 ERR("out of memory\n");
1666c2c66affSColin Finck                 return STATUS_INSUFFICIENT_RESOURCES;
1667c2c66affSColin Finck             }
1668c2c66affSColin Finck 
1669c2c66affSColin Finck             ed2 = (EXTENT_DATA2*)ext2->data.data;
1670c2c66affSColin Finck 
1671c2c66affSColin Finck             ext2->offset = lastoff;
1672c2c66affSColin Finck             ext2->datalen = offsetof(EXTENT_DATA, data) + sizeof(EXTENT_DATA2);
1673c2c66affSColin Finck             ext2->data.decoded_size = ed2->num_bytes = ext->offset - lastoff;
1674c2c66affSColin Finck             ext2->data.type = EXTENT_TYPE_REGULAR;
1675c2c66affSColin Finck             ed2->address = ed2->size = ed2->offset = 0;
1676c2c66affSColin Finck 
1677c2c66affSColin Finck             InsertHeadList(le->Blink, &ext2->list_entry);
1678c2c66affSColin Finck         }
1679c2c66affSColin Finck 
1680c2c66affSColin Finck         if (ext->data.type == EXTENT_TYPE_INLINE)
1681c2c66affSColin Finck             lastoff = ext->offset + ext->data.decoded_size;
1682c2c66affSColin Finck         else {
1683c2c66affSColin Finck             EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data.data;
1684c2c66affSColin Finck             lastoff = ext->offset + ed2->num_bytes;
1685c2c66affSColin Finck         }
1686c2c66affSColin Finck 
1687c2c66affSColin Finck         le = le->Flink;
1688c2c66affSColin Finck     }
1689c2c66affSColin Finck 
1690c2c66affSColin Finck     if (size > lastoff) {
1691c2c66affSColin Finck         send_ext* ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data.data) + sizeof(EXTENT_DATA2), ALLOC_TAG);
1692c2c66affSColin Finck         EXTENT_DATA2* ed2;
1693c2c66affSColin Finck 
1694c2c66affSColin Finck         if (!ext2) {
1695c2c66affSColin Finck             ERR("out of memory\n");
1696c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
1697c2c66affSColin Finck         }
1698c2c66affSColin Finck 
1699c2c66affSColin Finck         ed2 = (EXTENT_DATA2*)ext2->data.data;
1700c2c66affSColin Finck 
1701c2c66affSColin Finck         ext2->offset = lastoff;
1702c2c66affSColin Finck         ext2->datalen = offsetof(EXTENT_DATA, data) + sizeof(EXTENT_DATA2);
1703eb7fbc25SPierre Schweitzer         ext2->data.decoded_size = ed2->num_bytes = sector_align(size - lastoff, Vcb->superblock.sector_size);
1704c2c66affSColin Finck         ext2->data.type = EXTENT_TYPE_REGULAR;
1705c2c66affSColin Finck         ed2->address = ed2->size = ed2->offset = 0;
1706c2c66affSColin Finck 
1707c2c66affSColin Finck         InsertTailList(exts, &ext2->list_entry);
1708c2c66affSColin Finck     }
1709c2c66affSColin Finck 
1710c2c66affSColin Finck     return STATUS_SUCCESS;
1711c2c66affSColin Finck }
1712c2c66affSColin Finck 
divide_ext(send_ext * ext,uint64_t len,bool trunc)1713318da0c1SPierre Schweitzer static NTSTATUS divide_ext(send_ext* ext, uint64_t len, bool trunc) {
1714c2c66affSColin Finck     send_ext* ext2;
1715c2c66affSColin Finck     EXTENT_DATA2 *ed2a, *ed2b;
1716c2c66affSColin Finck 
1717c2c66affSColin Finck     if (ext->data.type == EXTENT_TYPE_INLINE) {
1718c2c66affSColin Finck         if (!trunc) {
1719c2c66affSColin Finck             ext2 = ExAllocatePoolWithTag(PagedPool, (ULONG)(offsetof(send_ext, data.data) + ext->data.decoded_size - len), ALLOC_TAG);
1720c2c66affSColin Finck 
1721c2c66affSColin Finck             if (!ext2) {
1722c2c66affSColin Finck                 ERR("out of memory\n");
1723c2c66affSColin Finck                 return STATUS_INSUFFICIENT_RESOURCES;
1724c2c66affSColin Finck             }
1725c2c66affSColin Finck 
1726c2c66affSColin Finck             ext2->offset = ext->offset + len;
1727c2c66affSColin Finck             ext2->datalen = (ULONG)(ext->data.decoded_size - len);
1728c2c66affSColin Finck             ext2->data.decoded_size = ext->data.decoded_size - len;
1729c2c66affSColin Finck             ext2->data.compression = ext->data.compression;
1730c2c66affSColin Finck             ext2->data.encryption = ext->data.encryption;
1731c2c66affSColin Finck             ext2->data.encoding = ext->data.encoding;
1732c2c66affSColin Finck             ext2->data.type = ext->data.type;
1733c2c66affSColin Finck             RtlCopyMemory(ext2->data.data, ext->data.data + len, (ULONG)(ext->data.decoded_size - len));
1734c2c66affSColin Finck 
1735c2c66affSColin Finck             InsertHeadList(&ext->list_entry, &ext2->list_entry);
1736c2c66affSColin Finck         }
1737c2c66affSColin Finck 
1738c2c66affSColin Finck         ext->data.decoded_size = len;
1739c2c66affSColin Finck 
1740c2c66affSColin Finck         return STATUS_SUCCESS;
1741c2c66affSColin Finck     }
1742c2c66affSColin Finck 
1743c2c66affSColin Finck     ed2a = (EXTENT_DATA2*)ext->data.data;
1744c2c66affSColin Finck 
1745c2c66affSColin Finck     if (!trunc) {
1746c2c66affSColin Finck         ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data.data) + sizeof(EXTENT_DATA2), ALLOC_TAG);
1747c2c66affSColin Finck 
1748c2c66affSColin Finck         if (!ext2) {
1749c2c66affSColin Finck             ERR("out of memory\n");
1750c2c66affSColin Finck             return STATUS_INSUFFICIENT_RESOURCES;
1751c2c66affSColin Finck         }
1752c2c66affSColin Finck 
1753c2c66affSColin Finck         ed2b = (EXTENT_DATA2*)ext2->data.data;
1754c2c66affSColin Finck 
1755c2c66affSColin Finck         ext2->offset = ext->offset + len;
1756c2c66affSColin Finck         ext2->datalen = offsetof(EXTENT_DATA, data) + sizeof(EXTENT_DATA2);
1757c2c66affSColin Finck 
1758c2c66affSColin Finck         ext2->data.compression = ext->data.compression;
1759c2c66affSColin Finck         ext2->data.encryption = ext->data.encryption;
1760c2c66affSColin Finck         ext2->data.encoding = ext->data.encoding;
1761c2c66affSColin Finck         ext2->data.type = ext->data.type;
1762c2c66affSColin Finck         ed2b->num_bytes = ed2a->num_bytes - len;
1763c2c66affSColin Finck 
1764c2c66affSColin Finck         if (ed2a->size == 0) {
1765c2c66affSColin Finck             ext2->data.decoded_size = ed2b->num_bytes;
1766c2c66affSColin Finck             ext->data.decoded_size = len;
1767c2c66affSColin Finck 
1768c2c66affSColin Finck             ed2b->address = ed2b->size = ed2b->offset = 0;
1769c2c66affSColin Finck         } else {
1770c2c66affSColin Finck             ext2->data.decoded_size = ext->data.decoded_size;
1771c2c66affSColin Finck 
1772c2c66affSColin Finck             ed2b->address = ed2a->address;
1773c2c66affSColin Finck             ed2b->size = ed2a->size;
1774c2c66affSColin Finck             ed2b->offset = ed2a->offset + len;
1775c2c66affSColin Finck         }
1776c2c66affSColin Finck 
1777c2c66affSColin Finck         InsertHeadList(&ext->list_entry, &ext2->list_entry);
1778c2c66affSColin Finck     }
1779c2c66affSColin Finck 
1780c2c66affSColin Finck     ed2a->num_bytes = len;
1781c2c66affSColin Finck 
1782c2c66affSColin Finck     return STATUS_SUCCESS;
1783c2c66affSColin Finck }
1784c2c66affSColin Finck 
sync_ext_cutoff_points(send_context * context)1785c2c66affSColin Finck static NTSTATUS sync_ext_cutoff_points(send_context* context) {
1786c2c66affSColin Finck     NTSTATUS Status;
1787c2c66affSColin Finck     send_ext *ext1, *ext2;
1788c2c66affSColin Finck 
1789c2c66affSColin Finck     ext1 = CONTAINING_RECORD(context->lastinode.exts.Flink, send_ext, list_entry);
1790c2c66affSColin Finck     ext2 = CONTAINING_RECORD(context->lastinode.oldexts.Flink, send_ext, list_entry);
1791c2c66affSColin Finck 
1792c2c66affSColin Finck     do {
1793318da0c1SPierre Schweitzer         uint64_t len1, len2;
1794c2c66affSColin Finck         EXTENT_DATA2 *ed2a, *ed2b;
1795c2c66affSColin Finck 
1796c2c66affSColin Finck         ed2a = ext1->data.type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ext1->data.data;
1797c2c66affSColin Finck         ed2b = ext2->data.type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ext2->data.data;
1798c2c66affSColin Finck 
1799c2c66affSColin Finck         len1 = ed2a ? ed2a->num_bytes : ext1->data.decoded_size;
1800c2c66affSColin Finck         len2 = ed2b ? ed2b->num_bytes : ext2->data.decoded_size;
1801c2c66affSColin Finck 
1802c2c66affSColin Finck         if (len1 < len2) {
1803318da0c1SPierre Schweitzer             Status = divide_ext(ext2, len1, false);
1804c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
1805194ea909SVictor Perevertkin                 ERR("divide_ext returned %08lx\n", Status);
1806c2c66affSColin Finck                 return Status;
1807c2c66affSColin Finck             }
1808c2c66affSColin Finck         } else if (len2 < len1) {
1809318da0c1SPierre Schweitzer             Status = divide_ext(ext1, len2, false);
1810c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
1811194ea909SVictor Perevertkin                 ERR("divide_ext returned %08lx\n", Status);
1812c2c66affSColin Finck                 return Status;
1813c2c66affSColin Finck             }
1814c2c66affSColin Finck         }
1815c2c66affSColin Finck 
1816c2c66affSColin Finck         if (ext1->list_entry.Flink == &context->lastinode.exts || ext2->list_entry.Flink == &context->lastinode.oldexts)
1817c2c66affSColin Finck             break;
1818c2c66affSColin Finck 
1819c2c66affSColin Finck         ext1 = CONTAINING_RECORD(ext1->list_entry.Flink, send_ext, list_entry);
1820c2c66affSColin Finck         ext2 = CONTAINING_RECORD(ext2->list_entry.Flink, send_ext, list_entry);
1821318da0c1SPierre Schweitzer     } while (true);
1822c2c66affSColin Finck 
1823c2c66affSColin Finck     ext1 = CONTAINING_RECORD(context->lastinode.exts.Blink, send_ext, list_entry);
1824c2c66affSColin Finck     ext2 = CONTAINING_RECORD(context->lastinode.oldexts.Blink, send_ext, list_entry);
1825c2c66affSColin Finck 
1826318da0c1SPierre Schweitzer     Status = divide_ext(ext1, context->lastinode.size - ext1->offset, true);
1827c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
1828194ea909SVictor Perevertkin         ERR("divide_ext returned %08lx\n", Status);
1829c2c66affSColin Finck         return Status;
1830c2c66affSColin Finck     }
1831c2c66affSColin Finck 
1832318da0c1SPierre Schweitzer     Status = divide_ext(ext2, context->lastinode.size - ext2->offset, true);
1833c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
1834194ea909SVictor Perevertkin         ERR("divide_ext returned %08lx\n", Status);
1835c2c66affSColin Finck         return Status;
1836c2c66affSColin Finck     }
1837c2c66affSColin Finck 
1838c2c66affSColin Finck     return STATUS_SUCCESS;
1839c2c66affSColin Finck }
1840c2c66affSColin Finck 
send_add_tlv_clone_path(send_context * context,root * r,uint64_t inode)1841318da0c1SPierre Schweitzer static bool send_add_tlv_clone_path(send_context* context, root* r, uint64_t inode) {
1842c2c66affSColin Finck     NTSTATUS Status;
1843c2c66affSColin Finck     KEY searchkey;
1844c2c66affSColin Finck     traverse_ptr tp;
1845318da0c1SPierre Schweitzer     uint16_t len = 0;
1846318da0c1SPierre Schweitzer     uint64_t num;
1847318da0c1SPierre Schweitzer     uint8_t* ptr;
1848c2c66affSColin Finck 
1849c2c66affSColin Finck     num = inode;
1850c2c66affSColin Finck 
1851c2c66affSColin Finck     while (num != SUBVOL_ROOT_INODE) {
1852c2c66affSColin Finck         searchkey.obj_id = num;
1853c2c66affSColin Finck         searchkey.obj_type = TYPE_INODE_EXTREF;
1854c2c66affSColin Finck         searchkey.offset = 0xffffffffffffffff;
1855c2c66affSColin Finck 
1856318da0c1SPierre Schweitzer         Status = find_item(context->Vcb, r, &tp, &searchkey, false, NULL);
1857c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
1858194ea909SVictor Perevertkin             ERR("find_item returned %08lx\n", Status);
1859318da0c1SPierre Schweitzer             return false;
1860c2c66affSColin Finck         }
1861c2c66affSColin Finck 
1862c2c66affSColin Finck         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)) {
1863318da0c1SPierre Schweitzer             ERR("could not find INODE_REF for inode %I64x\n", searchkey.obj_id);
1864318da0c1SPierre Schweitzer             return false;
1865c2c66affSColin Finck         }
1866c2c66affSColin Finck 
1867c2c66affSColin Finck         if (len > 0)
1868c2c66affSColin Finck             len++;
1869c2c66affSColin Finck 
1870c2c66affSColin Finck         if (tp.item->key.obj_type == TYPE_INODE_REF) {
1871c2c66affSColin Finck             INODE_REF* ir = (INODE_REF*)tp.item->data;
1872c2c66affSColin Finck 
1873c2c66affSColin Finck             if (tp.item->size < sizeof(INODE_REF) || tp.item->size < offsetof(INODE_REF, name[0]) + ir->n) {
1874318da0c1SPierre Schweitzer                 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1875318da0c1SPierre Schweitzer                 return false;
1876c2c66affSColin Finck             }
1877c2c66affSColin Finck 
1878c2c66affSColin Finck             len += ir->n;
1879c2c66affSColin Finck             num = tp.item->key.offset;
1880c2c66affSColin Finck         } else {
1881c2c66affSColin Finck             INODE_EXTREF* ier = (INODE_EXTREF*)tp.item->data;
1882c2c66affSColin Finck 
1883c2c66affSColin Finck             if (tp.item->size < sizeof(INODE_EXTREF) || tp.item->size < offsetof(INODE_EXTREF, name[0]) + ier->n) {
1884318da0c1SPierre Schweitzer                 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1885318da0c1SPierre Schweitzer                 return false;
1886c2c66affSColin Finck             }
1887c2c66affSColin Finck 
1888c2c66affSColin Finck             len += ier->n;
1889c2c66affSColin Finck             num = ier->dir;
1890c2c66affSColin Finck         }
1891c2c66affSColin Finck     }
1892c2c66affSColin Finck 
1893c2c66affSColin Finck     send_add_tlv(context, BTRFS_SEND_TLV_CLONE_PATH, NULL, len);
1894c2c66affSColin Finck     ptr = &context->data[context->datalen];
1895c2c66affSColin Finck 
1896c2c66affSColin Finck     num = inode;
1897c2c66affSColin Finck 
1898c2c66affSColin Finck     while (num != SUBVOL_ROOT_INODE) {
1899c2c66affSColin Finck         searchkey.obj_id = num;
1900c2c66affSColin Finck         searchkey.obj_type = TYPE_INODE_EXTREF;
1901c2c66affSColin Finck         searchkey.offset = 0xffffffffffffffff;
1902c2c66affSColin Finck 
1903318da0c1SPierre Schweitzer         Status = find_item(context->Vcb, r, &tp, &searchkey, false, NULL);
1904c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
1905194ea909SVictor Perevertkin             ERR("find_item returned %08lx\n", Status);
1906318da0c1SPierre Schweitzer             return false;
1907c2c66affSColin Finck         }
1908c2c66affSColin Finck 
1909c2c66affSColin Finck         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)) {
1910318da0c1SPierre Schweitzer             ERR("could not find INODE_REF for inode %I64x\n", searchkey.obj_id);
1911318da0c1SPierre Schweitzer             return false;
1912c2c66affSColin Finck         }
1913c2c66affSColin Finck 
1914c2c66affSColin Finck         if (num != inode) {
1915c2c66affSColin Finck             ptr--;
1916c2c66affSColin Finck             *ptr = '/';
1917c2c66affSColin Finck         }
1918c2c66affSColin Finck 
1919c2c66affSColin Finck         if (tp.item->key.obj_type == TYPE_INODE_REF) {
1920c2c66affSColin Finck             INODE_REF* ir = (INODE_REF*)tp.item->data;
1921c2c66affSColin Finck 
1922c2c66affSColin Finck             RtlCopyMemory(ptr - ir->n, ir->name, ir->n);
1923c2c66affSColin Finck             ptr -= ir->n;
1924c2c66affSColin Finck             num = tp.item->key.offset;
1925c2c66affSColin Finck         } else {
1926c2c66affSColin Finck             INODE_EXTREF* ier = (INODE_EXTREF*)tp.item->data;
1927c2c66affSColin Finck 
1928c2c66affSColin Finck             RtlCopyMemory(ptr - ier->n, ier->name, ier->n);
1929c2c66affSColin Finck             ptr -= ier->n;
1930c2c66affSColin Finck             num = ier->dir;
1931c2c66affSColin Finck         }
1932c2c66affSColin Finck     }
1933c2c66affSColin Finck 
1934318da0c1SPierre Schweitzer     return true;
1935c2c66affSColin Finck }
1936c2c66affSColin Finck 
try_clone_edr(send_context * context,send_ext * se,EXTENT_DATA_REF * edr)1937318da0c1SPierre Schweitzer static bool try_clone_edr(send_context* context, send_ext* se, EXTENT_DATA_REF* edr) {
1938c2c66affSColin Finck     NTSTATUS Status;
1939c2c66affSColin Finck     root* r = NULL;
1940c2c66affSColin Finck     KEY searchkey;
1941c2c66affSColin Finck     traverse_ptr tp;
1942c2c66affSColin Finck     EXTENT_DATA2* seed2 = (EXTENT_DATA2*)se->data.data;
1943c2c66affSColin Finck 
1944c2c66affSColin Finck     if (context->parent && edr->root == context->parent->id)
1945c2c66affSColin Finck         r = context->parent;
1946c2c66affSColin Finck 
1947c2c66affSColin Finck     if (!r && context->num_clones > 0) {
1948c2c66affSColin Finck         ULONG i;
1949c2c66affSColin Finck 
1950c2c66affSColin Finck         for (i = 0; i < context->num_clones; i++) {
1951c2c66affSColin Finck             if (context->clones[i]->id == edr->root && context->clones[i] != context->root) {
1952c2c66affSColin Finck                 r = context->clones[i];
1953c2c66affSColin Finck                 break;
1954c2c66affSColin Finck             }
1955c2c66affSColin Finck         }
1956c2c66affSColin Finck     }
1957c2c66affSColin Finck 
1958c2c66affSColin Finck     if (!r)
1959318da0c1SPierre Schweitzer         return false;
1960c2c66affSColin Finck 
1961c2c66affSColin Finck     searchkey.obj_id = edr->objid;
1962c2c66affSColin Finck     searchkey.obj_type = TYPE_EXTENT_DATA;
1963c2c66affSColin Finck     searchkey.offset = 0;
1964c2c66affSColin Finck 
1965318da0c1SPierre Schweitzer     Status = find_item(context->Vcb, r, &tp, &searchkey, false, NULL);
1966c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
1967194ea909SVictor Perevertkin         ERR("find_item returned %08lx\n", Status);
1968318da0c1SPierre Schweitzer         return false;
1969c2c66affSColin Finck     }
1970c2c66affSColin Finck 
1971318da0c1SPierre Schweitzer     while (true) {
1972c2c66affSColin Finck         traverse_ptr next_tp;
1973c2c66affSColin Finck 
1974c2c66affSColin Finck         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
1975c2c66affSColin Finck             if (tp.item->size < sizeof(EXTENT_DATA))
1976194ea909SVictor Perevertkin                 ERR("(%I64x,%x,%I64x) has size %u, not at least %Iu as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA));
1977c2c66affSColin Finck             else {
1978c2c66affSColin Finck                 EXTENT_DATA* ed = (EXTENT_DATA*)tp.item->data;
1979c2c66affSColin Finck 
1980c2c66affSColin Finck                 if (ed->type == EXTENT_TYPE_REGULAR) {
1981c2c66affSColin Finck                     if (tp.item->size < offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2))
1982194ea909SVictor Perevertkin                         ERR("(%I64x,%x,%I64x) has size %u, not %Iu as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
1983c2c66affSColin Finck                             tp.item->size, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2));
1984c2c66affSColin Finck                     else {
1985c2c66affSColin Finck                         EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
1986c2c66affSColin Finck 
1987c2c66affSColin Finck                         if (ed2->address == seed2->address && ed2->size == seed2->size && seed2->offset <= ed2->offset && seed2->offset + seed2->num_bytes >= ed2->offset + ed2->num_bytes) {
1988318da0c1SPierre Schweitzer                             uint64_t clone_offset = tp.item->key.offset + ed2->offset - seed2->offset;
1989318da0c1SPierre Schweitzer                             uint64_t clone_len = min(context->lastinode.size - se->offset, ed2->num_bytes);
1990c2c66affSColin Finck 
1991*174dfab6SVincent Franchomme                             if ((clone_offset & (context->Vcb->superblock.sector_size - 1)) == 0 && (clone_len & (context->Vcb->superblock.sector_size - 1)) == 0) {
1992c2c66affSColin Finck                                 ULONG pos = context->datalen;
1993c2c66affSColin Finck 
1994c2c66affSColin Finck                                 send_command(context, BTRFS_SEND_CMD_CLONE);
1995c2c66affSColin Finck 
1996318da0c1SPierre Schweitzer                                 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &se->offset, sizeof(uint64_t));
1997318da0c1SPierre Schweitzer                                 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_LENGTH, &clone_len, sizeof(uint64_t));
1998318da0c1SPierre Schweitzer                                 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
1999c2c66affSColin Finck                                 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_UUID, r->root_item.rtransid == 0 ? &r->root_item.uuid : &r->root_item.received_uuid, sizeof(BTRFS_UUID));
2000318da0c1SPierre Schweitzer                                 send_add_tlv(context, BTRFS_SEND_TLV_CLONE_CTRANSID, &r->root_item.ctransid, sizeof(uint64_t));
2001c2c66affSColin Finck 
2002c2c66affSColin Finck                                 if (!send_add_tlv_clone_path(context, r, tp.item->key.obj_id))
2003c2c66affSColin Finck                                     context->datalen = pos;
2004c2c66affSColin Finck                                 else {
2005318da0c1SPierre Schweitzer                                     send_add_tlv(context, BTRFS_SEND_TLV_CLONE_OFFSET, &clone_offset, sizeof(uint64_t));
2006c2c66affSColin Finck 
2007c2c66affSColin Finck                                     send_command_finish(context, pos);
2008c2c66affSColin Finck 
2009318da0c1SPierre Schweitzer                                     return true;
2010c2c66affSColin Finck                                 }
2011c2c66affSColin Finck                             }
2012c2c66affSColin Finck                         }
2013c2c66affSColin Finck                     }
2014c2c66affSColin Finck                 }
2015c2c66affSColin Finck             }
2016c2c66affSColin Finck         } 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))
2017c2c66affSColin Finck             break;
2018c2c66affSColin Finck 
2019318da0c1SPierre Schweitzer         if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL))
2020c2c66affSColin Finck             tp = next_tp;
2021c2c66affSColin Finck         else
2022c2c66affSColin Finck             break;
2023c2c66affSColin Finck     }
2024c2c66affSColin Finck 
2025318da0c1SPierre Schweitzer     return false;
2026c2c66affSColin Finck }
2027c2c66affSColin Finck 
try_clone(send_context * context,send_ext * se)2028318da0c1SPierre Schweitzer static bool try_clone(send_context* context, send_ext* se) {
2029c2c66affSColin Finck     NTSTATUS Status;
2030c2c66affSColin Finck     KEY searchkey;
2031c2c66affSColin Finck     traverse_ptr tp;
2032c2c66affSColin Finck     EXTENT_DATA2* ed2 = (EXTENT_DATA2*)se->data.data;
2033c2c66affSColin Finck     EXTENT_ITEM* ei;
2034318da0c1SPierre Schweitzer     uint64_t rc = 0;
2035c2c66affSColin Finck 
2036c2c66affSColin Finck     searchkey.obj_id = ed2->address;
2037c2c66affSColin Finck     searchkey.obj_type = TYPE_EXTENT_ITEM;
2038c2c66affSColin Finck     searchkey.offset = ed2->size;
2039c2c66affSColin Finck 
2040318da0c1SPierre Schweitzer     Status = find_item(context->Vcb, context->Vcb->extent_root, &tp, &searchkey, false, NULL);
2041c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
2042194ea909SVictor Perevertkin         ERR("find_item returned %08lx\n", Status);
2043318da0c1SPierre Schweitzer         return false;
2044c2c66affSColin Finck     }
2045c2c66affSColin Finck 
2046c2c66affSColin Finck     if (keycmp(tp.item->key, searchkey)) {
2047318da0c1SPierre Schweitzer         ERR("(%I64x,%x,%I64x) not found\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
2048318da0c1SPierre Schweitzer         return false;
2049c2c66affSColin Finck     }
2050c2c66affSColin Finck 
2051c2c66affSColin Finck     if (tp.item->size < sizeof(EXTENT_ITEM)) {
2052194ea909SVictor Perevertkin         ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
2053318da0c1SPierre Schweitzer         return false;
2054c2c66affSColin Finck     }
2055c2c66affSColin Finck 
2056c2c66affSColin Finck     ei = (EXTENT_ITEM*)tp.item->data;
2057c2c66affSColin Finck 
2058c2c66affSColin Finck     if (tp.item->size > sizeof(EXTENT_ITEM)) {
2059318da0c1SPierre Schweitzer         uint32_t len = tp.item->size - sizeof(EXTENT_ITEM);
2060318da0c1SPierre Schweitzer         uint8_t* ptr = (uint8_t*)&ei[1];
2061c2c66affSColin Finck 
2062c2c66affSColin Finck         while (len > 0) {
2063318da0c1SPierre Schweitzer             uint8_t secttype = *ptr;
2064c2c66affSColin Finck             ULONG sectlen = get_extent_data_len(secttype);
2065318da0c1SPierre Schweitzer             uint64_t sectcount = get_extent_data_refcount(secttype, ptr + sizeof(uint8_t));
2066c2c66affSColin Finck 
2067c2c66affSColin Finck             len--;
2068c2c66affSColin Finck 
2069c2c66affSColin Finck             if (sectlen > len) {
2070194ea909SVictor Perevertkin                 ERR("(%I64x,%x,%I64x): %x bytes left, expecting at least %lx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, len, sectlen);
2071318da0c1SPierre Schweitzer                 return false;
2072c2c66affSColin Finck             }
2073c2c66affSColin Finck 
2074c2c66affSColin Finck             if (sectlen == 0) {
2075318da0c1SPierre Schweitzer                 ERR("(%I64x,%x,%I64x): unrecognized extent type %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, secttype);
2076318da0c1SPierre Schweitzer                 return false;
2077c2c66affSColin Finck             }
2078c2c66affSColin Finck 
2079c2c66affSColin Finck             rc += sectcount;
2080c2c66affSColin Finck 
2081c2c66affSColin Finck             if (secttype == TYPE_EXTENT_DATA_REF) {
2082318da0c1SPierre Schweitzer                 EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)(ptr + sizeof(uint8_t));
2083c2c66affSColin Finck 
2084c2c66affSColin Finck                 if (try_clone_edr(context, se, sectedr))
2085318da0c1SPierre Schweitzer                     return true;
2086c2c66affSColin Finck             }
2087c2c66affSColin Finck 
2088c2c66affSColin Finck             len -= sectlen;
2089318da0c1SPierre Schweitzer             ptr += sizeof(uint8_t) + sectlen;
2090c2c66affSColin Finck         }
2091c2c66affSColin Finck     }
2092c2c66affSColin Finck 
2093c2c66affSColin Finck     if (rc >= ei->refcount)
2094318da0c1SPierre Schweitzer         return false;
2095c2c66affSColin Finck 
2096c2c66affSColin Finck     searchkey.obj_type = TYPE_EXTENT_DATA_REF;
2097c2c66affSColin Finck     searchkey.offset = 0;
2098c2c66affSColin Finck 
2099318da0c1SPierre Schweitzer     Status = find_item(context->Vcb, context->Vcb->extent_root, &tp, &searchkey, false, NULL);
2100c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
2101194ea909SVictor Perevertkin         ERR("find_item returned %08lx\n", Status);
2102318da0c1SPierre Schweitzer         return false;
2103c2c66affSColin Finck     }
2104c2c66affSColin Finck 
2105318da0c1SPierre Schweitzer     while (true) {
2106c2c66affSColin Finck         traverse_ptr next_tp;
2107c2c66affSColin Finck 
2108c2c66affSColin Finck         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
2109c2c66affSColin Finck             if (tp.item->size < sizeof(EXTENT_DATA_REF))
2110194ea909SVictor Perevertkin                 ERR("(%I64x,%x,%I64x) has size %u, not %Iu as expected\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA_REF));
2111c2c66affSColin Finck             else {
2112c2c66affSColin Finck                 if (try_clone_edr(context, se, (EXTENT_DATA_REF*)tp.item->data))
2113318da0c1SPierre Schweitzer                     return true;
2114c2c66affSColin Finck             }
2115c2c66affSColin Finck         } 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))
2116c2c66affSColin Finck             break;
2117c2c66affSColin Finck 
2118318da0c1SPierre Schweitzer         if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL))
2119c2c66affSColin Finck             tp = next_tp;
2120c2c66affSColin Finck         else
2121c2c66affSColin Finck             break;
2122c2c66affSColin Finck     }
2123c2c66affSColin Finck 
2124318da0c1SPierre Schweitzer     return false;
2125c2c66affSColin Finck }
2126c2c66affSColin Finck 
flush_extents(send_context * context,traverse_ptr * tp1,traverse_ptr * tp2)2127c2c66affSColin Finck static NTSTATUS flush_extents(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2) {
2128c2c66affSColin Finck     NTSTATUS Status;
2129c2c66affSColin Finck 
2130c2c66affSColin Finck     if ((IsListEmpty(&context->lastinode.exts) && IsListEmpty(&context->lastinode.oldexts)) || context->lastinode.size == 0)
2131c2c66affSColin Finck         return STATUS_SUCCESS;
2132c2c66affSColin Finck 
2133c2c66affSColin Finck     if (context->parent) {
2134eb7fbc25SPierre Schweitzer         Status = add_ext_holes(context->Vcb, &context->lastinode.exts, context->lastinode.size);
2135c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
2136194ea909SVictor Perevertkin             ERR("add_ext_holes returned %08lx\n", Status);
2137c2c66affSColin Finck             return Status;
2138c2c66affSColin Finck         }
2139c2c66affSColin Finck 
2140eb7fbc25SPierre Schweitzer         Status = add_ext_holes(context->Vcb, &context->lastinode.oldexts, context->lastinode.size);
2141c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
2142194ea909SVictor Perevertkin             ERR("add_ext_holes returned %08lx\n", Status);
2143c2c66affSColin Finck             return Status;
2144c2c66affSColin Finck         }
2145c2c66affSColin Finck 
2146c2c66affSColin Finck         Status = sync_ext_cutoff_points(context);
2147c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
2148194ea909SVictor Perevertkin             ERR("sync_ext_cutoff_points returned %08lx\n", Status);
2149c2c66affSColin Finck             return Status;
2150c2c66affSColin Finck         }
2151c2c66affSColin Finck     }
2152c2c66affSColin Finck 
2153c2c66affSColin Finck     while (!IsListEmpty(&context->lastinode.exts)) {
2154c2c66affSColin Finck         send_ext* se = CONTAINING_RECORD(RemoveHeadList(&context->lastinode.exts), send_ext, list_entry);
2155c2c66affSColin Finck         send_ext* se2 = context->parent ? CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldexts), send_ext, list_entry) : NULL;
2156c2c66affSColin Finck         ULONG pos;
2157c2c66affSColin Finck         EXTENT_DATA2* ed2;
2158c2c66affSColin Finck 
2159c2c66affSColin Finck         if (se2) {
2160c2c66affSColin Finck             if (se->data.type == EXTENT_TYPE_INLINE && se2->data.type == EXTENT_TYPE_INLINE &&
2161c2c66affSColin Finck                 RtlCompareMemory(se->data.data, se2->data.data, (ULONG)se->data.decoded_size) == (ULONG)se->data.decoded_size) {
2162c2c66affSColin Finck                 ExFreePool(se);
2163c2c66affSColin Finck                 ExFreePool(se2);
2164c2c66affSColin Finck                 continue;
2165c2c66affSColin Finck             }
2166c2c66affSColin Finck 
2167c2c66affSColin Finck             if (se->data.type == EXTENT_TYPE_REGULAR && se2->data.type == EXTENT_TYPE_REGULAR) {
2168c2c66affSColin Finck                 EXTENT_DATA2 *ed2a, *ed2b;
2169c2c66affSColin Finck 
2170c2c66affSColin Finck                 ed2a = (EXTENT_DATA2*)se->data.data;
2171c2c66affSColin Finck                 ed2b = (EXTENT_DATA2*)se2->data.data;
2172c2c66affSColin Finck 
2173c2c66affSColin Finck                 if (RtlCompareMemory(ed2a, ed2b, sizeof(EXTENT_DATA2)) == sizeof(EXTENT_DATA2)) {
2174c2c66affSColin Finck                     ExFreePool(se);
2175c2c66affSColin Finck                     ExFreePool(se2);
2176c2c66affSColin Finck                     continue;
2177c2c66affSColin Finck                 }
2178c2c66affSColin Finck             }
2179c2c66affSColin Finck         }
2180c2c66affSColin Finck 
2181c2c66affSColin Finck         if (se->data.type == EXTENT_TYPE_INLINE) {
2182c2c66affSColin Finck             pos = context->datalen;
2183c2c66affSColin Finck 
2184c2c66affSColin Finck             send_command(context, BTRFS_SEND_CMD_WRITE);
2185c2c66affSColin Finck 
2186318da0c1SPierre Schweitzer             send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2187318da0c1SPierre Schweitzer             send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &se->offset, sizeof(uint64_t));
2188c2c66affSColin Finck 
2189c2c66affSColin Finck             if (se->data.compression == BTRFS_COMPRESSION_NONE)
2190318da0c1SPierre Schweitzer                 send_add_tlv(context, BTRFS_SEND_TLV_DATA, se->data.data, (uint16_t)se->data.decoded_size);
2191eb7fbc25SPierre Schweitzer             else if (se->data.compression == BTRFS_COMPRESSION_ZLIB || se->data.compression == BTRFS_COMPRESSION_LZO || se->data.compression == BTRFS_COMPRESSION_ZSTD) {
2192c2c66affSColin Finck                 ULONG inlen = se->datalen - (ULONG)offsetof(EXTENT_DATA, data[0]);
2193c2c66affSColin Finck 
2194318da0c1SPierre Schweitzer                 send_add_tlv(context, BTRFS_SEND_TLV_DATA, NULL, (uint16_t)se->data.decoded_size);
2195c2c66affSColin Finck                 RtlZeroMemory(&context->data[context->datalen - se->data.decoded_size], (ULONG)se->data.decoded_size);
2196c2c66affSColin Finck 
2197c2c66affSColin Finck                 if (se->data.compression == BTRFS_COMPRESSION_ZLIB) {
2198318da0c1SPierre Schweitzer                     Status = zlib_decompress(se->data.data, inlen, &context->data[context->datalen - se->data.decoded_size], (uint32_t)se->data.decoded_size);
2199c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
2200194ea909SVictor Perevertkin                         ERR("zlib_decompress returned %08lx\n", Status);
2201c2c66affSColin Finck                         ExFreePool(se);
2202c2c66affSColin Finck                         if (se2) ExFreePool(se2);
2203c2c66affSColin Finck                         return Status;
2204c2c66affSColin Finck                     }
2205c2c66affSColin Finck                 } else if (se->data.compression == BTRFS_COMPRESSION_LZO) {
2206318da0c1SPierre Schweitzer                     if (inlen < sizeof(uint32_t)) {
2207c2c66affSColin Finck                         ERR("extent data was truncated\n");
2208c2c66affSColin Finck                         ExFreePool(se);
2209c2c66affSColin Finck                         if (se2) ExFreePool(se2);
2210c2c66affSColin Finck                         return STATUS_INTERNAL_ERROR;
2211c2c66affSColin Finck                     } else
2212318da0c1SPierre Schweitzer                         inlen -= sizeof(uint32_t);
2213c2c66affSColin Finck 
2214318da0c1SPierre Schweitzer                     Status = lzo_decompress(se->data.data + sizeof(uint32_t), inlen, &context->data[context->datalen - se->data.decoded_size], (uint32_t)se->data.decoded_size, sizeof(uint32_t));
2215c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
2216194ea909SVictor Perevertkin                         ERR("lzo_decompress returned %08lx\n", Status);
2217c2c66affSColin Finck                         ExFreePool(se);
2218c2c66affSColin Finck                         if (se2) ExFreePool(se2);
2219c2c66affSColin Finck                         return Status;
2220c2c66affSColin Finck                     }
2221eb7fbc25SPierre Schweitzer                 } else if (se->data.compression == BTRFS_COMPRESSION_ZSTD) {
2222318da0c1SPierre Schweitzer                     Status = zstd_decompress(se->data.data, inlen, &context->data[context->datalen - se->data.decoded_size], (uint32_t)se->data.decoded_size);
2223eb7fbc25SPierre Schweitzer                     if (!NT_SUCCESS(Status)) {
2224194ea909SVictor Perevertkin                         ERR("zlib_decompress returned %08lx\n", Status);
2225eb7fbc25SPierre Schweitzer                         ExFreePool(se);
2226eb7fbc25SPierre Schweitzer                         if (se2) ExFreePool(se2);
2227eb7fbc25SPierre Schweitzer                         return Status;
2228eb7fbc25SPierre Schweitzer                     }
2229c2c66affSColin Finck                 }
2230c2c66affSColin Finck             } else {
2231c2c66affSColin Finck                 ERR("unhandled compression type %x\n", se->data.compression);
2232c2c66affSColin Finck                 ExFreePool(se);
2233c2c66affSColin Finck                 if (se2) ExFreePool(se2);
2234c2c66affSColin Finck                 return STATUS_NOT_IMPLEMENTED;
2235c2c66affSColin Finck             }
2236c2c66affSColin Finck 
2237c2c66affSColin Finck             send_command_finish(context, pos);
2238c2c66affSColin Finck 
2239c2c66affSColin Finck             ExFreePool(se);
2240c2c66affSColin Finck             if (se2) ExFreePool(se2);
2241c2c66affSColin Finck             continue;
2242c2c66affSColin Finck         }
2243c2c66affSColin Finck 
2244c2c66affSColin Finck         ed2 = (EXTENT_DATA2*)se->data.data;
2245c2c66affSColin Finck 
2246c2c66affSColin Finck         if (ed2->size != 0 && (context->parent || context->num_clones > 0)) {
2247c2c66affSColin Finck             if (try_clone(context, se)) {
2248c2c66affSColin Finck                 ExFreePool(se);
2249c2c66affSColin Finck                 if (se2) ExFreePool(se2);
2250c2c66affSColin Finck                 continue;
2251c2c66affSColin Finck             }
2252c2c66affSColin Finck         }
2253c2c66affSColin Finck 
2254c2c66affSColin Finck         if (ed2->size == 0) { // write sparse
2255318da0c1SPierre Schweitzer             uint64_t off, offset;
2256c2c66affSColin Finck 
2257c2c66affSColin Finck             for (off = ed2->offset; off < ed2->offset + ed2->num_bytes; off += MAX_SEND_WRITE) {
2258318da0c1SPierre Schweitzer                 uint16_t length = (uint16_t)min(min(ed2->offset + ed2->num_bytes - off, MAX_SEND_WRITE), context->lastinode.size - se->offset - off);
2259c2c66affSColin Finck 
2260c2c66affSColin Finck                 if (context->datalen > SEND_BUFFER_LENGTH) {
2261c2c66affSColin Finck                     Status = wait_for_flush(context, tp1, tp2);
2262c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
2263194ea909SVictor Perevertkin                         ERR("wait_for_flush returned %08lx\n", Status);
2264c2c66affSColin Finck                         ExFreePool(se);
2265c2c66affSColin Finck                         if (se2) ExFreePool(se2);
2266c2c66affSColin Finck                         return Status;
2267c2c66affSColin Finck                     }
2268c2c66affSColin Finck 
2269c2c66affSColin Finck                     if (context->send->cancelling) {
2270c2c66affSColin Finck                         ExFreePool(se);
2271c2c66affSColin Finck                         if (se2) ExFreePool(se2);
2272c2c66affSColin Finck                         return STATUS_SUCCESS;
2273c2c66affSColin Finck                     }
2274c2c66affSColin Finck                 }
2275c2c66affSColin Finck 
2276c2c66affSColin Finck                 pos = context->datalen;
2277c2c66affSColin Finck 
2278c2c66affSColin Finck                 send_command(context, BTRFS_SEND_CMD_WRITE);
2279c2c66affSColin Finck 
2280318da0c1SPierre Schweitzer                 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2281c2c66affSColin Finck 
2282c2c66affSColin Finck                 offset = se->offset + off;
2283318da0c1SPierre Schweitzer                 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &offset, sizeof(uint64_t));
2284c2c66affSColin Finck 
2285c2c66affSColin Finck                 send_add_tlv(context, BTRFS_SEND_TLV_DATA, NULL, length);
2286c2c66affSColin Finck                 RtlZeroMemory(&context->data[context->datalen - length], length);
2287c2c66affSColin Finck 
2288c2c66affSColin Finck                 send_command_finish(context, pos);
2289c2c66affSColin Finck             }
2290c2c66affSColin Finck         } else if (se->data.compression == BTRFS_COMPRESSION_NONE) {
2291318da0c1SPierre Schweitzer             uint64_t off, offset;
2292318da0c1SPierre Schweitzer             uint8_t* buf;
2293c2c66affSColin Finck 
2294c2c66affSColin Finck             buf = ExAllocatePoolWithTag(NonPagedPool, MAX_SEND_WRITE + (2 * context->Vcb->superblock.sector_size), ALLOC_TAG);
2295c2c66affSColin Finck             if (!buf) {
2296c2c66affSColin Finck                 ERR("out of memory\n");
2297c2c66affSColin Finck                 ExFreePool(se);
2298c2c66affSColin Finck                 if (se2) ExFreePool(se2);
2299c2c66affSColin Finck                 return STATUS_INSUFFICIENT_RESOURCES;
2300c2c66affSColin Finck             }
2301c2c66affSColin Finck 
2302c2c66affSColin Finck             for (off = ed2->offset; off < ed2->offset + ed2->num_bytes; off += MAX_SEND_WRITE) {
2303318da0c1SPierre Schweitzer                 uint16_t length = (uint16_t)min(ed2->offset + ed2->num_bytes - off, MAX_SEND_WRITE);
2304c2c66affSColin Finck                 ULONG skip_start;
2305318da0c1SPierre Schweitzer                 uint64_t addr = ed2->address + off;
2306194ea909SVictor Perevertkin                 void* csum;
2307c2c66affSColin Finck 
2308c2c66affSColin Finck                 if (context->datalen > SEND_BUFFER_LENGTH) {
2309c2c66affSColin Finck                     Status = wait_for_flush(context, tp1, tp2);
2310c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
2311194ea909SVictor Perevertkin                         ERR("wait_for_flush returned %08lx\n", Status);
2312c2c66affSColin Finck                         ExFreePool(buf);
2313c2c66affSColin Finck                         ExFreePool(se);
2314c2c66affSColin Finck                         if (se2) ExFreePool(se2);
2315c2c66affSColin Finck                         return Status;
2316c2c66affSColin Finck                     }
2317c2c66affSColin Finck 
2318c2c66affSColin Finck                     if (context->send->cancelling) {
2319c2c66affSColin Finck                         ExFreePool(buf);
2320c2c66affSColin Finck                         ExFreePool(se);
2321c2c66affSColin Finck                         if (se2) ExFreePool(se2);
2322c2c66affSColin Finck                         return STATUS_SUCCESS;
2323c2c66affSColin Finck                     }
2324c2c66affSColin Finck                 }
2325c2c66affSColin Finck 
2326*174dfab6SVincent Franchomme                 skip_start = addr & (context->Vcb->superblock.sector_size - 1);
2327c2c66affSColin Finck                 addr -= skip_start;
2328c2c66affSColin Finck 
2329c2c66affSColin Finck                 if (context->lastinode.flags & BTRFS_INODE_NODATASUM)
2330c2c66affSColin Finck                     csum = NULL;
2331c2c66affSColin Finck                 else {
2332318da0c1SPierre Schweitzer                     uint32_t len;
2333c2c66affSColin Finck 
2334*174dfab6SVincent Franchomme                     len = (uint32_t)sector_align(length + skip_start, context->Vcb->superblock.sector_size) >> context->Vcb->sector_shift;
2335c2c66affSColin Finck 
2336194ea909SVictor Perevertkin                     csum = ExAllocatePoolWithTag(PagedPool, len * context->Vcb->csum_size, ALLOC_TAG);
2337c2c66affSColin Finck                     if (!csum) {
2338c2c66affSColin Finck                         ERR("out of memory\n");
2339c2c66affSColin Finck                         ExFreePool(buf);
2340c2c66affSColin Finck                         ExFreePool(se);
2341c2c66affSColin Finck                         if (se2) ExFreePool(se2);
2342c2c66affSColin Finck                         return STATUS_INSUFFICIENT_RESOURCES;
2343c2c66affSColin Finck                     }
2344c2c66affSColin Finck 
2345c2c66affSColin Finck                     Status = load_csum(context->Vcb, csum, addr, len, NULL);
2346c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
2347194ea909SVictor Perevertkin                         ERR("load_csum returned %08lx\n", Status);
2348c2c66affSColin Finck                         ExFreePool(csum);
2349c2c66affSColin Finck                         ExFreePool(buf);
2350c2c66affSColin Finck                         ExFreePool(se);
2351c2c66affSColin Finck                         if (se2) ExFreePool(se2);
2352c2c66affSColin Finck                         return STATUS_INSUFFICIENT_RESOURCES;
2353c2c66affSColin Finck                     }
2354c2c66affSColin Finck                 }
2355c2c66affSColin Finck 
2356318da0c1SPierre Schweitzer                 Status = read_data(context->Vcb, addr, (uint32_t)sector_align(length + skip_start, context->Vcb->superblock.sector_size),
2357318da0c1SPierre Schweitzer                                    csum, false, buf, NULL, NULL, NULL, 0, false, NormalPagePriority);
2358c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
2359194ea909SVictor Perevertkin                     ERR("read_data returned %08lx\n", Status);
2360c2c66affSColin Finck                     ExFreePool(buf);
2361c2c66affSColin Finck                     ExFreePool(se);
2362c2c66affSColin Finck                     if (se2) ExFreePool(se2);
2363c2c66affSColin Finck                     if (csum) ExFreePool(csum);
2364c2c66affSColin Finck                     return Status;
2365c2c66affSColin Finck                 }
2366c2c66affSColin Finck 
2367c2c66affSColin Finck                 if (csum)
2368c2c66affSColin Finck                     ExFreePool(csum);
2369c2c66affSColin Finck 
2370c2c66affSColin Finck                 pos = context->datalen;
2371c2c66affSColin Finck 
2372c2c66affSColin Finck                 send_command(context, BTRFS_SEND_CMD_WRITE);
2373c2c66affSColin Finck 
2374318da0c1SPierre Schweitzer                 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2375c2c66affSColin Finck 
2376c2c66affSColin Finck                 offset = se->offset + off;
2377318da0c1SPierre Schweitzer                 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &offset, sizeof(uint64_t));
2378c2c66affSColin Finck 
2379318da0c1SPierre Schweitzer                 length = (uint16_t)min(context->lastinode.size - se->offset - off, length);
2380c2c66affSColin Finck                 send_add_tlv(context, BTRFS_SEND_TLV_DATA, buf + skip_start, length);
2381c2c66affSColin Finck 
2382c2c66affSColin Finck                 send_command_finish(context, pos);
2383c2c66affSColin Finck             }
2384c2c66affSColin Finck 
2385c2c66affSColin Finck             ExFreePool(buf);
2386c2c66affSColin Finck         } else {
2387318da0c1SPierre Schweitzer             uint8_t *buf, *compbuf;
2388318da0c1SPierre Schweitzer             uint64_t off;
2389194ea909SVictor Perevertkin             void* csum;
2390c2c66affSColin Finck 
2391c2c66affSColin Finck             buf = ExAllocatePoolWithTag(PagedPool, (ULONG)se->data.decoded_size, ALLOC_TAG);
2392c2c66affSColin Finck             if (!buf) {
2393c2c66affSColin Finck                 ERR("out of memory\n");
2394c2c66affSColin Finck                 ExFreePool(se);
2395c2c66affSColin Finck                 if (se2) ExFreePool(se2);
2396c2c66affSColin Finck                 return STATUS_INSUFFICIENT_RESOURCES;
2397c2c66affSColin Finck             }
2398c2c66affSColin Finck 
2399c2c66affSColin Finck             compbuf = ExAllocatePoolWithTag(PagedPool, (ULONG)ed2->size, ALLOC_TAG);
2400c2c66affSColin Finck             if (!compbuf) {
2401c2c66affSColin Finck                 ERR("out of memory\n");
2402c2c66affSColin Finck                 ExFreePool(buf);
2403c2c66affSColin Finck                 ExFreePool(se);
2404c2c66affSColin Finck                 if (se2) ExFreePool(se2);
2405c2c66affSColin Finck                 return STATUS_INSUFFICIENT_RESOURCES;
2406c2c66affSColin Finck             }
2407c2c66affSColin Finck 
2408c2c66affSColin Finck             if (context->lastinode.flags & BTRFS_INODE_NODATASUM)
2409c2c66affSColin Finck                 csum = NULL;
2410c2c66affSColin Finck             else {
2411318da0c1SPierre Schweitzer                 uint32_t len;
2412c2c66affSColin Finck 
2413*174dfab6SVincent Franchomme                 len = (uint32_t)(ed2->size >> context->Vcb->sector_shift);
2414c2c66affSColin Finck 
2415194ea909SVictor Perevertkin                 csum = ExAllocatePoolWithTag(PagedPool, len * context->Vcb->csum_size, ALLOC_TAG);
2416c2c66affSColin Finck                 if (!csum) {
2417c2c66affSColin Finck                     ERR("out of memory\n");
2418c2c66affSColin Finck                     ExFreePool(compbuf);
2419c2c66affSColin Finck                     ExFreePool(buf);
2420c2c66affSColin Finck                     ExFreePool(se);
2421c2c66affSColin Finck                     if (se2) ExFreePool(se2);
2422c2c66affSColin Finck                     return STATUS_INSUFFICIENT_RESOURCES;
2423c2c66affSColin Finck                 }
2424c2c66affSColin Finck 
2425c2c66affSColin Finck                 Status = load_csum(context->Vcb, csum, ed2->address, len, NULL);
2426c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
2427194ea909SVictor Perevertkin                     ERR("load_csum returned %08lx\n", Status);
2428c2c66affSColin Finck                     ExFreePool(csum);
2429c2c66affSColin Finck                     ExFreePool(compbuf);
2430c2c66affSColin Finck                     ExFreePool(buf);
2431c2c66affSColin Finck                     ExFreePool(se);
2432c2c66affSColin Finck                     if (se2) ExFreePool(se2);
2433c2c66affSColin Finck                     return Status;
2434c2c66affSColin Finck                 }
2435c2c66affSColin Finck             }
2436c2c66affSColin Finck 
2437318da0c1SPierre Schweitzer             Status = read_data(context->Vcb, ed2->address, (uint32_t)ed2->size, csum, false, compbuf, NULL, NULL, NULL, 0, false, NormalPagePriority);
2438c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
2439194ea909SVictor Perevertkin                 ERR("read_data returned %08lx\n", Status);
2440c2c66affSColin Finck                 ExFreePool(compbuf);
2441c2c66affSColin Finck                 ExFreePool(buf);
2442c2c66affSColin Finck                 ExFreePool(se);
2443c2c66affSColin Finck                 if (se2) ExFreePool(se2);
2444c2c66affSColin Finck                 if (csum) ExFreePool(csum);
2445c2c66affSColin Finck                 return Status;
2446c2c66affSColin Finck             }
2447c2c66affSColin Finck 
2448c2c66affSColin Finck             if (csum)
2449c2c66affSColin Finck                 ExFreePool(csum);
2450c2c66affSColin Finck 
2451c2c66affSColin Finck             if (se->data.compression == BTRFS_COMPRESSION_ZLIB) {
2452318da0c1SPierre Schweitzer                 Status = zlib_decompress(compbuf, (uint32_t)ed2->size, buf, (uint32_t)se->data.decoded_size);
2453c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
2454194ea909SVictor Perevertkin                     ERR("zlib_decompress returned %08lx\n", Status);
2455c2c66affSColin Finck                     ExFreePool(compbuf);
2456c2c66affSColin Finck                     ExFreePool(buf);
2457c2c66affSColin Finck                     ExFreePool(se);
2458c2c66affSColin Finck                     if (se2) ExFreePool(se2);
2459c2c66affSColin Finck                     return Status;
2460c2c66affSColin Finck                 }
2461c2c66affSColin Finck             } else if (se->data.compression == BTRFS_COMPRESSION_LZO) {
2462318da0c1SPierre Schweitzer                 Status = lzo_decompress(&compbuf[sizeof(uint32_t)], (uint32_t)ed2->size, buf, (uint32_t)se->data.decoded_size, sizeof(uint32_t));
2463c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
2464194ea909SVictor Perevertkin                     ERR("lzo_decompress returned %08lx\n", Status);
2465c2c66affSColin Finck                     ExFreePool(compbuf);
2466c2c66affSColin Finck                     ExFreePool(buf);
2467c2c66affSColin Finck                     ExFreePool(se);
2468c2c66affSColin Finck                     if (se2) ExFreePool(se2);
2469c2c66affSColin Finck                     return Status;
2470c2c66affSColin Finck                 }
2471eb7fbc25SPierre Schweitzer             } else if (se->data.compression == BTRFS_COMPRESSION_ZSTD) {
2472318da0c1SPierre Schweitzer                 Status = zstd_decompress(compbuf, (uint32_t)ed2->size, buf, (uint32_t)se->data.decoded_size);
2473eb7fbc25SPierre Schweitzer                 if (!NT_SUCCESS(Status)) {
2474194ea909SVictor Perevertkin                     ERR("zstd_decompress returned %08lx\n", Status);
2475eb7fbc25SPierre Schweitzer                     ExFreePool(compbuf);
2476eb7fbc25SPierre Schweitzer                     ExFreePool(buf);
2477eb7fbc25SPierre Schweitzer                     ExFreePool(se);
2478eb7fbc25SPierre Schweitzer                     if (se2) ExFreePool(se2);
2479eb7fbc25SPierre Schweitzer                     return Status;
2480eb7fbc25SPierre Schweitzer                 }
2481c2c66affSColin Finck             }
2482c2c66affSColin Finck 
2483c2c66affSColin Finck             ExFreePool(compbuf);
2484c2c66affSColin Finck 
2485c2c66affSColin Finck             for (off = ed2->offset; off < ed2->offset + ed2->num_bytes; off += MAX_SEND_WRITE) {
2486318da0c1SPierre Schweitzer                 uint16_t length = (uint16_t)min(ed2->offset + ed2->num_bytes - off, MAX_SEND_WRITE);
2487318da0c1SPierre Schweitzer                 uint64_t offset;
2488c2c66affSColin Finck 
2489c2c66affSColin Finck                 if (context->datalen > SEND_BUFFER_LENGTH) {
2490c2c66affSColin Finck                     Status = wait_for_flush(context, tp1, tp2);
2491c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
2492194ea909SVictor Perevertkin                         ERR("wait_for_flush returned %08lx\n", Status);
2493c2c66affSColin Finck                         ExFreePool(buf);
2494c2c66affSColin Finck                         ExFreePool(se);
2495c2c66affSColin Finck                         if (se2) ExFreePool(se2);
2496c2c66affSColin Finck                         return Status;
2497c2c66affSColin Finck                     }
2498c2c66affSColin Finck 
2499c2c66affSColin Finck                     if (context->send->cancelling) {
2500c2c66affSColin Finck                         ExFreePool(buf);
2501c2c66affSColin Finck                         ExFreePool(se);
2502c2c66affSColin Finck                         if (se2) ExFreePool(se2);
2503c2c66affSColin Finck                         return STATUS_SUCCESS;
2504c2c66affSColin Finck                     }
2505c2c66affSColin Finck                 }
2506c2c66affSColin Finck 
2507c2c66affSColin Finck                 pos = context->datalen;
2508c2c66affSColin Finck 
2509c2c66affSColin Finck                 send_command(context, BTRFS_SEND_CMD_WRITE);
2510c2c66affSColin Finck 
2511318da0c1SPierre Schweitzer                 send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2512c2c66affSColin Finck 
2513c2c66affSColin Finck                 offset = se->offset + off;
2514318da0c1SPierre Schweitzer                 send_add_tlv(context, BTRFS_SEND_TLV_OFFSET, &offset, sizeof(uint64_t));
2515c2c66affSColin Finck 
2516318da0c1SPierre Schweitzer                 length = (uint16_t)min(context->lastinode.size - se->offset - off, length);
2517c2c66affSColin Finck                 send_add_tlv(context, BTRFS_SEND_TLV_DATA, &buf[off], length);
2518c2c66affSColin Finck 
2519c2c66affSColin Finck                 send_command_finish(context, pos);
2520c2c66affSColin Finck             }
2521c2c66affSColin Finck 
2522c2c66affSColin Finck             ExFreePool(buf);
2523c2c66affSColin Finck         }
2524c2c66affSColin Finck 
2525c2c66affSColin Finck         ExFreePool(se);
2526c2c66affSColin Finck         if (se2) ExFreePool(se2);
2527c2c66affSColin Finck     }
2528c2c66affSColin Finck 
2529c2c66affSColin Finck     return STATUS_SUCCESS;
2530c2c66affSColin Finck }
2531c2c66affSColin Finck 
finish_inode(send_context * context,traverse_ptr * tp1,traverse_ptr * tp2)2532c2c66affSColin Finck static NTSTATUS finish_inode(send_context* context, traverse_ptr* tp1, traverse_ptr* tp2) {
2533c2c66affSColin Finck     LIST_ENTRY* le;
2534c2c66affSColin Finck 
2535c2c66affSColin Finck     if (!IsListEmpty(&context->lastinode.refs) || !IsListEmpty(&context->lastinode.oldrefs)) {
2536c2c66affSColin Finck         NTSTATUS Status = flush_refs(context, tp1, tp2);
2537c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
2538194ea909SVictor Perevertkin             ERR("flush_refs returned %08lx\n", Status);
2539c2c66affSColin Finck             return Status;
2540c2c66affSColin Finck         }
2541c2c66affSColin Finck 
2542c2c66affSColin Finck         if (context->send->cancelling)
2543c2c66affSColin Finck             return STATUS_SUCCESS;
2544c2c66affSColin Finck     }
2545c2c66affSColin Finck 
2546c2c66affSColin Finck     if (!context->lastinode.deleting) {
2547c2c66affSColin Finck         if (context->lastinode.file) {
2548c2c66affSColin Finck             NTSTATUS Status = flush_extents(context, tp1, tp2);
2549c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
2550194ea909SVictor Perevertkin                 ERR("flush_extents returned %08lx\n", Status);
2551c2c66affSColin Finck                 return Status;
2552c2c66affSColin Finck             }
2553c2c66affSColin Finck 
2554c2c66affSColin Finck             if (context->send->cancelling)
2555c2c66affSColin Finck                 return STATUS_SUCCESS;
2556c2c66affSColin Finck 
2557c2c66affSColin Finck             send_truncate_command(context, context->lastinode.path, context->lastinode.size);
2558c2c66affSColin Finck         }
2559c2c66affSColin Finck 
2560c2c66affSColin Finck         if (context->lastinode.new || context->lastinode.uid != context->lastinode.olduid || context->lastinode.gid != context->lastinode.oldgid)
2561c2c66affSColin Finck             send_chown_command(context, context->lastinode.path, context->lastinode.uid, context->lastinode.gid);
2562c2c66affSColin Finck 
2563c2c66affSColin Finck         if (((context->lastinode.mode & __S_IFLNK) != __S_IFLNK || ((context->lastinode.mode & 07777) != 0777)) &&
2564c2c66affSColin Finck             (context->lastinode.new || context->lastinode.mode != context->lastinode.oldmode))
2565c2c66affSColin Finck             send_chmod_command(context, context->lastinode.path, context->lastinode.mode);
2566c2c66affSColin Finck 
2567c2c66affSColin Finck         send_utimes_command(context, context->lastinode.path, &context->lastinode.atime, &context->lastinode.mtime, &context->lastinode.ctime);
2568c2c66affSColin Finck     }
2569c2c66affSColin Finck 
2570c2c66affSColin Finck     while (!IsListEmpty(&context->lastinode.exts)) {
2571c2c66affSColin Finck         ExFreePool(CONTAINING_RECORD(RemoveHeadList(&context->lastinode.exts), send_ext, list_entry));
2572c2c66affSColin Finck     }
2573c2c66affSColin Finck 
2574c2c66affSColin Finck     while (!IsListEmpty(&context->lastinode.oldexts)) {
2575c2c66affSColin Finck         ExFreePool(CONTAINING_RECORD(RemoveHeadList(&context->lastinode.oldexts), send_ext, list_entry));
2576c2c66affSColin Finck     }
2577c2c66affSColin Finck 
2578c2c66affSColin Finck     if (context->parent) {
2579c2c66affSColin Finck         le = context->pending_rmdirs.Flink;
2580c2c66affSColin Finck 
2581c2c66affSColin Finck         while (le != &context->pending_rmdirs) {
2582c2c66affSColin Finck             pending_rmdir* pr = CONTAINING_RECORD(le, pending_rmdir, list_entry);
2583c2c66affSColin Finck 
2584c2c66affSColin Finck             if (pr->last_child_inode <= context->lastinode.inode) {
2585c2c66affSColin Finck                 le = le->Flink;
2586c2c66affSColin Finck 
2587c2c66affSColin Finck                 send_rmdir_command(context, pr->sd->namelen, pr->sd->name);
2588c2c66affSColin Finck 
2589c2c66affSColin Finck                 RemoveEntryList(&pr->sd->list_entry);
2590c2c66affSColin Finck 
2591c2c66affSColin Finck                 if (pr->sd->name)
2592c2c66affSColin Finck                     ExFreePool(pr->sd->name);
2593c2c66affSColin Finck 
2594c2c66affSColin Finck                 while (!IsListEmpty(&pr->sd->deleted_children)) {
2595c2c66affSColin Finck                     deleted_child* dc = CONTAINING_RECORD(RemoveHeadList(&pr->sd->deleted_children), deleted_child, list_entry);
2596c2c66affSColin Finck                     ExFreePool(dc);
2597c2c66affSColin Finck                 }
2598c2c66affSColin Finck 
2599c2c66affSColin Finck                 ExFreePool(pr->sd);
2600c2c66affSColin Finck 
2601c2c66affSColin Finck                 RemoveEntryList(&pr->list_entry);
2602c2c66affSColin Finck                 ExFreePool(pr);
2603c2c66affSColin Finck             } else
2604c2c66affSColin Finck                 break;
2605c2c66affSColin Finck         }
2606c2c66affSColin Finck     }
2607c2c66affSColin Finck 
2608c2c66affSColin Finck     context->lastinode.inode = 0;
2609c2c66affSColin Finck     context->lastinode.o = NULL;
2610c2c66affSColin Finck 
2611c2c66affSColin Finck     if (context->lastinode.path) {
2612c2c66affSColin Finck         ExFreePool(context->lastinode.path);
2613c2c66affSColin Finck         context->lastinode.path = NULL;
2614c2c66affSColin Finck     }
2615c2c66affSColin Finck 
2616c2c66affSColin Finck     return STATUS_SUCCESS;
2617c2c66affSColin Finck }
2618c2c66affSColin Finck 
send_extent_data(send_context * context,traverse_ptr * tp,traverse_ptr * tp2)2619c2c66affSColin Finck static NTSTATUS send_extent_data(send_context* context, traverse_ptr* tp, traverse_ptr* tp2) {
2620c2c66affSColin Finck     NTSTATUS Status;
2621c2c66affSColin Finck 
2622c2c66affSColin Finck     if (tp && tp2 && tp->item->size == tp2->item->size && RtlCompareMemory(tp->item->data, tp2->item->data, tp->item->size) == tp->item->size)
2623c2c66affSColin Finck         return STATUS_SUCCESS;
2624c2c66affSColin Finck 
2625c2c66affSColin Finck     if (!IsListEmpty(&context->lastinode.refs) || !IsListEmpty(&context->lastinode.oldrefs)) {
2626c2c66affSColin Finck         Status = flush_refs(context, tp, tp2);
2627c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
2628194ea909SVictor Perevertkin             ERR("flush_refs returned %08lx\n", Status);
2629c2c66affSColin Finck             return Status;
2630c2c66affSColin Finck         }
2631c2c66affSColin Finck 
2632c2c66affSColin Finck         if (context->send->cancelling)
2633c2c66affSColin Finck             return STATUS_SUCCESS;
2634c2c66affSColin Finck     }
2635c2c66affSColin Finck 
2636c2c66affSColin Finck     if ((context->lastinode.mode & __S_IFLNK) == __S_IFLNK)
2637c2c66affSColin Finck         return STATUS_SUCCESS;
2638c2c66affSColin Finck 
2639c2c66affSColin Finck     if (tp) {
2640c2c66affSColin Finck         EXTENT_DATA* ed;
2641c2c66affSColin Finck         EXTENT_DATA2* ed2 = NULL;
2642c2c66affSColin Finck 
2643c2c66affSColin Finck         if (tp->item->size < sizeof(EXTENT_DATA)) {
2644194ea909SVictor Perevertkin             ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
2645c2c66affSColin Finck                 tp->item->size, sizeof(EXTENT_DATA));
2646c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
2647c2c66affSColin Finck         }
2648c2c66affSColin Finck 
2649c2c66affSColin Finck         ed = (EXTENT_DATA*)tp->item->data;
2650c2c66affSColin Finck 
2651c2c66affSColin Finck         if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
2652c2c66affSColin Finck             ERR("unknown encryption type %u\n", ed->encryption);
2653c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
2654c2c66affSColin Finck         }
2655c2c66affSColin Finck 
2656c2c66affSColin Finck         if (ed->encoding != BTRFS_ENCODING_NONE) {
2657c2c66affSColin Finck             ERR("unknown encoding type %u\n", ed->encoding);
2658c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
2659c2c66affSColin Finck         }
2660c2c66affSColin Finck 
2661eb7fbc25SPierre Schweitzer         if (ed->compression != BTRFS_COMPRESSION_NONE && ed->compression != BTRFS_COMPRESSION_ZLIB &&
2662eb7fbc25SPierre Schweitzer             ed->compression != BTRFS_COMPRESSION_LZO && ed->compression != BTRFS_COMPRESSION_ZSTD) {
2663c2c66affSColin Finck             ERR("unknown compression type %u\n", ed->compression);
2664c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
2665c2c66affSColin Finck         }
2666c2c66affSColin Finck 
2667c2c66affSColin Finck         if (ed->type == EXTENT_TYPE_REGULAR) {
2668c2c66affSColin Finck             if (tp->item->size < offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)) {
2669194ea909SVictor Perevertkin                 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
2670c2c66affSColin Finck                     tp->item->size, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2));
2671c2c66affSColin Finck                 return STATUS_INTERNAL_ERROR;
2672c2c66affSColin Finck             }
2673c2c66affSColin Finck 
2674c2c66affSColin Finck             ed2 = (EXTENT_DATA2*)ed->data;
2675c2c66affSColin Finck         } else if (ed->type == EXTENT_TYPE_INLINE) {
2676c2c66affSColin Finck             if (tp->item->size < offsetof(EXTENT_DATA, data[0]) + ed->decoded_size && ed->compression == BTRFS_COMPRESSION_NONE) {
2677194ea909SVictor Perevertkin                 ERR("(%I64x,%x,%I64x) was %u bytes, expected %I64u\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
2678c2c66affSColin Finck                     tp->item->size, offsetof(EXTENT_DATA, data[0]) + ed->decoded_size);
2679c2c66affSColin Finck                 return STATUS_INTERNAL_ERROR;
2680c2c66affSColin Finck             }
2681c2c66affSColin Finck         }
2682c2c66affSColin Finck 
2683c2c66affSColin Finck         if ((ed->type == EXTENT_TYPE_INLINE || (ed->type == EXTENT_TYPE_REGULAR && ed2->size != 0)) && ed->decoded_size != 0) {
2684c2c66affSColin Finck             send_ext* se = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data) + tp->item->size, ALLOC_TAG);
2685c2c66affSColin Finck 
2686c2c66affSColin Finck             if (!se) {
2687c2c66affSColin Finck                 ERR("out of memory\n");
2688c2c66affSColin Finck                 return STATUS_INSUFFICIENT_RESOURCES;
2689c2c66affSColin Finck             }
2690c2c66affSColin Finck 
2691c2c66affSColin Finck             se->offset = tp->item->key.offset;
2692c2c66affSColin Finck             se->datalen = tp->item->size;
2693c2c66affSColin Finck             RtlCopyMemory(&se->data, tp->item->data, tp->item->size);
2694c2c66affSColin Finck             InsertTailList(&context->lastinode.exts, &se->list_entry);
2695c2c66affSColin Finck         }
2696c2c66affSColin Finck     }
2697c2c66affSColin Finck 
2698c2c66affSColin Finck     if (tp2) {
2699c2c66affSColin Finck         EXTENT_DATA* ed;
2700c2c66affSColin Finck         EXTENT_DATA2* ed2 = NULL;
2701c2c66affSColin Finck 
2702c2c66affSColin Finck         if (tp2->item->size < sizeof(EXTENT_DATA)) {
2703194ea909SVictor Perevertkin             ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
2704c2c66affSColin Finck                 tp2->item->size, sizeof(EXTENT_DATA));
2705c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
2706c2c66affSColin Finck         }
2707c2c66affSColin Finck 
2708c2c66affSColin Finck         ed = (EXTENT_DATA*)tp2->item->data;
2709c2c66affSColin Finck 
2710c2c66affSColin Finck         if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
2711c2c66affSColin Finck             ERR("unknown encryption type %u\n", ed->encryption);
2712c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
2713c2c66affSColin Finck         }
2714c2c66affSColin Finck 
2715c2c66affSColin Finck         if (ed->encoding != BTRFS_ENCODING_NONE) {
2716c2c66affSColin Finck             ERR("unknown encoding type %u\n", ed->encoding);
2717c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
2718c2c66affSColin Finck         }
2719c2c66affSColin Finck 
2720eb7fbc25SPierre Schweitzer         if (ed->compression != BTRFS_COMPRESSION_NONE && ed->compression != BTRFS_COMPRESSION_ZLIB &&
2721eb7fbc25SPierre Schweitzer             ed->compression != BTRFS_COMPRESSION_LZO && ed->compression != BTRFS_COMPRESSION_ZSTD) {
2722c2c66affSColin Finck             ERR("unknown compression type %u\n", ed->compression);
2723c2c66affSColin Finck             return STATUS_INTERNAL_ERROR;
2724c2c66affSColin Finck         }
2725c2c66affSColin Finck 
2726c2c66affSColin Finck         if (ed->type == EXTENT_TYPE_REGULAR) {
2727c2c66affSColin Finck             if (tp2->item->size < offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)) {
2728194ea909SVictor Perevertkin                 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
2729c2c66affSColin Finck                     tp2->item->size, offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2));
2730c2c66affSColin Finck                 return STATUS_INTERNAL_ERROR;
2731c2c66affSColin Finck             }
2732c2c66affSColin Finck 
2733c2c66affSColin Finck             ed2 = (EXTENT_DATA2*)ed->data;
2734c2c66affSColin Finck         } else if (ed->type == EXTENT_TYPE_INLINE) {
2735c2c66affSColin Finck             if (tp2->item->size < offsetof(EXTENT_DATA, data[0]) + ed->decoded_size) {
2736194ea909SVictor Perevertkin                 ERR("(%I64x,%x,%I64x) was %u bytes, expected %I64u\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
2737c2c66affSColin Finck                     tp2->item->size, offsetof(EXTENT_DATA, data[0]) + ed->decoded_size);
2738c2c66affSColin Finck                 return STATUS_INTERNAL_ERROR;
2739c2c66affSColin Finck             }
2740c2c66affSColin Finck         }
2741c2c66affSColin Finck 
2742c2c66affSColin Finck         if ((ed->type == EXTENT_TYPE_INLINE || (ed->type == EXTENT_TYPE_REGULAR && ed2->size != 0)) && ed->decoded_size != 0) {
2743c2c66affSColin Finck             send_ext* se = ExAllocatePoolWithTag(PagedPool, offsetof(send_ext, data) + tp2->item->size, ALLOC_TAG);
2744c2c66affSColin Finck 
2745c2c66affSColin Finck             if (!se) {
2746c2c66affSColin Finck                 ERR("out of memory\n");
2747c2c66affSColin Finck                 return STATUS_INSUFFICIENT_RESOURCES;
2748c2c66affSColin Finck             }
2749c2c66affSColin Finck 
2750c2c66affSColin Finck             se->offset = tp2->item->key.offset;
2751c2c66affSColin Finck             se->datalen = tp2->item->size;
2752c2c66affSColin Finck             RtlCopyMemory(&se->data, tp2->item->data, tp2->item->size);
2753c2c66affSColin Finck             InsertTailList(&context->lastinode.oldexts, &se->list_entry);
2754c2c66affSColin Finck         }
2755c2c66affSColin Finck     }
2756c2c66affSColin Finck 
2757c2c66affSColin Finck     return STATUS_SUCCESS;
2758c2c66affSColin Finck }
2759c2c66affSColin Finck 
2760c2c66affSColin Finck typedef struct {
2761318da0c1SPierre Schweitzer     uint16_t namelen;
2762c2c66affSColin Finck     char* name;
2763318da0c1SPierre Schweitzer     uint16_t value1len;
2764c2c66affSColin Finck     char* value1;
2765318da0c1SPierre Schweitzer     uint16_t value2len;
2766c2c66affSColin Finck     char* value2;
2767c2c66affSColin Finck     LIST_ENTRY list_entry;
2768c2c66affSColin Finck } xattr_cmp;
2769c2c66affSColin Finck 
send_xattr(send_context * context,traverse_ptr * tp,traverse_ptr * tp2)2770c2c66affSColin Finck static NTSTATUS send_xattr(send_context* context, traverse_ptr* tp, traverse_ptr* tp2) {
2771c2c66affSColin Finck     if (tp && tp2 && tp->item->size == tp2->item->size && RtlCompareMemory(tp->item->data, tp2->item->data, tp->item->size) == tp->item->size)
2772c2c66affSColin Finck         return STATUS_SUCCESS;
2773c2c66affSColin Finck 
2774c2c66affSColin Finck     if (!IsListEmpty(&context->lastinode.refs) || !IsListEmpty(&context->lastinode.oldrefs)) {
2775c2c66affSColin Finck         NTSTATUS Status = flush_refs(context, tp, tp2);
2776c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
2777194ea909SVictor Perevertkin             ERR("flush_refs returned %08lx\n", Status);
2778c2c66affSColin Finck             return Status;
2779c2c66affSColin Finck         }
2780c2c66affSColin Finck 
2781c2c66affSColin Finck         if (context->send->cancelling)
2782c2c66affSColin Finck             return STATUS_SUCCESS;
2783c2c66affSColin Finck     }
2784c2c66affSColin Finck 
2785c2c66affSColin Finck     if (tp && tp->item->size < sizeof(DIR_ITEM)) {
2786194ea909SVictor Perevertkin         ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset,
2787c2c66affSColin Finck             tp->item->size, sizeof(DIR_ITEM));
2788c2c66affSColin Finck         return STATUS_INTERNAL_ERROR;
2789c2c66affSColin Finck     }
2790c2c66affSColin Finck 
2791c2c66affSColin Finck     if (tp2 && tp2->item->size < sizeof(DIR_ITEM)) {
2792194ea909SVictor Perevertkin         ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset,
2793c2c66affSColin Finck             tp2->item->size, sizeof(DIR_ITEM));
2794c2c66affSColin Finck         return STATUS_INTERNAL_ERROR;
2795c2c66affSColin Finck     }
2796c2c66affSColin Finck 
2797c2c66affSColin Finck     if (tp && !tp2) {
2798c2c66affSColin Finck         ULONG len;
2799c2c66affSColin Finck         DIR_ITEM* di;
2800c2c66affSColin Finck 
2801c2c66affSColin Finck         len = tp->item->size;
2802c2c66affSColin Finck         di = (DIR_ITEM*)tp->item->data;
2803c2c66affSColin Finck 
2804c2c66affSColin Finck         do {
2805c2c66affSColin Finck             ULONG pos;
2806c2c66affSColin Finck 
2807c2c66affSColin Finck             if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
2808318da0c1SPierre Schweitzer                 ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
2809c2c66affSColin Finck                 return STATUS_INTERNAL_ERROR;
2810c2c66affSColin Finck             }
2811c2c66affSColin Finck 
2812c2c66affSColin Finck             pos = context->datalen;
2813c2c66affSColin Finck             send_command(context, BTRFS_SEND_CMD_SET_XATTR);
2814318da0c1SPierre Schweitzer             send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2815c2c66affSColin Finck             send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, di->name, di->n);
2816c2c66affSColin Finck             send_add_tlv(context, BTRFS_SEND_TLV_XATTR_DATA, &di->name[di->n], di->m);
2817c2c66affSColin Finck             send_command_finish(context, pos);
2818c2c66affSColin Finck 
2819c2c66affSColin Finck             len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
2820c2c66affSColin Finck             di = (DIR_ITEM*)&di->name[di->m + di->n];
2821c2c66affSColin Finck         } while (len > 0);
2822c2c66affSColin Finck     } else if (!tp && tp2) {
2823c2c66affSColin Finck         ULONG len;
2824c2c66affSColin Finck         DIR_ITEM* di;
2825c2c66affSColin Finck 
2826c2c66affSColin Finck         len = tp2->item->size;
2827c2c66affSColin Finck         di = (DIR_ITEM*)tp2->item->data;
2828c2c66affSColin Finck 
2829c2c66affSColin Finck         do {
2830c2c66affSColin Finck             ULONG pos;
2831c2c66affSColin Finck 
2832c2c66affSColin Finck             if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
2833318da0c1SPierre Schweitzer                 ERR("(%I64x,%x,%I64x) was truncated\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset);
2834c2c66affSColin Finck                 return STATUS_INTERNAL_ERROR;
2835c2c66affSColin Finck             }
2836c2c66affSColin Finck 
2837c2c66affSColin Finck             pos = context->datalen;
2838c2c66affSColin Finck             send_command(context, BTRFS_SEND_CMD_REMOVE_XATTR);
2839318da0c1SPierre Schweitzer             send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2840c2c66affSColin Finck             send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, di->name, di->n);
2841c2c66affSColin Finck             send_command_finish(context, pos);
2842c2c66affSColin Finck 
2843c2c66affSColin Finck             len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
2844c2c66affSColin Finck             di = (DIR_ITEM*)&di->name[di->m + di->n];
2845c2c66affSColin Finck         } while (len > 0);
2846c2c66affSColin Finck     } else {
2847c2c66affSColin Finck         ULONG len;
2848c2c66affSColin Finck         DIR_ITEM* di;
2849c2c66affSColin Finck         LIST_ENTRY xattrs;
2850c2c66affSColin Finck 
2851c2c66affSColin Finck         InitializeListHead(&xattrs);
2852c2c66affSColin Finck 
2853c2c66affSColin Finck         len = tp->item->size;
2854c2c66affSColin Finck         di = (DIR_ITEM*)tp->item->data;
2855c2c66affSColin Finck 
2856c2c66affSColin Finck         do {
2857c2c66affSColin Finck             xattr_cmp* xa;
2858c2c66affSColin Finck 
2859c2c66affSColin Finck             if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
2860318da0c1SPierre Schweitzer                 ERR("(%I64x,%x,%I64x) was truncated\n", tp->item->key.obj_id, tp->item->key.obj_type, tp->item->key.offset);
2861c2c66affSColin Finck                 return STATUS_INTERNAL_ERROR;
2862c2c66affSColin Finck             }
2863c2c66affSColin Finck 
2864c2c66affSColin Finck             xa = ExAllocatePoolWithTag(PagedPool, sizeof(xattr_cmp), ALLOC_TAG);
2865c2c66affSColin Finck             if (!xa) {
2866c2c66affSColin Finck                 ERR("out of memory\n");
2867c2c66affSColin Finck 
2868c2c66affSColin Finck                 while (!IsListEmpty(&xattrs)) {
2869c2c66affSColin Finck                     ExFreePool(CONTAINING_RECORD(RemoveHeadList(&xattrs), xattr_cmp, list_entry));
2870c2c66affSColin Finck                 }
2871c2c66affSColin Finck 
2872c2c66affSColin Finck                 return STATUS_INSUFFICIENT_RESOURCES;
2873c2c66affSColin Finck             }
2874c2c66affSColin Finck 
2875c2c66affSColin Finck             xa->namelen = di->n;
2876c2c66affSColin Finck             xa->name = di->name;
2877c2c66affSColin Finck             xa->value1len = di->m;
2878c2c66affSColin Finck             xa->value1 = di->name + di->n;
2879c2c66affSColin Finck             xa->value2len = 0;
2880c2c66affSColin Finck             xa->value2 = NULL;
2881c2c66affSColin Finck 
2882c2c66affSColin Finck             InsertTailList(&xattrs, &xa->list_entry);
2883c2c66affSColin Finck 
2884c2c66affSColin Finck             len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
2885c2c66affSColin Finck             di = (DIR_ITEM*)&di->name[di->m + di->n];
2886c2c66affSColin Finck         } while (len > 0);
2887c2c66affSColin Finck 
2888c2c66affSColin Finck         len = tp2->item->size;
2889c2c66affSColin Finck         di = (DIR_ITEM*)tp2->item->data;
2890c2c66affSColin Finck 
2891c2c66affSColin Finck         do {
2892c2c66affSColin Finck             xattr_cmp* xa;
2893c2c66affSColin Finck             LIST_ENTRY* le;
2894318da0c1SPierre Schweitzer             bool found = false;
2895c2c66affSColin Finck 
2896c2c66affSColin Finck             if (len < sizeof(DIR_ITEM) || len < offsetof(DIR_ITEM, name[0]) + di->m + di->n) {
2897318da0c1SPierre Schweitzer                 ERR("(%I64x,%x,%I64x) was truncated\n", tp2->item->key.obj_id, tp2->item->key.obj_type, tp2->item->key.offset);
2898c2c66affSColin Finck                 return STATUS_INTERNAL_ERROR;
2899c2c66affSColin Finck             }
2900c2c66affSColin Finck 
2901c2c66affSColin Finck             le = xattrs.Flink;
2902c2c66affSColin Finck             while (le != &xattrs) {
2903c2c66affSColin Finck                 xa = CONTAINING_RECORD(le, xattr_cmp, list_entry);
2904c2c66affSColin Finck 
2905c2c66affSColin Finck                 if (xa->namelen == di->n && RtlCompareMemory(xa->name, di->name, di->n) == di->n) {
2906c2c66affSColin Finck                     xa->value2len = di->m;
2907c2c66affSColin Finck                     xa->value2 = di->name + di->n;
2908318da0c1SPierre Schweitzer                     found = true;
2909c2c66affSColin Finck                     break;
2910c2c66affSColin Finck                 }
2911c2c66affSColin Finck 
2912c2c66affSColin Finck                 le = le->Flink;
2913c2c66affSColin Finck             }
2914c2c66affSColin Finck 
2915c2c66affSColin Finck             if (!found) {
2916c2c66affSColin Finck                 xa = ExAllocatePoolWithTag(PagedPool, sizeof(xattr_cmp), ALLOC_TAG);
2917c2c66affSColin Finck                 if (!xa) {
2918c2c66affSColin Finck                     ERR("out of memory\n");
2919c2c66affSColin Finck 
2920c2c66affSColin Finck                     while (!IsListEmpty(&xattrs)) {
2921c2c66affSColin Finck                         ExFreePool(CONTAINING_RECORD(RemoveHeadList(&xattrs), xattr_cmp, list_entry));
2922c2c66affSColin Finck                     }
2923c2c66affSColin Finck 
2924c2c66affSColin Finck                     return STATUS_INSUFFICIENT_RESOURCES;
2925c2c66affSColin Finck                 }
2926c2c66affSColin Finck 
2927c2c66affSColin Finck                 xa->namelen = di->n;
2928c2c66affSColin Finck                 xa->name = di->name;
2929c2c66affSColin Finck                 xa->value1len = 0;
2930c2c66affSColin Finck                 xa->value1 = NULL;
2931c2c66affSColin Finck                 xa->value2len = di->m;
2932c2c66affSColin Finck                 xa->value2 = di->name + di->n;
2933c2c66affSColin Finck 
2934c2c66affSColin Finck                 InsertTailList(&xattrs, &xa->list_entry);
2935c2c66affSColin Finck             }
2936c2c66affSColin Finck 
2937c2c66affSColin Finck             len -= (ULONG)offsetof(DIR_ITEM, name[0]) + di->m + di->n;
2938c2c66affSColin Finck             di = (DIR_ITEM*)&di->name[di->m + di->n];
2939c2c66affSColin Finck         } while (len > 0);
2940c2c66affSColin Finck 
2941c2c66affSColin Finck         while (!IsListEmpty(&xattrs)) {
2942c2c66affSColin Finck             xattr_cmp* xa = CONTAINING_RECORD(RemoveHeadList(&xattrs), xattr_cmp, list_entry);
2943c2c66affSColin Finck 
2944c2c66affSColin Finck             if (xa->value1len != xa->value2len || !xa->value1 || !xa->value2 || RtlCompareMemory(xa->value1, xa->value2, xa->value1len) != xa->value1len) {
2945c2c66affSColin Finck                 ULONG pos;
2946c2c66affSColin Finck 
2947c2c66affSColin Finck                 if (!xa->value1) {
2948c2c66affSColin Finck                     pos = context->datalen;
2949c2c66affSColin Finck                     send_command(context, BTRFS_SEND_CMD_REMOVE_XATTR);
2950318da0c1SPierre Schweitzer                     send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2951c2c66affSColin Finck                     send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, xa->name, xa->namelen);
2952c2c66affSColin Finck                     send_command_finish(context, pos);
2953c2c66affSColin Finck                 } else {
2954c2c66affSColin Finck                     pos = context->datalen;
2955c2c66affSColin Finck                     send_command(context, BTRFS_SEND_CMD_SET_XATTR);
2956318da0c1SPierre Schweitzer                     send_add_tlv(context, BTRFS_SEND_TLV_PATH, context->lastinode.path, context->lastinode.path ? (uint16_t)strlen(context->lastinode.path) : 0);
2957c2c66affSColin Finck                     send_add_tlv(context, BTRFS_SEND_TLV_XATTR_NAME, xa->name, xa->namelen);
2958c2c66affSColin Finck                     send_add_tlv(context, BTRFS_SEND_TLV_XATTR_DATA, xa->value1, xa->value1len);
2959c2c66affSColin Finck                     send_command_finish(context, pos);
2960c2c66affSColin Finck                 }
2961c2c66affSColin Finck             }
2962c2c66affSColin Finck 
2963c2c66affSColin Finck             ExFreePool(xa);
2964c2c66affSColin Finck         }
2965c2c66affSColin Finck     }
2966c2c66affSColin Finck 
2967c2c66affSColin Finck     return STATUS_SUCCESS;
2968c2c66affSColin Finck }
2969c2c66affSColin Finck 
_Function_class_(KSTART_ROUTINE)2970c2c66affSColin Finck _Function_class_(KSTART_ROUTINE)
2971318da0c1SPierre Schweitzer static void __stdcall send_thread(void* ctx) {
2972c2c66affSColin Finck     send_context* context = (send_context*)ctx;
2973c2c66affSColin Finck     NTSTATUS Status;
2974c2c66affSColin Finck     KEY searchkey;
2975c2c66affSColin Finck     traverse_ptr tp, tp2;
2976c2c66affSColin Finck 
2977c2c66affSColin Finck     InterlockedIncrement(&context->root->send_ops);
2978c2c66affSColin Finck 
2979c2c66affSColin Finck     if (context->parent)
2980c2c66affSColin Finck         InterlockedIncrement(&context->parent->send_ops);
2981c2c66affSColin Finck 
2982c2c66affSColin Finck     if (context->clones) {
2983c2c66affSColin Finck         ULONG i;
2984c2c66affSColin Finck 
2985c2c66affSColin Finck         for (i = 0; i < context->num_clones; i++) {
2986c2c66affSColin Finck             InterlockedIncrement(&context->clones[i]->send_ops);
2987c2c66affSColin Finck         }
2988c2c66affSColin Finck     }
2989c2c66affSColin Finck 
2990318da0c1SPierre Schweitzer     ExAcquireResourceExclusiveLite(&context->Vcb->tree_lock, true);
2991c2c66affSColin Finck 
2992c2c66affSColin Finck     flush_subvol_fcbs(context->root);
2993c2c66affSColin Finck 
2994c2c66affSColin Finck     if (context->parent)
2995c2c66affSColin Finck         flush_subvol_fcbs(context->parent);
2996c2c66affSColin Finck 
2997c2c66affSColin Finck     if (context->Vcb->need_write)
2998c2c66affSColin Finck         Status = do_write(context->Vcb, NULL);
2999c2c66affSColin Finck     else
3000c2c66affSColin Finck         Status = STATUS_SUCCESS;
3001c2c66affSColin Finck 
3002c2c66affSColin Finck     free_trees(context->Vcb);
3003c2c66affSColin Finck 
3004c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
3005194ea909SVictor Perevertkin         ERR("do_write returned %08lx\n", Status);
3006c2c66affSColin Finck         ExReleaseResourceLite(&context->Vcb->tree_lock);
3007c2c66affSColin Finck         goto end;
3008c2c66affSColin Finck     }
3009c2c66affSColin Finck 
3010c2c66affSColin Finck     ExConvertExclusiveToSharedLite(&context->Vcb->tree_lock);
3011c2c66affSColin Finck 
3012c2c66affSColin Finck     searchkey.obj_id = searchkey.offset = 0;
3013c2c66affSColin Finck     searchkey.obj_type = 0;
3014c2c66affSColin Finck 
3015318da0c1SPierre Schweitzer     Status = find_item(context->Vcb, context->root, &tp, &searchkey, false, NULL);
3016c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
3017194ea909SVictor Perevertkin         ERR("find_item returned %08lx\n", Status);
3018c2c66affSColin Finck         ExReleaseResourceLite(&context->Vcb->tree_lock);
3019c2c66affSColin Finck         goto end;
3020c2c66affSColin Finck     }
3021c2c66affSColin Finck 
3022c2c66affSColin Finck     if (context->parent) {
3023318da0c1SPierre Schweitzer         bool ended1 = false, ended2 = false;
3024318da0c1SPierre Schweitzer         Status = find_item(context->Vcb, context->parent, &tp2, &searchkey, false, NULL);
3025c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
3026194ea909SVictor Perevertkin             ERR("find_item returned %08lx\n", Status);
3027c2c66affSColin Finck             ExReleaseResourceLite(&context->Vcb->tree_lock);
3028c2c66affSColin Finck             goto end;
3029c2c66affSColin Finck         }
3030c2c66affSColin Finck 
3031c2c66affSColin Finck         do {
3032c2c66affSColin Finck             traverse_ptr next_tp;
3033c2c66affSColin Finck 
3034c2c66affSColin Finck             if (context->datalen > SEND_BUFFER_LENGTH) {
3035c2c66affSColin Finck                 KEY key1 = tp.item->key, key2 = tp2.item->key;
3036c2c66affSColin Finck 
3037c2c66affSColin Finck                 ExReleaseResourceLite(&context->Vcb->tree_lock);
3038c2c66affSColin Finck 
3039c2c66affSColin Finck                 KeClearEvent(&context->send->cleared_event);
3040318da0c1SPierre Schweitzer                 KeSetEvent(&context->buffer_event, 0, true);
3041318da0c1SPierre Schweitzer                 KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, false, NULL);
3042c2c66affSColin Finck 
3043c2c66affSColin Finck                 if (context->send->cancelling)
3044c2c66affSColin Finck                     goto end;
3045c2c66affSColin Finck 
3046318da0c1SPierre Schweitzer                 ExAcquireResourceSharedLite(&context->Vcb->tree_lock, true);
3047c2c66affSColin Finck 
3048c2c66affSColin Finck                 if (!ended1) {
3049318da0c1SPierre Schweitzer                     Status = find_item(context->Vcb, context->root, &tp, &key1, false, NULL);
3050c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3051194ea909SVictor Perevertkin                         ERR("find_item returned %08lx\n", Status);
3052c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3053c2c66affSColin Finck                         goto end;
3054c2c66affSColin Finck                     }
3055c2c66affSColin Finck 
3056c2c66affSColin Finck                     if (keycmp(tp.item->key, key1)) {
3057c2c66affSColin Finck                         ERR("readonly subvolume changed\n");
3058c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3059c2c66affSColin Finck                         Status = STATUS_INTERNAL_ERROR;
3060c2c66affSColin Finck                         goto end;
3061c2c66affSColin Finck                     }
3062c2c66affSColin Finck                 }
3063c2c66affSColin Finck 
3064c2c66affSColin Finck                 if (!ended2) {
3065318da0c1SPierre Schweitzer                     Status = find_item(context->Vcb, context->parent, &tp2, &key2, false, NULL);
3066c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3067194ea909SVictor Perevertkin                         ERR("find_item returned %08lx\n", Status);
3068c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3069c2c66affSColin Finck                         goto end;
3070c2c66affSColin Finck                     }
3071c2c66affSColin Finck 
3072c2c66affSColin Finck                     if (keycmp(tp2.item->key, key2)) {
3073c2c66affSColin Finck                         ERR("readonly subvolume changed\n");
3074c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3075c2c66affSColin Finck                         Status = STATUS_INTERNAL_ERROR;
3076c2c66affSColin Finck                         goto end;
3077c2c66affSColin Finck                     }
3078c2c66affSColin Finck                 }
3079c2c66affSColin Finck             }
3080c2c66affSColin Finck 
3081c2c66affSColin Finck             while (!ended1 && !ended2 && tp.tree->header.address == tp2.tree->header.address) {
3082c2c66affSColin Finck                 Status = skip_to_difference(context->Vcb, &tp, &tp2, &ended1, &ended2);
3083c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
3084194ea909SVictor Perevertkin                     ERR("skip_to_difference returned %08lx\n", Status);
3085c2c66affSColin Finck                     ExReleaseResourceLite(&context->Vcb->tree_lock);
3086c2c66affSColin Finck                     goto end;
3087c2c66affSColin Finck                 }
3088c2c66affSColin Finck             }
3089c2c66affSColin Finck 
3090c2c66affSColin Finck             if (!ended1 && !ended2 && !keycmp(tp.item->key, tp2.item->key)) {
3091318da0c1SPierre Schweitzer                 bool no_next = false, no_next2 = false;
3092c2c66affSColin Finck 
3093318da0c1SPierre Schweitzer                 TRACE("~ %I64x,%x,%I64x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
3094c2c66affSColin Finck 
3095c2c66affSColin Finck                 if (context->lastinode.inode != 0 && tp.item->key.obj_id > context->lastinode.inode) {
3096c2c66affSColin Finck                     Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2);
3097c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3098194ea909SVictor Perevertkin                         ERR("finish_inode returned %08lx\n", Status);
3099c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3100c2c66affSColin Finck                         goto end;
3101c2c66affSColin Finck                     }
3102c2c66affSColin Finck 
3103c2c66affSColin Finck                     if (context->send->cancelling) {
3104c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3105c2c66affSColin Finck                         goto end;
3106c2c66affSColin Finck                     }
3107c2c66affSColin Finck                 }
3108c2c66affSColin Finck 
3109c2c66affSColin Finck                 if (tp.item->key.obj_type == TYPE_INODE_ITEM) {
3110c2c66affSColin Finck                     if (tp.item->size == tp2.item->size && tp.item->size > 0 && RtlCompareMemory(tp.item->data, tp2.item->data, tp.item->size) == tp.item->size) {
3111318da0c1SPierre Schweitzer                         uint64_t inode = tp.item->key.obj_id;
3112c2c66affSColin Finck 
3113318da0c1SPierre Schweitzer                         while (true) {
3114318da0c1SPierre Schweitzer                             if (!find_next_item(context->Vcb, &tp, &next_tp, false, NULL)) {
3115318da0c1SPierre Schweitzer                                 ended1 = true;
3116c2c66affSColin Finck                                 break;
3117c2c66affSColin Finck                             }
3118c2c66affSColin Finck 
3119c2c66affSColin Finck                             tp = next_tp;
3120c2c66affSColin Finck 
3121c2c66affSColin Finck                             if (tp.item->key.obj_id != inode)
3122c2c66affSColin Finck                                 break;
3123c2c66affSColin Finck                         }
3124c2c66affSColin Finck 
3125318da0c1SPierre Schweitzer                         while (true) {
3126318da0c1SPierre Schweitzer                             if (!find_next_item(context->Vcb, &tp2, &next_tp, false, NULL)) {
3127318da0c1SPierre Schweitzer                                 ended2 = true;
3128c2c66affSColin Finck                                 break;
3129c2c66affSColin Finck                             }
3130c2c66affSColin Finck 
3131c2c66affSColin Finck                             tp2 = next_tp;
3132c2c66affSColin Finck 
3133c2c66affSColin Finck                             if (tp2.item->key.obj_id != inode)
3134c2c66affSColin Finck                                 break;
3135c2c66affSColin Finck                         }
3136c2c66affSColin Finck 
3137318da0c1SPierre Schweitzer                         no_next = true;
3138318da0c1SPierre Schweitzer                     } else if (tp.item->size > sizeof(uint64_t) && tp2.item->size > sizeof(uint64_t) && *(uint64_t*)tp.item->data != *(uint64_t*)tp2.item->data) {
3139318da0c1SPierre Schweitzer                         uint64_t inode = tp.item->key.obj_id;
3140c2c66affSColin Finck 
3141c2c66affSColin Finck                         Status = send_inode(context, NULL, &tp2);
3142c2c66affSColin Finck                         if (!NT_SUCCESS(Status)) {
3143194ea909SVictor Perevertkin                             ERR("send_inode returned %08lx\n", Status);
3144c2c66affSColin Finck                             ExReleaseResourceLite(&context->Vcb->tree_lock);
3145c2c66affSColin Finck                             goto end;
3146c2c66affSColin Finck                         }
3147c2c66affSColin Finck 
3148318da0c1SPierre Schweitzer                         while (true) {
3149318da0c1SPierre Schweitzer                             if (!find_next_item(context->Vcb, &tp2, &next_tp, false, NULL)) {
3150318da0c1SPierre Schweitzer                                 ended2 = true;
3151c2c66affSColin Finck                                 break;
3152c2c66affSColin Finck                             }
3153c2c66affSColin Finck 
3154c2c66affSColin Finck                             tp2 = next_tp;
3155c2c66affSColin Finck 
3156c2c66affSColin Finck                             if (tp2.item->key.obj_id != inode)
3157c2c66affSColin Finck                                 break;
3158c2c66affSColin Finck 
3159c2c66affSColin Finck                             if (tp2.item->key.obj_type == TYPE_INODE_REF) {
3160318da0c1SPierre Schweitzer                                 Status = send_inode_ref(context, &tp2, true);
3161c2c66affSColin Finck                                 if (!NT_SUCCESS(Status)) {
3162194ea909SVictor Perevertkin                                     ERR("send_inode_ref returned %08lx\n", Status);
3163c2c66affSColin Finck                                     ExReleaseResourceLite(&context->Vcb->tree_lock);
3164c2c66affSColin Finck                                     goto end;
3165c2c66affSColin Finck                                 }
3166c2c66affSColin Finck                             } else if (tp2.item->key.obj_type == TYPE_INODE_EXTREF) {
3167318da0c1SPierre Schweitzer                                 Status = send_inode_extref(context, &tp2, true);
3168c2c66affSColin Finck                                 if (!NT_SUCCESS(Status)) {
3169194ea909SVictor Perevertkin                                     ERR("send_inode_extref returned %08lx\n", Status);
3170c2c66affSColin Finck                                     ExReleaseResourceLite(&context->Vcb->tree_lock);
3171c2c66affSColin Finck                                     goto end;
3172c2c66affSColin Finck                                 }
3173c2c66affSColin Finck                             }
3174c2c66affSColin Finck                         }
3175c2c66affSColin Finck 
3176c2c66affSColin Finck                         Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2);
3177c2c66affSColin Finck                         if (!NT_SUCCESS(Status)) {
3178194ea909SVictor Perevertkin                             ERR("finish_inode returned %08lx\n", Status);
3179c2c66affSColin Finck                             ExReleaseResourceLite(&context->Vcb->tree_lock);
3180c2c66affSColin Finck                             goto end;
3181c2c66affSColin Finck                         }
3182c2c66affSColin Finck 
3183c2c66affSColin Finck                         if (context->send->cancelling) {
3184c2c66affSColin Finck                             ExReleaseResourceLite(&context->Vcb->tree_lock);
3185c2c66affSColin Finck                             goto end;
3186c2c66affSColin Finck                         }
3187c2c66affSColin Finck 
3188318da0c1SPierre Schweitzer                         no_next2 = true;
3189c2c66affSColin Finck 
3190c2c66affSColin Finck                         Status = send_inode(context, &tp, NULL);
3191c2c66affSColin Finck                         if (!NT_SUCCESS(Status)) {
3192194ea909SVictor Perevertkin                             ERR("send_inode returned %08lx\n", Status);
3193c2c66affSColin Finck                             ExReleaseResourceLite(&context->Vcb->tree_lock);
3194c2c66affSColin Finck                             goto end;
3195c2c66affSColin Finck                         }
3196c2c66affSColin Finck                     } else {
3197c2c66affSColin Finck                         Status = send_inode(context, &tp, &tp2);
3198c2c66affSColin Finck                         if (!NT_SUCCESS(Status)) {
3199194ea909SVictor Perevertkin                             ERR("send_inode returned %08lx\n", Status);
3200c2c66affSColin Finck                             ExReleaseResourceLite(&context->Vcb->tree_lock);
3201c2c66affSColin Finck                             goto end;
3202c2c66affSColin Finck                         }
3203c2c66affSColin Finck                     }
3204c2c66affSColin Finck                 } else if (tp.item->key.obj_type == TYPE_INODE_REF) {
3205318da0c1SPierre Schweitzer                     Status = send_inode_ref(context, &tp, false);
3206c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3207194ea909SVictor Perevertkin                         ERR("send_inode_ref returned %08lx\n", Status);
3208c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3209c2c66affSColin Finck                         goto end;
3210c2c66affSColin Finck                     }
3211c2c66affSColin Finck 
3212318da0c1SPierre Schweitzer                     Status = send_inode_ref(context, &tp2, true);
3213c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3214194ea909SVictor Perevertkin                         ERR("send_inode_ref returned %08lx\n", Status);
3215c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3216c2c66affSColin Finck                         goto end;
3217c2c66affSColin Finck                     }
3218c2c66affSColin Finck                 } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
3219318da0c1SPierre Schweitzer                     Status = send_inode_extref(context, &tp, false);
3220c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3221194ea909SVictor Perevertkin                         ERR("send_inode_extref returned %08lx\n", Status);
3222c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3223c2c66affSColin Finck                         goto end;
3224c2c66affSColin Finck                     }
3225c2c66affSColin Finck 
3226318da0c1SPierre Schweitzer                     Status = send_inode_extref(context, &tp2, true);
3227c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3228194ea909SVictor Perevertkin                         ERR("send_inode_extref returned %08lx\n", Status);
3229c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3230c2c66affSColin Finck                         goto end;
3231c2c66affSColin Finck                     }
3232c2c66affSColin Finck                 } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) {
3233c2c66affSColin Finck                     Status = send_extent_data(context, &tp, &tp2);
3234c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3235194ea909SVictor Perevertkin                         ERR("send_extent_data returned %08lx\n", Status);
3236c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3237c2c66affSColin Finck                         goto end;
3238c2c66affSColin Finck                     }
3239c2c66affSColin Finck 
3240c2c66affSColin Finck                     if (context->send->cancelling) {
3241c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3242c2c66affSColin Finck                         goto end;
3243c2c66affSColin Finck                     }
3244c2c66affSColin Finck                 } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
3245c2c66affSColin Finck                     Status = send_xattr(context, &tp, &tp2);
3246c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3247194ea909SVictor Perevertkin                         ERR("send_xattr returned %08lx\n", Status);
3248c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3249c2c66affSColin Finck                         goto end;
3250c2c66affSColin Finck                     }
3251c2c66affSColin Finck 
3252c2c66affSColin Finck                     if (context->send->cancelling) {
3253c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3254c2c66affSColin Finck                         goto end;
3255c2c66affSColin Finck                     }
3256c2c66affSColin Finck                 }
3257c2c66affSColin Finck 
3258c2c66affSColin Finck                 if (!no_next) {
3259318da0c1SPierre Schweitzer                     if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL))
3260c2c66affSColin Finck                         tp = next_tp;
3261c2c66affSColin Finck                     else
3262318da0c1SPierre Schweitzer                         ended1 = true;
3263c2c66affSColin Finck 
3264c2c66affSColin Finck                     if (!no_next2) {
3265318da0c1SPierre Schweitzer                         if (find_next_item(context->Vcb, &tp2, &next_tp, false, NULL))
3266c2c66affSColin Finck                             tp2 = next_tp;
3267c2c66affSColin Finck                         else
3268318da0c1SPierre Schweitzer                             ended2 = true;
3269c2c66affSColin Finck                     }
3270c2c66affSColin Finck                 }
3271c2c66affSColin Finck             } else if (ended2 || (!ended1 && !ended2 && keycmp(tp.item->key, tp2.item->key) == -1)) {
3272318da0c1SPierre Schweitzer                 TRACE("A %I64x,%x,%I64x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
3273c2c66affSColin Finck 
3274c2c66affSColin Finck                 if (context->lastinode.inode != 0 && tp.item->key.obj_id > context->lastinode.inode) {
3275c2c66affSColin Finck                     Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2);
3276c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3277194ea909SVictor Perevertkin                         ERR("finish_inode returned %08lx\n", Status);
3278c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3279c2c66affSColin Finck                         goto end;
3280c2c66affSColin Finck                     }
3281c2c66affSColin Finck 
3282c2c66affSColin Finck                     if (context->send->cancelling) {
3283c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3284c2c66affSColin Finck                         goto end;
3285c2c66affSColin Finck                     }
3286c2c66affSColin Finck                 }
3287c2c66affSColin Finck 
3288c2c66affSColin Finck                 if (tp.item->key.obj_type == TYPE_INODE_ITEM) {
3289c2c66affSColin Finck                     Status = send_inode(context, &tp, NULL);
3290c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3291194ea909SVictor Perevertkin                         ERR("send_inode returned %08lx\n", Status);
3292c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3293c2c66affSColin Finck                         goto end;
3294c2c66affSColin Finck                     }
3295c2c66affSColin Finck                 } else if (tp.item->key.obj_type == TYPE_INODE_REF) {
3296318da0c1SPierre Schweitzer                     Status = send_inode_ref(context, &tp, false);
3297c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3298194ea909SVictor Perevertkin                         ERR("send_inode_ref returned %08lx\n", Status);
3299c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3300c2c66affSColin Finck                         goto end;
3301c2c66affSColin Finck                     }
3302c2c66affSColin Finck                 } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
3303318da0c1SPierre Schweitzer                     Status = send_inode_extref(context, &tp, false);
3304c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3305194ea909SVictor Perevertkin                         ERR("send_inode_extref returned %08lx\n", Status);
3306c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3307c2c66affSColin Finck                         goto end;
3308c2c66affSColin Finck                     }
3309c2c66affSColin Finck                 } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) {
3310c2c66affSColin Finck                     Status = send_extent_data(context, &tp, NULL);
3311c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3312194ea909SVictor Perevertkin                         ERR("send_extent_data returned %08lx\n", Status);
3313c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3314c2c66affSColin Finck                         goto end;
3315c2c66affSColin Finck                     }
3316c2c66affSColin Finck 
3317c2c66affSColin Finck                     if (context->send->cancelling) {
3318c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3319c2c66affSColin Finck                         goto end;
3320c2c66affSColin Finck                     }
3321c2c66affSColin Finck                 } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
3322c2c66affSColin Finck                     Status = send_xattr(context, &tp, NULL);
3323c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3324194ea909SVictor Perevertkin                         ERR("send_xattr returned %08lx\n", Status);
3325c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3326c2c66affSColin Finck                         goto end;
3327c2c66affSColin Finck                     }
3328c2c66affSColin Finck 
3329c2c66affSColin Finck                     if (context->send->cancelling) {
3330c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3331c2c66affSColin Finck                         goto end;
3332c2c66affSColin Finck                     }
3333c2c66affSColin Finck                 }
3334c2c66affSColin Finck 
3335318da0c1SPierre Schweitzer                 if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL))
3336c2c66affSColin Finck                     tp = next_tp;
3337c2c66affSColin Finck                 else
3338318da0c1SPierre Schweitzer                     ended1 = true;
3339c2c66affSColin Finck             } else if (ended1 || (!ended1 && !ended2 && keycmp(tp.item->key, tp2.item->key) == 1)) {
3340318da0c1SPierre Schweitzer                 TRACE("B %I64x,%x,%I64x\n", tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset);
3341c2c66affSColin Finck 
3342c2c66affSColin Finck                 if (context->lastinode.inode != 0 && tp2.item->key.obj_id > context->lastinode.inode) {
3343c2c66affSColin Finck                     Status = finish_inode(context, ended1 ? NULL : &tp, ended2 ? NULL : &tp2);
3344c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3345194ea909SVictor Perevertkin                         ERR("finish_inode returned %08lx\n", Status);
3346c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3347c2c66affSColin Finck                         goto end;
3348c2c66affSColin Finck                     }
3349c2c66affSColin Finck 
3350c2c66affSColin Finck                     if (context->send->cancelling) {
3351c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3352c2c66affSColin Finck                         goto end;
3353c2c66affSColin Finck                     }
3354c2c66affSColin Finck                 }
3355c2c66affSColin Finck 
3356c2c66affSColin Finck                 if (tp2.item->key.obj_type == TYPE_INODE_ITEM) {
3357c2c66affSColin Finck                     Status = send_inode(context, NULL, &tp2);
3358c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3359194ea909SVictor Perevertkin                         ERR("send_inode returned %08lx\n", Status);
3360c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3361c2c66affSColin Finck                         goto end;
3362c2c66affSColin Finck                     }
3363c2c66affSColin Finck                 } else if (tp2.item->key.obj_type == TYPE_INODE_REF) {
3364318da0c1SPierre Schweitzer                     Status = send_inode_ref(context, &tp2, true);
3365c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3366194ea909SVictor Perevertkin                         ERR("send_inode_ref returned %08lx\n", Status);
3367c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3368c2c66affSColin Finck                         goto end;
3369c2c66affSColin Finck                     }
3370c2c66affSColin Finck                 } else if (tp2.item->key.obj_type == TYPE_INODE_EXTREF) {
3371318da0c1SPierre Schweitzer                     Status = send_inode_extref(context, &tp2, true);
3372c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3373194ea909SVictor Perevertkin                         ERR("send_inode_extref returned %08lx\n", Status);
3374c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3375c2c66affSColin Finck                         goto end;
3376c2c66affSColin Finck                     }
3377c2c66affSColin Finck                 } else if (tp2.item->key.obj_type == TYPE_EXTENT_DATA && !context->lastinode.deleting) {
3378c2c66affSColin Finck                     Status = send_extent_data(context, NULL, &tp2);
3379c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3380194ea909SVictor Perevertkin                         ERR("send_extent_data returned %08lx\n", Status);
3381c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3382c2c66affSColin Finck                         goto end;
3383c2c66affSColin Finck                     }
3384c2c66affSColin Finck 
3385c2c66affSColin Finck                     if (context->send->cancelling) {
3386c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3387c2c66affSColin Finck                         goto end;
3388c2c66affSColin Finck                     }
3389c2c66affSColin Finck                 } else if (tp2.item->key.obj_type == TYPE_XATTR_ITEM && !context->lastinode.deleting) {
3390c2c66affSColin Finck                     Status = send_xattr(context, NULL, &tp2);
3391c2c66affSColin Finck                     if (!NT_SUCCESS(Status)) {
3392194ea909SVictor Perevertkin                         ERR("send_xattr returned %08lx\n", Status);
3393c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3394c2c66affSColin Finck                         goto end;
3395c2c66affSColin Finck                     }
3396c2c66affSColin Finck 
3397c2c66affSColin Finck                     if (context->send->cancelling) {
3398c2c66affSColin Finck                         ExReleaseResourceLite(&context->Vcb->tree_lock);
3399c2c66affSColin Finck                         goto end;
3400c2c66affSColin Finck                     }
3401c2c66affSColin Finck                 }
3402c2c66affSColin Finck 
3403318da0c1SPierre Schweitzer                 if (find_next_item(context->Vcb, &tp2, &next_tp, false, NULL))
3404c2c66affSColin Finck                     tp2 = next_tp;
3405c2c66affSColin Finck                 else
3406318da0c1SPierre Schweitzer                     ended2 = true;
3407c2c66affSColin Finck             }
3408c2c66affSColin Finck         } while (!ended1 || !ended2);
3409c2c66affSColin Finck     } else {
3410c2c66affSColin Finck         do {
3411c2c66affSColin Finck             traverse_ptr next_tp;
3412c2c66affSColin Finck 
3413c2c66affSColin Finck             if (context->datalen > SEND_BUFFER_LENGTH) {
3414c2c66affSColin Finck                 KEY key = tp.item->key;
3415c2c66affSColin Finck 
3416c2c66affSColin Finck                 ExReleaseResourceLite(&context->Vcb->tree_lock);
3417c2c66affSColin Finck 
3418c2c66affSColin Finck                 KeClearEvent(&context->send->cleared_event);
3419318da0c1SPierre Schweitzer                 KeSetEvent(&context->buffer_event, 0, true);
3420318da0c1SPierre Schweitzer                 KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, false, NULL);
3421c2c66affSColin Finck 
3422c2c66affSColin Finck                 if (context->send->cancelling)
3423c2c66affSColin Finck                     goto end;
3424c2c66affSColin Finck 
3425318da0c1SPierre Schweitzer                 ExAcquireResourceSharedLite(&context->Vcb->tree_lock, true);
3426c2c66affSColin Finck 
3427318da0c1SPierre Schweitzer                 Status = find_item(context->Vcb, context->root, &tp, &key, false, NULL);
3428c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
3429194ea909SVictor Perevertkin                     ERR("find_item returned %08lx\n", Status);
3430c2c66affSColin Finck                     ExReleaseResourceLite(&context->Vcb->tree_lock);
3431c2c66affSColin Finck                     goto end;
3432c2c66affSColin Finck                 }
3433c2c66affSColin Finck 
3434c2c66affSColin Finck                 if (keycmp(tp.item->key, key)) {
3435c2c66affSColin Finck                     ERR("readonly subvolume changed\n");
3436c2c66affSColin Finck                     ExReleaseResourceLite(&context->Vcb->tree_lock);
3437c2c66affSColin Finck                     Status = STATUS_INTERNAL_ERROR;
3438c2c66affSColin Finck                     goto end;
3439c2c66affSColin Finck                 }
3440c2c66affSColin Finck             }
3441c2c66affSColin Finck 
3442c2c66affSColin Finck             if (context->lastinode.inode != 0 && tp.item->key.obj_id > context->lastinode.inode) {
3443c2c66affSColin Finck                 Status = finish_inode(context, &tp, NULL);
3444c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
3445194ea909SVictor Perevertkin                     ERR("finish_inode returned %08lx\n", Status);
3446c2c66affSColin Finck                     ExReleaseResourceLite(&context->Vcb->tree_lock);
3447c2c66affSColin Finck                     goto end;
3448c2c66affSColin Finck                 }
3449c2c66affSColin Finck 
3450c2c66affSColin Finck                 if (context->send->cancelling) {
3451c2c66affSColin Finck                     ExReleaseResourceLite(&context->Vcb->tree_lock);
3452c2c66affSColin Finck                     goto end;
3453c2c66affSColin Finck                 }
3454c2c66affSColin Finck             }
3455c2c66affSColin Finck 
3456c2c66affSColin Finck             if (tp.item->key.obj_type == TYPE_INODE_ITEM) {
3457c2c66affSColin Finck                 Status = send_inode(context, &tp, NULL);
3458c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
3459194ea909SVictor Perevertkin                     ERR("send_inode returned %08lx\n", Status);
3460c2c66affSColin Finck                     ExReleaseResourceLite(&context->Vcb->tree_lock);
3461c2c66affSColin Finck                     goto end;
3462c2c66affSColin Finck                 }
3463c2c66affSColin Finck             } else if (tp.item->key.obj_type == TYPE_INODE_REF) {
3464318da0c1SPierre Schweitzer                 Status = send_inode_ref(context, &tp, false);
3465c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
3466194ea909SVictor Perevertkin                     ERR("send_inode_ref returned %08lx\n", Status);
3467c2c66affSColin Finck                     ExReleaseResourceLite(&context->Vcb->tree_lock);
3468c2c66affSColin Finck                     goto end;
3469c2c66affSColin Finck                 }
3470c2c66affSColin Finck             } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) {
3471318da0c1SPierre Schweitzer                 Status = send_inode_extref(context, &tp, false);
3472c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
3473194ea909SVictor Perevertkin                     ERR("send_inode_extref returned %08lx\n", Status);
3474c2c66affSColin Finck                     ExReleaseResourceLite(&context->Vcb->tree_lock);
3475c2c66affSColin Finck                     goto end;
3476c2c66affSColin Finck                 }
3477c2c66affSColin Finck             } else if (tp.item->key.obj_type == TYPE_EXTENT_DATA) {
3478c2c66affSColin Finck                 Status = send_extent_data(context, &tp, NULL);
3479c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
3480194ea909SVictor Perevertkin                     ERR("send_extent_data returned %08lx\n", Status);
3481c2c66affSColin Finck                     ExReleaseResourceLite(&context->Vcb->tree_lock);
3482c2c66affSColin Finck                     goto end;
3483c2c66affSColin Finck                 }
3484c2c66affSColin Finck 
3485c2c66affSColin Finck                 if (context->send->cancelling) {
3486c2c66affSColin Finck                     ExReleaseResourceLite(&context->Vcb->tree_lock);
3487c2c66affSColin Finck                     goto end;
3488c2c66affSColin Finck                 }
3489c2c66affSColin Finck             } else if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
3490c2c66affSColin Finck                 Status = send_xattr(context, &tp, NULL);
3491c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
3492194ea909SVictor Perevertkin                     ERR("send_xattr returned %08lx\n", Status);
3493c2c66affSColin Finck                     ExReleaseResourceLite(&context->Vcb->tree_lock);
3494c2c66affSColin Finck                     goto end;
3495c2c66affSColin Finck                 }
3496c2c66affSColin Finck 
3497c2c66affSColin Finck                 if (context->send->cancelling) {
3498c2c66affSColin Finck                     ExReleaseResourceLite(&context->Vcb->tree_lock);
3499c2c66affSColin Finck                     goto end;
3500c2c66affSColin Finck                 }
3501c2c66affSColin Finck             }
3502c2c66affSColin Finck 
3503318da0c1SPierre Schweitzer             if (find_next_item(context->Vcb, &tp, &next_tp, false, NULL))
3504c2c66affSColin Finck                 tp = next_tp;
3505c2c66affSColin Finck             else
3506c2c66affSColin Finck                 break;
3507318da0c1SPierre Schweitzer         } while (true);
3508c2c66affSColin Finck     }
3509c2c66affSColin Finck 
3510c2c66affSColin Finck     if (context->lastinode.inode != 0) {
3511c2c66affSColin Finck         Status = finish_inode(context, NULL, NULL);
3512c2c66affSColin Finck         if (!NT_SUCCESS(Status)) {
3513194ea909SVictor Perevertkin             ERR("finish_inode returned %08lx\n", Status);
3514c2c66affSColin Finck             ExReleaseResourceLite(&context->Vcb->tree_lock);
3515c2c66affSColin Finck             goto end;
3516c2c66affSColin Finck         }
3517c2c66affSColin Finck 
3518c2c66affSColin Finck         ExReleaseResourceLite(&context->Vcb->tree_lock);
3519c2c66affSColin Finck 
3520c2c66affSColin Finck         if (context->send->cancelling)
3521c2c66affSColin Finck             goto end;
3522c2c66affSColin Finck     } else
3523c2c66affSColin Finck         ExReleaseResourceLite(&context->Vcb->tree_lock);
3524c2c66affSColin Finck 
3525c2c66affSColin Finck     KeClearEvent(&context->send->cleared_event);
3526318da0c1SPierre Schweitzer     KeSetEvent(&context->buffer_event, 0, true);
3527318da0c1SPierre Schweitzer     KeWaitForSingleObject(&context->send->cleared_event, Executive, KernelMode, false, NULL);
3528c2c66affSColin Finck 
3529c2c66affSColin Finck     Status = STATUS_SUCCESS;
3530c2c66affSColin Finck 
3531c2c66affSColin Finck end:
3532c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
3533318da0c1SPierre Schweitzer         KeSetEvent(&context->buffer_event, 0, false);
3534c2c66affSColin Finck 
3535c2c66affSColin Finck         if (context->send->ccb)
3536c2c66affSColin Finck             context->send->ccb->send_status = Status;
3537c2c66affSColin Finck     }
3538c2c66affSColin Finck 
3539318da0c1SPierre Schweitzer     ExAcquireResourceExclusiveLite(&context->Vcb->send_load_lock, true);
3540c2c66affSColin Finck 
3541c2c66affSColin Finck     while (!IsListEmpty(&context->orphans)) {
3542c2c66affSColin Finck         orphan* o = CONTAINING_RECORD(RemoveHeadList(&context->orphans), orphan, list_entry);
3543c2c66affSColin Finck         ExFreePool(o);
3544c2c66affSColin Finck     }
3545c2c66affSColin Finck 
3546c2c66affSColin Finck     while (!IsListEmpty(&context->dirs)) {
3547c2c66affSColin Finck         send_dir* sd = CONTAINING_RECORD(RemoveHeadList(&context->dirs), send_dir, list_entry);
3548c2c66affSColin Finck 
3549c2c66affSColin Finck         if (sd->name)
3550c2c66affSColin Finck             ExFreePool(sd->name);
3551c2c66affSColin Finck 
3552c2c66affSColin Finck         while (!IsListEmpty(&sd->deleted_children)) {
3553c2c66affSColin Finck             deleted_child* dc = CONTAINING_RECORD(RemoveHeadList(&sd->deleted_children), deleted_child, list_entry);
3554c2c66affSColin Finck             ExFreePool(dc);
3555c2c66affSColin Finck         }
3556c2c66affSColin Finck 
3557c2c66affSColin Finck         ExFreePool(sd);
3558c2c66affSColin Finck     }
3559c2c66affSColin Finck 
3560c2c66affSColin Finck     ZwClose(context->send->thread);
3561c2c66affSColin Finck     context->send->thread = NULL;
3562c2c66affSColin Finck 
3563c2c66affSColin Finck     if (context->send->ccb)
3564c2c66affSColin Finck         context->send->ccb->send = NULL;
3565c2c66affSColin Finck 
3566c2c66affSColin Finck     RemoveEntryList(&context->send->list_entry);
3567c2c66affSColin Finck     ExFreePool(context->send);
3568c2c66affSColin Finck     ExFreePool(context->data);
3569c2c66affSColin Finck 
3570c2c66affSColin Finck     InterlockedDecrement(&context->Vcb->running_sends);
3571c2c66affSColin Finck     InterlockedDecrement(&context->root->send_ops);
3572c2c66affSColin Finck 
3573c2c66affSColin Finck     if (context->parent)
3574c2c66affSColin Finck         InterlockedDecrement(&context->parent->send_ops);
3575c2c66affSColin Finck 
3576c2c66affSColin Finck     ExReleaseResourceLite(&context->Vcb->send_load_lock);
3577c2c66affSColin Finck 
3578c2c66affSColin Finck     if (context->clones) {
3579c2c66affSColin Finck         ULONG i;
3580c2c66affSColin Finck 
3581c2c66affSColin Finck         for (i = 0; i < context->num_clones; i++) {
3582c2c66affSColin Finck             InterlockedDecrement(&context->clones[i]->send_ops);
3583c2c66affSColin Finck         }
3584c2c66affSColin Finck 
3585c2c66affSColin Finck         ExFreePool(context->clones);
3586c2c66affSColin Finck     }
3587c2c66affSColin Finck 
3588c2c66affSColin Finck     ExFreePool(context);
3589c2c66affSColin Finck 
3590c2c66affSColin Finck     PsTerminateSystemThread(STATUS_SUCCESS);
3591c2c66affSColin Finck }
3592c2c66affSColin Finck 
send_subvol(device_extension * Vcb,void * data,ULONG datalen,PFILE_OBJECT FileObject,PIRP Irp)3593c2c66affSColin Finck NTSTATUS send_subvol(device_extension* Vcb, void* data, ULONG datalen, PFILE_OBJECT FileObject, PIRP Irp) {
3594c2c66affSColin Finck     NTSTATUS Status;
3595c2c66affSColin Finck     fcb* fcb;
3596c2c66affSColin Finck     ccb* ccb;
3597c2c66affSColin Finck     root* parsubvol = NULL;
3598c2c66affSColin Finck     send_context* context;
3599c2c66affSColin Finck     send_info* send;
3600c2c66affSColin Finck     ULONG num_clones = 0;
3601c2c66affSColin Finck     root** clones = NULL;
360262e630deSPierre Schweitzer     OBJECT_ATTRIBUTES oa;
3603c2c66affSColin Finck 
3604c2c66affSColin Finck     if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb)
3605c2c66affSColin Finck         return STATUS_INVALID_PARAMETER;
3606c2c66affSColin Finck 
3607c2c66affSColin Finck     if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode))
3608c2c66affSColin Finck         return STATUS_PRIVILEGE_NOT_HELD;
3609c2c66affSColin Finck 
3610c2c66affSColin Finck     fcb = FileObject->FsContext;
3611c2c66affSColin Finck     ccb = FileObject->FsContext2;
3612c2c66affSColin Finck 
3613c2c66affSColin Finck     if (fcb->inode != SUBVOL_ROOT_INODE || fcb == Vcb->root_fileref->fcb)
3614c2c66affSColin Finck         return STATUS_INVALID_PARAMETER;
3615c2c66affSColin Finck 
3616c2c66affSColin Finck     if (!Vcb->readonly && !(fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY))
3617c2c66affSColin Finck         return STATUS_INVALID_PARAMETER;
3618c2c66affSColin Finck 
3619c2c66affSColin Finck     if (data) {
3620c2c66affSColin Finck         btrfs_send_subvol* bss = (btrfs_send_subvol*)data;
3621c2c66affSColin Finck         HANDLE parent;
3622c2c66affSColin Finck 
3623c2c66affSColin Finck #if defined(_WIN64)
3624c2c66affSColin Finck         if (IoIs32bitProcess(Irp)) {
3625c2c66affSColin Finck             btrfs_send_subvol32* bss32 = (btrfs_send_subvol32*)data;
3626c2c66affSColin Finck 
3627c2c66affSColin Finck             if (datalen < offsetof(btrfs_send_subvol32, num_clones))
3628c2c66affSColin Finck                 return STATUS_INVALID_PARAMETER;
3629c2c66affSColin Finck 
3630c2c66affSColin Finck             parent = Handle32ToHandle(bss32->parent);
3631c2c66affSColin Finck 
3632c2c66affSColin Finck             if (datalen >= offsetof(btrfs_send_subvol32, clones[0]))
3633c2c66affSColin Finck                 num_clones = bss32->num_clones;
3634c2c66affSColin Finck 
3635318da0c1SPierre Schweitzer             if (datalen < offsetof(btrfs_send_subvol32, clones[0]) + (num_clones * sizeof(uint32_t)))
3636c2c66affSColin Finck                 return STATUS_INVALID_PARAMETER;
3637c2c66affSColin Finck         } else {
3638c2c66affSColin Finck #endif
3639c2c66affSColin Finck             if (datalen < offsetof(btrfs_send_subvol, num_clones))
3640c2c66affSColin Finck                 return STATUS_INVALID_PARAMETER;
3641c2c66affSColin Finck 
3642c2c66affSColin Finck             parent = bss->parent;
3643c2c66affSColin Finck 
3644c2c66affSColin Finck             if (datalen >= offsetof(btrfs_send_subvol, clones[0]))
3645c2c66affSColin Finck                 num_clones = bss->num_clones;
3646c2c66affSColin Finck 
3647c2c66affSColin Finck             if (datalen < offsetof(btrfs_send_subvol, clones[0]) + (num_clones * sizeof(HANDLE)))
3648c2c66affSColin Finck                 return STATUS_INVALID_PARAMETER;
3649c2c66affSColin Finck #if defined(_WIN64)
3650c2c66affSColin Finck         }
3651c2c66affSColin Finck #endif
3652c2c66affSColin Finck 
3653c2c66affSColin Finck         if (parent) {
3654c2c66affSColin Finck             PFILE_OBJECT fileobj;
3655c2c66affSColin Finck             struct _fcb* parfcb;
3656c2c66affSColin Finck 
3657c2c66affSColin Finck             Status = ObReferenceObjectByHandle(parent, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
3658c2c66affSColin Finck             if (!NT_SUCCESS(Status)) {
3659194ea909SVictor Perevertkin                 ERR("ObReferenceObjectByHandle returned %08lx\n", Status);
3660c2c66affSColin Finck                 return Status;
3661c2c66affSColin Finck             }
3662c2c66affSColin Finck 
3663c2c66affSColin Finck             if (fileobj->DeviceObject != FileObject->DeviceObject) {
3664c2c66affSColin Finck                 ObDereferenceObject(fileobj);
3665c2c66affSColin Finck                 return STATUS_INVALID_PARAMETER;
3666c2c66affSColin Finck             }
3667c2c66affSColin Finck 
3668c2c66affSColin Finck             parfcb = fileobj->FsContext;
3669c2c66affSColin Finck 
3670c2c66affSColin Finck             if (!parfcb || parfcb == Vcb->root_fileref->fcb || parfcb == Vcb->volume_fcb || parfcb->inode != SUBVOL_ROOT_INODE) {
3671c2c66affSColin Finck                 ObDereferenceObject(fileobj);
3672c2c66affSColin Finck                 return STATUS_INVALID_PARAMETER;
3673c2c66affSColin Finck             }
3674c2c66affSColin Finck 
3675c2c66affSColin Finck             parsubvol = parfcb->subvol;
3676c2c66affSColin Finck             ObDereferenceObject(fileobj);
3677c2c66affSColin Finck 
3678c2c66affSColin Finck             if (!Vcb->readonly && !(parsubvol->root_item.flags & BTRFS_SUBVOL_READONLY))
3679c2c66affSColin Finck                 return STATUS_INVALID_PARAMETER;
3680c2c66affSColin Finck 
3681c2c66affSColin Finck             if (parsubvol == fcb->subvol)
3682c2c66affSColin Finck                 return STATUS_INVALID_PARAMETER;
3683c2c66affSColin Finck         }
3684c2c66affSColin Finck 
3685c2c66affSColin Finck         if (num_clones > 0) {
3686c2c66affSColin Finck             ULONG i;
3687c2c66affSColin Finck 
3688c2c66affSColin Finck             clones = ExAllocatePoolWithTag(PagedPool, sizeof(root*) * num_clones, ALLOC_TAG);
3689c2c66affSColin Finck             if (!clones) {
3690c2c66affSColin Finck                 ERR("out of memory\n");
3691c2c66affSColin Finck                 return STATUS_INSUFFICIENT_RESOURCES;
3692c2c66affSColin Finck             }
3693c2c66affSColin Finck 
3694c2c66affSColin Finck             for (i = 0; i < num_clones; i++) {
3695c2c66affSColin Finck                 HANDLE h;
3696c2c66affSColin Finck                 PFILE_OBJECT fileobj;
3697c2c66affSColin Finck                 struct _fcb* clonefcb;
3698c2c66affSColin Finck 
3699c2c66affSColin Finck #if defined(_WIN64)
3700c2c66affSColin Finck                 if (IoIs32bitProcess(Irp)) {
3701c2c66affSColin Finck                     btrfs_send_subvol32* bss32 = (btrfs_send_subvol32*)data;
3702c2c66affSColin Finck 
3703c2c66affSColin Finck                     h = Handle32ToHandle(bss32->clones[i]);
3704c2c66affSColin Finck                 } else
3705c2c66affSColin Finck #endif
3706c2c66affSColin Finck                     h = bss->clones[i];
3707c2c66affSColin Finck 
3708c2c66affSColin Finck                 Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL);
3709c2c66affSColin Finck                 if (!NT_SUCCESS(Status)) {
3710194ea909SVictor Perevertkin                     ERR("ObReferenceObjectByHandle returned %08lx\n", Status);
3711c2c66affSColin Finck                     ExFreePool(clones);
3712c2c66affSColin Finck                     return Status;
3713c2c66affSColin Finck                 }
3714c2c66affSColin Finck 
3715c2c66affSColin Finck                 if (fileobj->DeviceObject != FileObject->DeviceObject) {
3716c2c66affSColin Finck                     ObDereferenceObject(fileobj);
3717c2c66affSColin Finck                     ExFreePool(clones);
3718c2c66affSColin Finck                     return STATUS_INVALID_PARAMETER;
3719c2c66affSColin Finck                 }
3720c2c66affSColin Finck 
3721c2c66affSColin Finck                 clonefcb = fileobj->FsContext;
3722c2c66affSColin Finck 
3723c2c66affSColin Finck                 if (!clonefcb || clonefcb == Vcb->root_fileref->fcb || clonefcb == Vcb->volume_fcb || clonefcb->inode != SUBVOL_ROOT_INODE) {
3724c2c66affSColin Finck                     ObDereferenceObject(fileobj);
3725c2c66affSColin Finck                     ExFreePool(clones);
3726c2c66affSColin Finck                     return STATUS_INVALID_PARAMETER;
3727c2c66affSColin Finck                 }
3728c2c66affSColin Finck 
3729c2c66affSColin Finck                 clones[i] = clonefcb->subvol;
3730c2c66affSColin Finck                 ObDereferenceObject(fileobj);
3731c2c66affSColin Finck 
3732c2c66affSColin Finck                 if (!Vcb->readonly && !(clones[i]->root_item.flags & BTRFS_SUBVOL_READONLY)) {
3733c2c66affSColin Finck                     ExFreePool(clones);
3734c2c66affSColin Finck                     return STATUS_INVALID_PARAMETER;
3735c2c66affSColin Finck                 }
3736c2c66affSColin Finck             }
3737c2c66affSColin Finck         }
3738c2c66affSColin Finck     }
3739c2c66affSColin Finck 
3740318da0c1SPierre Schweitzer     ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, true);
3741c2c66affSColin Finck 
3742c2c66affSColin Finck     if (ccb->send) {
3743c2c66affSColin Finck         WARN("send operation already running\n");
3744c2c66affSColin Finck         ExReleaseResourceLite(&Vcb->send_load_lock);
3745c2c66affSColin Finck         return STATUS_DEVICE_NOT_READY;
3746c2c66affSColin Finck     }
3747c2c66affSColin Finck 
3748c2c66affSColin Finck     context = ExAllocatePoolWithTag(NonPagedPool, sizeof(send_context), ALLOC_TAG);
3749c2c66affSColin Finck     if (!context) {
3750c2c66affSColin Finck         ERR("out of memory\n");
3751eb7fbc25SPierre Schweitzer 
3752eb7fbc25SPierre Schweitzer         if (clones)
3753eb7fbc25SPierre Schweitzer             ExFreePool(clones);
3754eb7fbc25SPierre Schweitzer 
3755c2c66affSColin Finck         ExReleaseResourceLite(&Vcb->send_load_lock);
3756c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
3757c2c66affSColin Finck     }
3758c2c66affSColin Finck 
3759c2c66affSColin Finck     context->Vcb = Vcb;
3760c2c66affSColin Finck     context->root = fcb->subvol;
3761c2c66affSColin Finck     context->parent = parsubvol;
3762c2c66affSColin Finck     InitializeListHead(&context->orphans);
3763c2c66affSColin Finck     InitializeListHead(&context->dirs);
3764c2c66affSColin Finck     InitializeListHead(&context->pending_rmdirs);
3765c2c66affSColin Finck     context->lastinode.inode = 0;
3766c2c66affSColin Finck     context->lastinode.path = NULL;
3767c2c66affSColin Finck     context->lastinode.sd = NULL;
3768c2c66affSColin Finck     context->root_dir = NULL;
3769c2c66affSColin Finck     context->num_clones = num_clones;
3770c2c66affSColin Finck     context->clones = clones;
3771c2c66affSColin Finck     InitializeListHead(&context->lastinode.refs);
3772c2c66affSColin Finck     InitializeListHead(&context->lastinode.oldrefs);
3773c2c66affSColin Finck     InitializeListHead(&context->lastinode.exts);
3774c2c66affSColin Finck     InitializeListHead(&context->lastinode.oldexts);
3775c2c66affSColin Finck 
3776c2c66affSColin Finck     context->data = ExAllocatePoolWithTag(PagedPool, SEND_BUFFER_LENGTH + (2 * MAX_SEND_WRITE), ALLOC_TAG); // give ourselves some wiggle room
3777c2c66affSColin Finck     if (!context->data) {
3778c2c66affSColin Finck         ExFreePool(context);
3779c2c66affSColin Finck         ExReleaseResourceLite(&Vcb->send_load_lock);
3780c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
3781c2c66affSColin Finck     }
3782c2c66affSColin Finck 
3783c2c66affSColin Finck     context->datalen = 0;
3784c2c66affSColin Finck 
3785c2c66affSColin Finck     send_subvol_header(context, fcb->subvol, ccb->fileref); // FIXME - fileref needs some sort of lock here
3786c2c66affSColin Finck 
3787318da0c1SPierre Schweitzer     KeInitializeEvent(&context->buffer_event, NotificationEvent, false);
3788c2c66affSColin Finck 
3789c2c66affSColin Finck     send = ExAllocatePoolWithTag(NonPagedPool, sizeof(send_info), ALLOC_TAG);
3790c2c66affSColin Finck     if (!send) {
3791c2c66affSColin Finck         ERR("out of memory\n");
3792c2c66affSColin Finck         ExFreePool(context->data);
3793c2c66affSColin Finck         ExFreePool(context);
3794eb7fbc25SPierre Schweitzer 
3795eb7fbc25SPierre Schweitzer         if (clones)
3796eb7fbc25SPierre Schweitzer             ExFreePool(clones);
3797eb7fbc25SPierre Schweitzer 
3798c2c66affSColin Finck         ExReleaseResourceLite(&Vcb->send_load_lock);
3799c2c66affSColin Finck         return STATUS_INSUFFICIENT_RESOURCES;
3800c2c66affSColin Finck     }
3801c2c66affSColin Finck 
3802318da0c1SPierre Schweitzer     KeInitializeEvent(&send->cleared_event, NotificationEvent, false);
3803c2c66affSColin Finck 
3804c2c66affSColin Finck     send->context = context;
3805c2c66affSColin Finck     context->send = send;
3806c2c66affSColin Finck 
3807c2c66affSColin Finck     ccb->send = send;
3808c2c66affSColin Finck     send->ccb = ccb;
3809c2c66affSColin Finck     ccb->send_status = STATUS_SUCCESS;
3810c2c66affSColin Finck 
3811318da0c1SPierre Schweitzer     send->cancelling = false;
3812c2c66affSColin Finck 
3813c2c66affSColin Finck     InterlockedIncrement(&Vcb->running_sends);
3814c2c66affSColin Finck 
381562e630deSPierre Schweitzer     InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
381662e630deSPierre Schweitzer 
381762e630deSPierre Schweitzer     Status = PsCreateSystemThread(&send->thread, 0, &oa, NULL, NULL, send_thread, context);
3818c2c66affSColin Finck     if (!NT_SUCCESS(Status)) {
3819194ea909SVictor Perevertkin         ERR("PsCreateSystemThread returned %08lx\n", Status);
3820c2c66affSColin Finck         ccb->send = NULL;
3821c2c66affSColin Finck         InterlockedDecrement(&Vcb->running_sends);
3822c2c66affSColin Finck         ExFreePool(send);
3823c2c66affSColin Finck         ExFreePool(context->data);
3824c2c66affSColin Finck         ExFreePool(context);
3825eb7fbc25SPierre Schweitzer 
3826eb7fbc25SPierre Schweitzer         if (clones)
3827eb7fbc25SPierre Schweitzer             ExFreePool(clones);
3828eb7fbc25SPierre Schweitzer 
3829c2c66affSColin Finck         ExReleaseResourceLite(&Vcb->send_load_lock);
3830c2c66affSColin Finck         return Status;
3831c2c66affSColin Finck     }
3832c2c66affSColin Finck 
3833c2c66affSColin Finck     InsertTailList(&Vcb->send_ops, &send->list_entry);
3834c2c66affSColin Finck     ExReleaseResourceLite(&Vcb->send_load_lock);
3835c2c66affSColin Finck 
3836c2c66affSColin Finck     return STATUS_SUCCESS;
3837c2c66affSColin Finck }
3838c2c66affSColin Finck 
read_send_buffer(device_extension * Vcb,PFILE_OBJECT FileObject,void * data,ULONG datalen,ULONG_PTR * retlen,KPROCESSOR_MODE processor_mode)3839c2c66affSColin Finck NTSTATUS read_send_buffer(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, ULONG_PTR* retlen, KPROCESSOR_MODE processor_mode) {
3840c2c66affSColin Finck     ccb* ccb;
3841c2c66affSColin Finck     send_context* context;
3842c2c66affSColin Finck 
3843c2c66affSColin Finck     ccb = FileObject ? FileObject->FsContext2 : NULL;
3844c2c66affSColin Finck     if (!ccb)
3845c2c66affSColin Finck         return STATUS_INVALID_PARAMETER;
3846c2c66affSColin Finck 
3847c2c66affSColin Finck     if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
3848c2c66affSColin Finck         return STATUS_PRIVILEGE_NOT_HELD;
3849c2c66affSColin Finck 
3850318da0c1SPierre Schweitzer     ExAcquireResourceExclusiveLite(&Vcb->send_load_lock, true);
3851c2c66affSColin Finck 
3852c2c66affSColin Finck     if (!ccb->send) {
3853c2c66affSColin Finck         ExReleaseResourceLite(&Vcb->send_load_lock);
3854c2c66affSColin Finck         return !NT_SUCCESS(ccb->send_status) ? ccb->send_status : STATUS_END_OF_FILE;
3855c2c66affSColin Finck     }
3856c2c66affSColin Finck 
3857c2c66affSColin Finck     context = (send_context*)ccb->send->context;
3858c2c66affSColin Finck 
3859318da0c1SPierre Schweitzer     KeWaitForSingleObject(&context->buffer_event, Executive, KernelMode, false, NULL);
3860c2c66affSColin Finck 
3861c2c66affSColin Finck     if (datalen == 0) {
3862c2c66affSColin Finck         ExReleaseResourceLite(&Vcb->send_load_lock);
3863c2c66affSColin Finck         return STATUS_SUCCESS;
3864c2c66affSColin Finck     }
3865c2c66affSColin Finck 
3866c2c66affSColin Finck     RtlCopyMemory(data, context->data, min(datalen, context->datalen));
3867c2c66affSColin Finck 
3868c2c66affSColin Finck     if (datalen < context->datalen) { // not empty yet
3869c2c66affSColin Finck         *retlen = datalen;
3870c2c66affSColin Finck         RtlMoveMemory(context->data, &context->data[datalen], context->datalen - datalen);
3871c2c66affSColin Finck         context->datalen -= datalen;
3872c2c66affSColin Finck         ExReleaseResourceLite(&Vcb->send_load_lock);
3873c2c66affSColin Finck     } else {
3874c2c66affSColin Finck         *retlen = context->datalen;
3875c2c66affSColin Finck         context->datalen = 0;
3876c2c66affSColin Finck         ExReleaseResourceLite(&Vcb->send_load_lock);
3877c2c66affSColin Finck 
3878c2c66affSColin Finck         KeClearEvent(&context->buffer_event);
3879318da0c1SPierre Schweitzer         KeSetEvent(&ccb->send->cleared_event, 0, false);
3880c2c66affSColin Finck     }
3881c2c66affSColin Finck 
3882c2c66affSColin Finck     return STATUS_SUCCESS;
3883c2c66affSColin Finck }
3884