1 /* Copyright (c) Mark Harmstone 2016-17
2  *
3  * This file is part of WinBtrfs.
4  *
5  * WinBtrfs is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public Licence as published by
7  * the Free Software Foundation, either version 3 of the Licence, or
8  * (at your option) any later version.
9  *
10  * WinBtrfs is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public Licence for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public Licence
16  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
17 
18 #include "btrfs_drv.h"
19 #include "crc32c.h"
20 
21 typedef struct {
22     uint8_t type;
23 
24     union {
25         EXTENT_DATA_REF edr;
26         SHARED_DATA_REF sdr;
27         TREE_BLOCK_REF tbr;
28         SHARED_BLOCK_REF sbr;
29     };
30 
31     uint64_t hash;
32     LIST_ENTRY list_entry;
33 } extent_ref;
34 
35 uint64_t get_extent_data_ref_hash2(uint64_t root, uint64_t objid, uint64_t offset) {
36     uint32_t high_crc = 0xffffffff, low_crc = 0xffffffff;
37 
38     high_crc = calc_crc32c(high_crc, (uint8_t*)&root, sizeof(uint64_t));
39     low_crc = calc_crc32c(low_crc, (uint8_t*)&objid, sizeof(uint64_t));
40     low_crc = calc_crc32c(low_crc, (uint8_t*)&offset, sizeof(uint64_t));
41 
42     return ((uint64_t)high_crc << 31) ^ (uint64_t)low_crc;
43 }
44 
45 static __inline uint64_t get_extent_data_ref_hash(EXTENT_DATA_REF* edr) {
46     return get_extent_data_ref_hash2(edr->root, edr->objid, edr->offset);
47 }
48 
49 static uint64_t get_extent_hash(uint8_t type, void* data) {
50     if (type == TYPE_EXTENT_DATA_REF) {
51         return get_extent_data_ref_hash((EXTENT_DATA_REF*)data);
52     } else if (type == TYPE_SHARED_BLOCK_REF) {
53         SHARED_BLOCK_REF* sbr = (SHARED_BLOCK_REF*)data;
54         return sbr->offset;
55     } else if (type == TYPE_SHARED_DATA_REF) {
56         SHARED_DATA_REF* sdr = (SHARED_DATA_REF*)data;
57         return sdr->offset;
58     } else if (type == TYPE_TREE_BLOCK_REF) {
59         TREE_BLOCK_REF* tbr = (TREE_BLOCK_REF*)data;
60         return tbr->offset;
61     } else {
62         ERR("unhandled extent type %x\n", type);
63         return 0;
64     }
65 }
66 
67 static void free_extent_refs(LIST_ENTRY* extent_refs) {
68     while (!IsListEmpty(extent_refs)) {
69         LIST_ENTRY* le = RemoveHeadList(extent_refs);
70         extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
71 
72         ExFreePool(er);
73     }
74 }
75 
76 static NTSTATUS add_shared_data_extent_ref(LIST_ENTRY* extent_refs, uint64_t parent, uint32_t count) {
77     extent_ref* er2;
78     LIST_ENTRY* le;
79 
80     if (!IsListEmpty(extent_refs)) {
81         le = extent_refs->Flink;
82 
83         while (le != extent_refs) {
84             extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
85 
86             if (er->type == TYPE_SHARED_DATA_REF && er->sdr.offset == parent) {
87                 er->sdr.count += count;
88                 return STATUS_SUCCESS;
89             }
90 
91             le = le->Flink;
92         }
93     }
94 
95     er2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG);
96     if (!er2) {
97         ERR("out of memory\n");
98         return STATUS_INSUFFICIENT_RESOURCES;
99     }
100 
101     er2->type = TYPE_SHARED_DATA_REF;
102     er2->sdr.offset = parent;
103     er2->sdr.count = count;
104 
105     InsertTailList(extent_refs, &er2->list_entry);
106 
107     return STATUS_SUCCESS;
108 }
109 
110 static NTSTATUS add_shared_block_extent_ref(LIST_ENTRY* extent_refs, uint64_t parent) {
111     extent_ref* er2;
112     LIST_ENTRY* le;
113 
114     if (!IsListEmpty(extent_refs)) {
115         le = extent_refs->Flink;
116 
117         while (le != extent_refs) {
118             extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
119 
120             if (er->type == TYPE_SHARED_BLOCK_REF && er->sbr.offset == parent)
121                 return STATUS_SUCCESS;
122 
123             le = le->Flink;
124         }
125     }
126 
127     er2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG);
128     if (!er2) {
129         ERR("out of memory\n");
130         return STATUS_INSUFFICIENT_RESOURCES;
131     }
132 
133     er2->type = TYPE_SHARED_BLOCK_REF;
134     er2->sbr.offset = parent;
135 
136     InsertTailList(extent_refs, &er2->list_entry);
137 
138     return STATUS_SUCCESS;
139 }
140 
141 static NTSTATUS add_tree_block_extent_ref(LIST_ENTRY* extent_refs, uint64_t root) {
142     extent_ref* er2;
143     LIST_ENTRY* le;
144 
145     if (!IsListEmpty(extent_refs)) {
146         le = extent_refs->Flink;
147 
148         while (le != extent_refs) {
149             extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
150 
151             if (er->type == TYPE_TREE_BLOCK_REF && er->tbr.offset == root)
152                 return STATUS_SUCCESS;
153 
154             le = le->Flink;
155         }
156     }
157 
158     er2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent_ref), ALLOC_TAG);
159     if (!er2) {
160         ERR("out of memory\n");
161         return STATUS_INSUFFICIENT_RESOURCES;
162     }
163 
164     er2->type = TYPE_TREE_BLOCK_REF;
165     er2->tbr.offset = root;
166 
167     InsertTailList(extent_refs, &er2->list_entry);
168 
169     return STATUS_SUCCESS;
170 }
171 
172 static void sort_extent_refs(LIST_ENTRY* extent_refs) {
173     LIST_ENTRY newlist;
174 
175     if (IsListEmpty(extent_refs))
176         return;
177 
178     // insertion sort
179 
180     InitializeListHead(&newlist);
181 
182     while (!IsListEmpty(extent_refs)) {
183         extent_ref* er = CONTAINING_RECORD(RemoveHeadList(extent_refs), extent_ref, list_entry);
184         LIST_ENTRY* le;
185         bool inserted = false;
186 
187         le = newlist.Flink;
188         while (le != &newlist) {
189             extent_ref* er2 = CONTAINING_RECORD(le, extent_ref, list_entry);
190 
191             if (er->type < er2->type || (er->type == er2->type && er->hash > er2->hash)) {
192                 InsertHeadList(le->Blink, &er->list_entry);
193                 inserted = true;
194                 break;
195             }
196 
197             le = le->Flink;
198         }
199 
200         if (!inserted)
201             InsertTailList(&newlist, &er->list_entry);
202     }
203 
204     newlist.Flink->Blink = extent_refs;
205     newlist.Blink->Flink = extent_refs;
206     extent_refs->Flink = newlist.Flink;
207     extent_refs->Blink = newlist.Blink;
208 }
209 
210 static NTSTATUS construct_extent_item(device_extension* Vcb, uint64_t address, uint64_t size, uint64_t flags, LIST_ENTRY* extent_refs,
211                                       KEY* firstitem, uint8_t level, PIRP Irp) {
212     NTSTATUS Status;
213     LIST_ENTRY *le, *next_le;
214     uint64_t refcount;
215     uint16_t inline_len;
216     bool all_inline = true;
217     extent_ref* first_noninline = NULL;
218     EXTENT_ITEM* ei;
219     uint8_t* siptr;
220 
221     // FIXME - write skinny extents if is tree and incompat flag set
222 
223     if (IsListEmpty(extent_refs)) {
224         WARN("no extent refs found\n");
225         return STATUS_SUCCESS;
226     }
227 
228     refcount = 0;
229     inline_len = sizeof(EXTENT_ITEM);
230 
231     if (flags & EXTENT_ITEM_TREE_BLOCK)
232         inline_len += sizeof(EXTENT_ITEM2);
233 
234     le = extent_refs->Flink;
235     while (le != extent_refs) {
236         extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
237         uint64_t rc;
238 
239         next_le = le->Flink;
240 
241         rc = get_extent_data_refcount(er->type, &er->edr);
242 
243         if (rc == 0) {
244             RemoveEntryList(&er->list_entry);
245 
246             ExFreePool(er);
247         } else {
248             uint16_t extlen = get_extent_data_len(er->type);
249 
250             refcount += rc;
251 
252             er->hash = get_extent_hash(er->type, &er->edr);
253 
254             if (all_inline) {
255                 if ((uint16_t)(inline_len + 1 + extlen) > Vcb->superblock.node_size >> 2) {
256                     all_inline = false;
257                     first_noninline = er;
258                 } else
259                     inline_len += extlen + 1;
260             }
261         }
262 
263         le = next_le;
264     }
265 
266     ei = ExAllocatePoolWithTag(PagedPool, inline_len, ALLOC_TAG);
267     if (!ei) {
268         ERR("out of memory\n");
269         return STATUS_INSUFFICIENT_RESOURCES;
270     }
271 
272     ei->refcount = refcount;
273     ei->generation = Vcb->superblock.generation;
274     ei->flags = flags;
275 
276     if (flags & EXTENT_ITEM_TREE_BLOCK) {
277         EXTENT_ITEM2* ei2 = (EXTENT_ITEM2*)&ei[1];
278 
279         if (firstitem) {
280             ei2->firstitem.obj_id = firstitem->obj_id;
281             ei2->firstitem.obj_type = firstitem->obj_type;
282             ei2->firstitem.offset = firstitem->offset;
283         } else {
284             ei2->firstitem.obj_id = 0;
285             ei2->firstitem.obj_type = 0;
286             ei2->firstitem.offset = 0;
287         }
288 
289         ei2->level = level;
290 
291         siptr = (uint8_t*)&ei2[1];
292     } else
293         siptr = (uint8_t*)&ei[1];
294 
295     sort_extent_refs(extent_refs);
296 
297     le = extent_refs->Flink;
298     while (le != extent_refs) {
299         extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
300         ULONG extlen = get_extent_data_len(er->type);
301 
302         if (!all_inline && er == first_noninline)
303             break;
304 
305         *siptr = er->type;
306         siptr++;
307 
308         if (extlen > 0) {
309             RtlCopyMemory(siptr, &er->edr, extlen);
310             siptr += extlen;
311         }
312 
313         le = le->Flink;
314     }
315 
316     Status = insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, inline_len, NULL, Irp);
317     if (!NT_SUCCESS(Status)) {
318         ERR("insert_tree_item returned %08lx\n", Status);
319         ExFreePool(ei);
320         return Status;
321     }
322 
323     if (!all_inline) {
324         le = &first_noninline->list_entry;
325 
326         while (le != extent_refs) {
327             extent_ref* er = CONTAINING_RECORD(le, extent_ref, list_entry);
328             uint16_t len;
329             uint8_t* data;
330 
331             if (er->type == TYPE_EXTENT_DATA_REF) {
332                 len = sizeof(EXTENT_DATA_REF);
333 
334                 data = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
335 
336                 if (!data) {
337                     ERR("out of memory\n");
338                     return STATUS_INSUFFICIENT_RESOURCES;
339                 }
340 
341                 RtlCopyMemory(data, &er->edr, len);
342             } else if (er->type == TYPE_SHARED_DATA_REF) {
343                 len = sizeof(uint32_t);
344 
345                 data = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
346 
347                 if (!data) {
348                     ERR("out of memory\n");
349                     return STATUS_INSUFFICIENT_RESOURCES;
350                 }
351 
352                 *((uint32_t*)data) = er->sdr.count;
353             } else {
354                 len = 0;
355                 data = NULL;
356             }
357 
358             Status = insert_tree_item(Vcb, Vcb->extent_root, address, er->type, er->hash, data, len, NULL, Irp);
359             if (!NT_SUCCESS(Status)) {
360                 ERR("insert_tree_item returned %08lx\n", Status);
361                 if (data) ExFreePool(data);
362                 return Status;
363             }
364 
365             le = le->Flink;
366         }
367     }
368 
369     return STATUS_SUCCESS;
370 }
371 
372 static NTSTATUS convert_old_extent(device_extension* Vcb, uint64_t address, bool tree, KEY* firstitem, uint8_t level, PIRP Irp) {
373     NTSTATUS Status;
374     KEY searchkey;
375     traverse_ptr tp, next_tp;
376     LIST_ENTRY extent_refs;
377     uint64_t size;
378 
379     InitializeListHead(&extent_refs);
380 
381     searchkey.obj_id = address;
382     searchkey.obj_type = TYPE_EXTENT_ITEM;
383     searchkey.offset = 0xffffffffffffffff;
384 
385     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
386     if (!NT_SUCCESS(Status)) {
387         ERR("find_item returned %08lx\n", Status);
388         return Status;
389     }
390 
391     if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
392         ERR("old-style extent %I64x not found\n", address);
393         return STATUS_INTERNAL_ERROR;
394     }
395 
396     size = tp.item->key.offset;
397 
398     Status = delete_tree_item(Vcb, &tp);
399     if (!NT_SUCCESS(Status)) {
400         ERR("delete_tree_item returned %08lx\n", Status);
401         return Status;
402     }
403 
404     while (find_next_item(Vcb, &tp, &next_tp, false, Irp)) {
405         tp = next_tp;
406 
407         if (tp.item->key.obj_id == address && tp.item->key.obj_type == TYPE_EXTENT_REF_V0 && tp.item->size >= sizeof(EXTENT_REF_V0)) {
408             EXTENT_REF_V0* erv0 = (EXTENT_REF_V0*)tp.item->data;
409 
410             if (tree) {
411                 if (tp.item->key.offset == tp.item->key.obj_id) { // top of the tree
412                     Status = add_tree_block_extent_ref(&extent_refs, erv0->root);
413                     if (!NT_SUCCESS(Status)) {
414                         ERR("add_tree_block_extent_ref returned %08lx\n", Status);
415                         goto end;
416                     }
417                 } else {
418                     Status = add_shared_block_extent_ref(&extent_refs, tp.item->key.offset);
419                     if (!NT_SUCCESS(Status)) {
420                         ERR("add_shared_block_extent_ref returned %08lx\n", Status);
421                         goto end;
422                     }
423                 }
424             } else {
425                 Status = add_shared_data_extent_ref(&extent_refs, tp.item->key.offset, erv0->count);
426                 if (!NT_SUCCESS(Status)) {
427                     ERR("add_shared_data_extent_ref returned %08lx\n", Status);
428                     goto end;
429                 }
430             }
431 
432             Status = delete_tree_item(Vcb, &tp);
433             if (!NT_SUCCESS(Status)) {
434                 ERR("delete_tree_item returned %08lx\n", Status);
435                 goto end;
436             }
437         }
438 
439         if (tp.item->key.obj_id > address || tp.item->key.obj_type > TYPE_EXTENT_REF_V0)
440             break;
441     }
442 
443     Status = construct_extent_item(Vcb, address, size, tree ? (EXTENT_ITEM_TREE_BLOCK | EXTENT_ITEM_SHARED_BACKREFS) : EXTENT_ITEM_DATA,
444                                    &extent_refs, firstitem, level, Irp);
445     if (!NT_SUCCESS(Status))
446         ERR("construct_extent_item returned %08lx\n", Status);
447 
448 end:
449     free_extent_refs(&extent_refs);
450 
451     return Status;
452 }
453 
454 NTSTATUS increase_extent_refcount(device_extension* Vcb, uint64_t address, uint64_t size, uint8_t type, void* data, KEY* firstitem, uint8_t level, PIRP Irp) {
455     NTSTATUS Status;
456     KEY searchkey;
457     traverse_ptr tp;
458     ULONG len, max_extent_item_size;
459     uint16_t datalen = get_extent_data_len(type);
460     EXTENT_ITEM* ei;
461     uint8_t* ptr;
462     uint64_t inline_rc, offset;
463     uint8_t* data2;
464     EXTENT_ITEM* newei;
465     bool skinny;
466     bool is_tree = type == TYPE_TREE_BLOCK_REF || type == TYPE_SHARED_BLOCK_REF;
467 
468     if (datalen == 0) {
469         ERR("unrecognized extent type %x\n", type);
470         return STATUS_INTERNAL_ERROR;
471     }
472 
473     searchkey.obj_id = address;
474     searchkey.obj_type = Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA ? TYPE_METADATA_ITEM : TYPE_EXTENT_ITEM;
475     searchkey.offset = 0xffffffffffffffff;
476 
477     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
478     if (!NT_SUCCESS(Status)) {
479         ERR("error - find_item returned %08lx\n", Status);
480         return Status;
481     }
482 
483     // If entry doesn't exist yet, create new inline extent item
484 
485     if (tp.item->key.obj_id != searchkey.obj_id || (tp.item->key.obj_type != TYPE_EXTENT_ITEM && tp.item->key.obj_type != TYPE_METADATA_ITEM)) {
486         uint16_t eisize;
487 
488         eisize = sizeof(EXTENT_ITEM);
489         if (is_tree && !(Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)) eisize += sizeof(EXTENT_ITEM2);
490         eisize += sizeof(uint8_t);
491         eisize += datalen;
492 
493         ei = ExAllocatePoolWithTag(PagedPool, eisize, ALLOC_TAG);
494         if (!ei) {
495             ERR("out of memory\n");
496             return STATUS_INSUFFICIENT_RESOURCES;
497         }
498 
499         ei->refcount = get_extent_data_refcount(type, data);
500         ei->generation = Vcb->superblock.generation;
501         ei->flags = is_tree ? EXTENT_ITEM_TREE_BLOCK : EXTENT_ITEM_DATA;
502         ptr = (uint8_t*)&ei[1];
503 
504         if (is_tree && !(Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)) {
505             EXTENT_ITEM2* ei2 = (EXTENT_ITEM2*)ptr;
506             ei2->firstitem = *firstitem;
507             ei2->level = level;
508             ptr = (uint8_t*)&ei2[1];
509         }
510 
511         *ptr = type;
512         RtlCopyMemory(ptr + 1, data, datalen);
513 
514         if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA && is_tree)
515             Status = insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, ei, eisize, NULL, Irp);
516         else
517             Status = insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, size, ei, eisize, NULL, Irp);
518 
519         if (!NT_SUCCESS(Status)) {
520             ERR("insert_tree_item returned %08lx\n", Status);
521             return Status;
522         }
523 
524         return STATUS_SUCCESS;
525     } else if (tp.item->key.obj_id == address && tp.item->key.obj_type == TYPE_EXTENT_ITEM && tp.item->key.offset != size) {
526         ERR("extent %I64x exists, but with size %I64x rather than %I64x expected\n", tp.item->key.obj_id, tp.item->key.offset, size);
527         return STATUS_INTERNAL_ERROR;
528     }
529 
530     skinny = tp.item->key.obj_type == TYPE_METADATA_ITEM;
531 
532     if (tp.item->size == sizeof(EXTENT_ITEM_V0) && !skinny) {
533         Status = convert_old_extent(Vcb, address, is_tree, firstitem, level, Irp);
534 
535         if (!NT_SUCCESS(Status)) {
536             ERR("convert_old_extent returned %08lx\n", Status);
537             return Status;
538         }
539 
540         return increase_extent_refcount(Vcb, address, size, type, data, firstitem, level, Irp);
541     }
542 
543     if (tp.item->size < sizeof(EXTENT_ITEM)) {
544         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));
545         return STATUS_INTERNAL_ERROR;
546     }
547 
548     ei = (EXTENT_ITEM*)tp.item->data;
549 
550     len = tp.item->size - sizeof(EXTENT_ITEM);
551     ptr = (uint8_t*)&ei[1];
552 
553     if (ei->flags & EXTENT_ITEM_TREE_BLOCK && !skinny) {
554         if (tp.item->size < sizeof(EXTENT_ITEM) + sizeof(EXTENT_ITEM2)) {
555             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) + sizeof(EXTENT_ITEM2));
556             return STATUS_INTERNAL_ERROR;
557         }
558 
559         len -= sizeof(EXTENT_ITEM2);
560         ptr += sizeof(EXTENT_ITEM2);
561     }
562 
563     inline_rc = 0;
564 
565     // Loop through existing inline extent entries
566 
567     while (len > 0) {
568         uint8_t secttype = *ptr;
569         ULONG sectlen = get_extent_data_len(secttype);
570         uint64_t sectcount = get_extent_data_refcount(secttype, ptr + sizeof(uint8_t));
571 
572         len--;
573 
574         if (sectlen > len) {
575             ERR("(%I64x,%x,%I64x): %lx bytes left, expecting at least %lx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, len, sectlen);
576             return STATUS_INTERNAL_ERROR;
577         }
578 
579         if (sectlen == 0) {
580             ERR("(%I64x,%x,%I64x): unrecognized extent type %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, secttype);
581             return STATUS_INTERNAL_ERROR;
582         }
583 
584         // If inline extent already present, increase refcount and return
585 
586         if (secttype == type) {
587             if (type == TYPE_EXTENT_DATA_REF) {
588                 EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)(ptr + sizeof(uint8_t));
589                 EXTENT_DATA_REF* edr = (EXTENT_DATA_REF*)data;
590 
591                 if (sectedr->root == edr->root && sectedr->objid == edr->objid && sectedr->offset == edr->offset) {
592                     uint32_t rc = get_extent_data_refcount(type, data);
593                     EXTENT_DATA_REF* sectedr2;
594 
595                     newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
596                     if (!newei) {
597                         ERR("out of memory\n");
598                         return STATUS_INSUFFICIENT_RESOURCES;
599                     }
600 
601                     RtlCopyMemory(newei, tp.item->data, tp.item->size);
602 
603                     newei->refcount += rc;
604 
605                     sectedr2 = (EXTENT_DATA_REF*)((uint8_t*)newei + ((uint8_t*)sectedr - tp.item->data));
606                     sectedr2->count += rc;
607 
608                     Status = delete_tree_item(Vcb, &tp);
609                     if (!NT_SUCCESS(Status)) {
610                         ERR("delete_tree_item returned %08lx\n", Status);
611                         return Status;
612                     }
613 
614                     Status = insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp);
615                     if (!NT_SUCCESS(Status)) {
616                         ERR("insert_tree_item returned %08lx\n", Status);
617                         return Status;
618                     }
619 
620                     return STATUS_SUCCESS;
621                 }
622             } else if (type == TYPE_TREE_BLOCK_REF) {
623                 TREE_BLOCK_REF* secttbr = (TREE_BLOCK_REF*)(ptr + sizeof(uint8_t));
624                 TREE_BLOCK_REF* tbr = (TREE_BLOCK_REF*)data;
625 
626                 if (secttbr->offset == tbr->offset) {
627                     TRACE("trying to increase refcount of non-shared tree extent\n");
628                     return STATUS_SUCCESS;
629                 }
630             } else if (type == TYPE_SHARED_BLOCK_REF) {
631                 SHARED_BLOCK_REF* sectsbr = (SHARED_BLOCK_REF*)(ptr + sizeof(uint8_t));
632                 SHARED_BLOCK_REF* sbr = (SHARED_BLOCK_REF*)data;
633 
634                 if (sectsbr->offset == sbr->offset)
635                     return STATUS_SUCCESS;
636             } else if (type == TYPE_SHARED_DATA_REF) {
637                 SHARED_DATA_REF* sectsdr = (SHARED_DATA_REF*)(ptr + sizeof(uint8_t));
638                 SHARED_DATA_REF* sdr = (SHARED_DATA_REF*)data;
639 
640                 if (sectsdr->offset == sdr->offset) {
641                     uint32_t rc = get_extent_data_refcount(type, data);
642                     SHARED_DATA_REF* sectsdr2;
643 
644                     newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
645                     if (!newei) {
646                         ERR("out of memory\n");
647                         return STATUS_INSUFFICIENT_RESOURCES;
648                     }
649 
650                     RtlCopyMemory(newei, tp.item->data, tp.item->size);
651 
652                     newei->refcount += rc;
653 
654                     sectsdr2 = (SHARED_DATA_REF*)((uint8_t*)newei + ((uint8_t*)sectsdr - tp.item->data));
655                     sectsdr2->count += rc;
656 
657                     Status = delete_tree_item(Vcb, &tp);
658                     if (!NT_SUCCESS(Status)) {
659                         ERR("delete_tree_item returned %08lx\n", Status);
660                         return Status;
661                     }
662 
663                     Status = insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp);
664                     if (!NT_SUCCESS(Status)) {
665                         ERR("insert_tree_item returned %08lx\n", Status);
666                         return Status;
667                     }
668 
669                     return STATUS_SUCCESS;
670                 }
671             } else {
672                 ERR("unhandled extent type %x\n", type);
673                 return STATUS_INTERNAL_ERROR;
674             }
675         }
676 
677         len -= sectlen;
678         ptr += sizeof(uint8_t) + sectlen;
679         inline_rc += sectcount;
680     }
681 
682     offset = get_extent_hash(type, data);
683 
684     max_extent_item_size = (Vcb->superblock.node_size >> 4) - sizeof(leaf_node);
685 
686     // If we can, add entry as inline extent item
687 
688     if (inline_rc == ei->refcount && tp.item->size + sizeof(uint8_t) + datalen < max_extent_item_size) {
689         len = tp.item->size - sizeof(EXTENT_ITEM);
690         ptr = (uint8_t*)&ei[1];
691 
692         if (ei->flags & EXTENT_ITEM_TREE_BLOCK && !skinny) {
693             len -= sizeof(EXTENT_ITEM2);
694             ptr += sizeof(EXTENT_ITEM2);
695         }
696 
697         // Confusingly, it appears that references are sorted forward by type (i.e. EXTENT_DATA_REFs before
698         // SHARED_DATA_REFs), but then backwards by hash...
699 
700         while (len > 0) {
701             uint8_t secttype = *ptr;
702             ULONG sectlen = get_extent_data_len(secttype);
703 
704             if (secttype > type)
705                 break;
706 
707             if (secttype == type) {
708                 uint64_t sectoff = get_extent_hash(secttype, ptr + 1);
709 
710                 if (sectoff < offset)
711                     break;
712             }
713 
714             len -= sectlen + sizeof(uint8_t);
715             ptr += sizeof(uint8_t) + sectlen;
716         }
717 
718         newei = ExAllocatePoolWithTag(PagedPool, tp.item->size + sizeof(uint8_t) + datalen, ALLOC_TAG);
719         RtlCopyMemory(newei, tp.item->data, ptr - tp.item->data);
720 
721         newei->refcount += get_extent_data_refcount(type, data);
722 
723         if (len > 0)
724             RtlCopyMemory((uint8_t*)newei + (ptr - tp.item->data) + sizeof(uint8_t) + datalen, ptr, len);
725 
726         ptr = (ptr - tp.item->data) + (uint8_t*)newei;
727 
728         *ptr = type;
729         RtlCopyMemory(ptr + 1, data, datalen);
730 
731         Status = delete_tree_item(Vcb, &tp);
732         if (!NT_SUCCESS(Status)) {
733             ERR("delete_tree_item returned %08lx\n", Status);
734             return Status;
735         }
736 
737         Status = insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size + sizeof(uint8_t) + datalen, NULL, Irp);
738         if (!NT_SUCCESS(Status)) {
739             ERR("insert_tree_item returned %08lx\n", Status);
740             return Status;
741         }
742 
743         return STATUS_SUCCESS;
744     }
745 
746     // Look for existing non-inline entry, and increase refcount if found
747 
748     if (inline_rc != ei->refcount) {
749         traverse_ptr tp2;
750 
751         searchkey.obj_id = address;
752         searchkey.obj_type = type;
753         searchkey.offset = offset;
754 
755         Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, false, Irp);
756         if (!NT_SUCCESS(Status)) {
757             ERR("error - find_item returned %08lx\n", Status);
758             return Status;
759         }
760 
761         if (!keycmp(tp2.item->key, searchkey)) {
762             if (type == TYPE_SHARED_DATA_REF && tp2.item->size < sizeof(uint32_t)) {
763                 ERR("(%I64x,%x,%I64x) was %x bytes, expecting %Ix\n", tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, tp2.item->size, sizeof(uint32_t));
764                 return STATUS_INTERNAL_ERROR;
765             } else if (type != TYPE_SHARED_DATA_REF && tp2.item->size < datalen) {
766                 ERR("(%I64x,%x,%I64x) was %x bytes, expecting %x\n", tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, tp2.item->size, datalen);
767                 return STATUS_INTERNAL_ERROR;
768             }
769 
770             data2 = ExAllocatePoolWithTag(PagedPool, tp2.item->size, ALLOC_TAG);
771             if (!data2) {
772                 ERR("out of memory\n");
773                 return STATUS_INSUFFICIENT_RESOURCES;
774             }
775 
776             RtlCopyMemory(data2, tp2.item->data, tp2.item->size);
777 
778             if (type == TYPE_EXTENT_DATA_REF) {
779                 EXTENT_DATA_REF* edr = (EXTENT_DATA_REF*)data2;
780 
781                 edr->count += get_extent_data_refcount(type, data);
782             } else if (type == TYPE_TREE_BLOCK_REF) {
783                 TRACE("trying to increase refcount of non-shared tree extent\n");
784                 return STATUS_SUCCESS;
785             } else if (type == TYPE_SHARED_BLOCK_REF)
786                 return STATUS_SUCCESS;
787             else if (type == TYPE_SHARED_DATA_REF) {
788                 uint32_t* sdr = (uint32_t*)data2;
789 
790                 *sdr += get_extent_data_refcount(type, data);
791             } else {
792                 ERR("unhandled extent type %x\n", type);
793                 return STATUS_INTERNAL_ERROR;
794             }
795 
796             Status = delete_tree_item(Vcb, &tp2);
797             if (!NT_SUCCESS(Status)) {
798                 ERR("delete_tree_item returned %08lx\n", Status);
799                 return Status;
800             }
801 
802             Status = insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, data2, tp2.item->size, NULL, Irp);
803             if (!NT_SUCCESS(Status)) {
804                 ERR("insert_tree_item returned %08lx\n", Status);
805                 return Status;
806             }
807 
808             newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
809             if (!newei) {
810                 ERR("out of memory\n");
811                 return STATUS_INSUFFICIENT_RESOURCES;
812             }
813 
814             RtlCopyMemory(newei, tp.item->data, tp.item->size);
815 
816             newei->refcount += get_extent_data_refcount(type, data);
817 
818             Status = delete_tree_item(Vcb, &tp);
819             if (!NT_SUCCESS(Status)) {
820                 ERR("delete_tree_item returned %08lx\n", Status);
821                 return Status;
822             }
823 
824             Status = insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp);
825             if (!NT_SUCCESS(Status)) {
826                 ERR("insert_tree_item returned %08lx\n", Status);
827                 return Status;
828             }
829 
830             return STATUS_SUCCESS;
831         }
832     }
833 
834     // Otherwise, add new non-inline entry
835 
836     if (type == TYPE_SHARED_DATA_REF) {
837         SHARED_DATA_REF* sdr = (SHARED_DATA_REF*)data;
838 
839         data2 = ExAllocatePoolWithTag(PagedPool, sizeof(uint32_t), ALLOC_TAG);
840         if (!data2) {
841             ERR("out of memory\n");
842             return STATUS_INSUFFICIENT_RESOURCES;
843         }
844 
845         datalen = sizeof(uint32_t);
846 
847         *((uint32_t*)data2) = sdr->count;
848     } else if (type == TYPE_TREE_BLOCK_REF || type == TYPE_SHARED_BLOCK_REF) {
849         data2 = NULL;
850         datalen = 0;
851     } else {
852         data2 = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG);
853         if (!data2) {
854             ERR("out of memory\n");
855             return STATUS_INSUFFICIENT_RESOURCES;
856         }
857 
858         RtlCopyMemory(data2, data, datalen);
859     }
860 
861     Status = insert_tree_item(Vcb, Vcb->extent_root, address, type, offset, data2, datalen, NULL, Irp);
862     if (!NT_SUCCESS(Status)) {
863         ERR("insert_tree_item returned %08lx\n", Status);
864         return Status;
865     }
866 
867     newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
868     if (!newei) {
869         ERR("out of memory\n");
870         return STATUS_INSUFFICIENT_RESOURCES;
871     }
872 
873     RtlCopyMemory(newei, tp.item->data, tp.item->size);
874 
875     newei->refcount += get_extent_data_refcount(type, data);
876 
877     Status = delete_tree_item(Vcb, &tp);
878     if (!NT_SUCCESS(Status)) {
879         ERR("delete_tree_item returned %08lx\n", Status);
880         return Status;
881     }
882 
883     Status = insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp);
884     if (!NT_SUCCESS(Status)) {
885         ERR("insert_tree_item returned %08lx\n", Status);
886         return Status;
887     }
888 
889     return STATUS_SUCCESS;
890 }
891 
892 NTSTATUS increase_extent_refcount_data(device_extension* Vcb, uint64_t address, uint64_t size, uint64_t root, uint64_t inode, uint64_t offset, uint32_t refcount, PIRP Irp) {
893     EXTENT_DATA_REF edr;
894 
895     edr.root = root;
896     edr.objid = inode;
897     edr.offset = offset;
898     edr.count = refcount;
899 
900     return increase_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, Irp);
901 }
902 
903 NTSTATUS decrease_extent_refcount(device_extension* Vcb, uint64_t address, uint64_t size, uint8_t type, void* data, KEY* firstitem,
904                                   uint8_t level, uint64_t parent, bool superseded, PIRP Irp) {
905     KEY searchkey;
906     NTSTATUS Status;
907     traverse_ptr tp, tp2;
908     EXTENT_ITEM* ei;
909     ULONG len;
910     uint64_t inline_rc;
911     uint8_t* ptr;
912     uint32_t rc = data ? get_extent_data_refcount(type, data) : 1;
913     ULONG datalen = get_extent_data_len(type);
914     bool is_tree = (type == TYPE_TREE_BLOCK_REF || type == TYPE_SHARED_BLOCK_REF), skinny = false;
915 
916     if (is_tree && Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
917         searchkey.obj_id = address;
918         searchkey.obj_type = TYPE_METADATA_ITEM;
919         searchkey.offset = 0xffffffffffffffff;
920 
921         Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
922         if (!NT_SUCCESS(Status)) {
923             ERR("error - find_item returned %08lx\n", Status);
924             return Status;
925         }
926 
927         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
928             skinny = true;
929     }
930 
931     if (!skinny) {
932         searchkey.obj_id = address;
933         searchkey.obj_type = TYPE_EXTENT_ITEM;
934         searchkey.offset = 0xffffffffffffffff;
935 
936         Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
937         if (!NT_SUCCESS(Status)) {
938             ERR("error - find_item returned %08lx\n", Status);
939             return Status;
940         }
941 
942         if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
943             ERR("could not find EXTENT_ITEM for address %I64x\n", address);
944             return STATUS_INTERNAL_ERROR;
945         }
946 
947         if (tp.item->key.offset != size) {
948             ERR("extent %I64x had length %I64x, not %I64x as expected\n", address, tp.item->key.offset, size);
949             return STATUS_INTERNAL_ERROR;
950         }
951 
952         if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
953             Status = convert_old_extent(Vcb, address, is_tree, firstitem, level, Irp);
954 
955             if (!NT_SUCCESS(Status)) {
956                 ERR("convert_old_extent returned %08lx\n", Status);
957                 return Status;
958             }
959 
960             return decrease_extent_refcount(Vcb, address, size, type, data, firstitem, level, parent, superseded, Irp);
961         }
962     }
963 
964     if (tp.item->size < sizeof(EXTENT_ITEM)) {
965         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));
966         return STATUS_INTERNAL_ERROR;
967     }
968 
969     ei = (EXTENT_ITEM*)tp.item->data;
970 
971     len = tp.item->size - sizeof(EXTENT_ITEM);
972     ptr = (uint8_t*)&ei[1];
973 
974     if (ei->flags & EXTENT_ITEM_TREE_BLOCK && !skinny) {
975         if (tp.item->size < sizeof(EXTENT_ITEM) + sizeof(EXTENT_ITEM2)) {
976             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) + sizeof(EXTENT_ITEM2));
977             return STATUS_INTERNAL_ERROR;
978         }
979 
980         len -= sizeof(EXTENT_ITEM2);
981         ptr += sizeof(EXTENT_ITEM2);
982     }
983 
984     if (ei->refcount < rc) {
985         ERR("error - extent has refcount %I64x, trying to reduce by %x\n", ei->refcount, rc);
986         return STATUS_INTERNAL_ERROR;
987     }
988 
989     inline_rc = 0;
990 
991     // Loop through inline extent entries
992 
993     while (len > 0) {
994         uint8_t secttype = *ptr;
995         uint16_t sectlen = get_extent_data_len(secttype);
996         uint64_t sectcount = get_extent_data_refcount(secttype, ptr + sizeof(uint8_t));
997 
998         len--;
999 
1000         if (sectlen > len) {
1001             ERR("(%I64x,%x,%I64x): %lx bytes left, expecting at least %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, len, sectlen);
1002             return STATUS_INTERNAL_ERROR;
1003         }
1004 
1005         if (sectlen == 0) {
1006             ERR("(%I64x,%x,%I64x): unrecognized extent type %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, secttype);
1007             return STATUS_INTERNAL_ERROR;
1008         }
1009 
1010         if (secttype == type) {
1011             if (type == TYPE_EXTENT_DATA_REF) {
1012                 EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)(ptr + sizeof(uint8_t));
1013                 EXTENT_DATA_REF* edr = (EXTENT_DATA_REF*)data;
1014 
1015                 if (sectedr->root == edr->root && sectedr->objid == edr->objid && sectedr->offset == edr->offset) {
1016                     uint16_t neweilen;
1017                     EXTENT_ITEM* newei;
1018 
1019                     if (ei->refcount == edr->count) {
1020                         Status = delete_tree_item(Vcb, &tp);
1021                         if (!NT_SUCCESS(Status)) {
1022                             ERR("delete_tree_item returned %08lx\n", Status);
1023                             return Status;
1024                         }
1025 
1026                         if (!superseded)
1027                             add_checksum_entry(Vcb, address, (ULONG)(size / Vcb->superblock.sector_size), NULL, Irp);
1028 
1029                         return STATUS_SUCCESS;
1030                     }
1031 
1032                     if (sectedr->count < edr->count) {
1033                         ERR("error - extent section has refcount %x, trying to reduce by %x\n", sectedr->count, edr->count);
1034                         return STATUS_INTERNAL_ERROR;
1035                     }
1036 
1037                     if (sectedr->count > edr->count)    // reduce section refcount
1038                         neweilen = tp.item->size;
1039                     else                                // remove section entirely
1040                         neweilen = tp.item->size - sizeof(uint8_t) - sectlen;
1041 
1042                     newei = ExAllocatePoolWithTag(PagedPool, neweilen, ALLOC_TAG);
1043                     if (!newei) {
1044                         ERR("out of memory\n");
1045                         return STATUS_INSUFFICIENT_RESOURCES;
1046                     }
1047 
1048                     if (sectedr->count > edr->count) {
1049                         EXTENT_DATA_REF* newedr = (EXTENT_DATA_REF*)((uint8_t*)newei + ((uint8_t*)sectedr - tp.item->data));
1050 
1051                         RtlCopyMemory(newei, ei, neweilen);
1052 
1053                         newedr->count -= rc;
1054                     } else {
1055                         RtlCopyMemory(newei, ei, ptr - tp.item->data);
1056 
1057                         if (len > sectlen)
1058                             RtlCopyMemory((uint8_t*)newei + (ptr - tp.item->data), ptr + sectlen + sizeof(uint8_t), len - sectlen);
1059                     }
1060 
1061                     newei->refcount -= rc;
1062 
1063                     Status = delete_tree_item(Vcb, &tp);
1064                     if (!NT_SUCCESS(Status)) {
1065                         ERR("delete_tree_item returned %08lx\n", Status);
1066                         return Status;
1067                     }
1068 
1069                     Status = insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, Irp);
1070                     if (!NT_SUCCESS(Status)) {
1071                         ERR("insert_tree_item returned %08lx\n", Status);
1072                         return Status;
1073                     }
1074 
1075                     return STATUS_SUCCESS;
1076                 }
1077             } else if (type == TYPE_SHARED_DATA_REF) {
1078                 SHARED_DATA_REF* sectsdr = (SHARED_DATA_REF*)(ptr + sizeof(uint8_t));
1079                 SHARED_DATA_REF* sdr = (SHARED_DATA_REF*)data;
1080 
1081                 if (sectsdr->offset == sdr->offset) {
1082                     EXTENT_ITEM* newei;
1083                     uint16_t neweilen;
1084 
1085                     if (ei->refcount == sectsdr->count) {
1086                         Status = delete_tree_item(Vcb, &tp);
1087                         if (!NT_SUCCESS(Status)) {
1088                             ERR("delete_tree_item returned %08lx\n", Status);
1089                             return Status;
1090                         }
1091 
1092                         if (!superseded)
1093                             add_checksum_entry(Vcb, address, (ULONG)(size / Vcb->superblock.sector_size), NULL, Irp);
1094 
1095                         return STATUS_SUCCESS;
1096                     }
1097 
1098                     if (sectsdr->count < sdr->count) {
1099                         ERR("error - SHARED_DATA_REF has refcount %x, trying to reduce by %x\n", sectsdr->count, sdr->count);
1100                         return STATUS_INTERNAL_ERROR;
1101                     }
1102 
1103                     if (sectsdr->count > sdr->count)    // reduce section refcount
1104                         neweilen = tp.item->size;
1105                     else                                // remove section entirely
1106                         neweilen = tp.item->size - sizeof(uint8_t) - sectlen;
1107 
1108                     newei = ExAllocatePoolWithTag(PagedPool, neweilen, ALLOC_TAG);
1109                     if (!newei) {
1110                         ERR("out of memory\n");
1111                         return STATUS_INSUFFICIENT_RESOURCES;
1112                     }
1113 
1114                     if (sectsdr->count > sdr->count) {
1115                         SHARED_DATA_REF* newsdr = (SHARED_DATA_REF*)((uint8_t*)newei + ((uint8_t*)sectsdr - tp.item->data));
1116 
1117                         RtlCopyMemory(newei, ei, neweilen);
1118 
1119                         newsdr->count -= rc;
1120                     } else {
1121                         RtlCopyMemory(newei, ei, ptr - tp.item->data);
1122 
1123                         if (len > sectlen)
1124                             RtlCopyMemory((uint8_t*)newei + (ptr - tp.item->data), ptr + sectlen + sizeof(uint8_t), len - sectlen);
1125                     }
1126 
1127                     newei->refcount -= rc;
1128 
1129                     Status = delete_tree_item(Vcb, &tp);
1130                     if (!NT_SUCCESS(Status)) {
1131                         ERR("delete_tree_item returned %08lx\n", Status);
1132                         return Status;
1133                     }
1134 
1135                     Status = insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, Irp);
1136                     if (!NT_SUCCESS(Status)) {
1137                         ERR("insert_tree_item returned %08lx\n", Status);
1138                         return Status;
1139                     }
1140 
1141                     return STATUS_SUCCESS;
1142                 }
1143             } else if (type == TYPE_TREE_BLOCK_REF) {
1144                 TREE_BLOCK_REF* secttbr = (TREE_BLOCK_REF*)(ptr + sizeof(uint8_t));
1145                 TREE_BLOCK_REF* tbr = (TREE_BLOCK_REF*)data;
1146 
1147                 if (secttbr->offset == tbr->offset) {
1148                     EXTENT_ITEM* newei;
1149                     uint16_t neweilen;
1150 
1151                     if (ei->refcount == 1) {
1152                         Status = delete_tree_item(Vcb, &tp);
1153                         if (!NT_SUCCESS(Status)) {
1154                             ERR("delete_tree_item returned %08lx\n", Status);
1155                             return Status;
1156                         }
1157 
1158                         return STATUS_SUCCESS;
1159                     }
1160 
1161                     neweilen = tp.item->size - sizeof(uint8_t) - sectlen;
1162 
1163                     newei = ExAllocatePoolWithTag(PagedPool, neweilen, ALLOC_TAG);
1164                     if (!newei) {
1165                         ERR("out of memory\n");
1166                         return STATUS_INSUFFICIENT_RESOURCES;
1167                     }
1168 
1169                     RtlCopyMemory(newei, ei, ptr - tp.item->data);
1170 
1171                     if (len > sectlen)
1172                         RtlCopyMemory((uint8_t*)newei + (ptr - tp.item->data), ptr + sectlen + sizeof(uint8_t), len - sectlen);
1173 
1174                     newei->refcount--;
1175 
1176                     Status = delete_tree_item(Vcb, &tp);
1177                     if (!NT_SUCCESS(Status)) {
1178                         ERR("delete_tree_item returned %08lx\n", Status);
1179                         return Status;
1180                     }
1181 
1182                     Status = insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, Irp);
1183                     if (!NT_SUCCESS(Status)) {
1184                         ERR("insert_tree_item returned %08lx\n", Status);
1185                         return Status;
1186                     }
1187 
1188                     return STATUS_SUCCESS;
1189                 }
1190             } else if (type == TYPE_SHARED_BLOCK_REF) {
1191                 SHARED_BLOCK_REF* sectsbr = (SHARED_BLOCK_REF*)(ptr + sizeof(uint8_t));
1192                 SHARED_BLOCK_REF* sbr = (SHARED_BLOCK_REF*)data;
1193 
1194                 if (sectsbr->offset == sbr->offset) {
1195                     EXTENT_ITEM* newei;
1196                     uint16_t neweilen;
1197 
1198                     if (ei->refcount == 1) {
1199                         Status = delete_tree_item(Vcb, &tp);
1200                         if (!NT_SUCCESS(Status)) {
1201                             ERR("delete_tree_item returned %08lx\n", Status);
1202                             return Status;
1203                         }
1204 
1205                         return STATUS_SUCCESS;
1206                     }
1207 
1208                     neweilen = tp.item->size - sizeof(uint8_t) - sectlen;
1209 
1210                     newei = ExAllocatePoolWithTag(PagedPool, neweilen, ALLOC_TAG);
1211                     if (!newei) {
1212                         ERR("out of memory\n");
1213                         return STATUS_INSUFFICIENT_RESOURCES;
1214                     }
1215 
1216                     RtlCopyMemory(newei, ei, ptr - tp.item->data);
1217 
1218                     if (len > sectlen)
1219                         RtlCopyMemory((uint8_t*)newei + (ptr - tp.item->data), ptr + sectlen + sizeof(uint8_t), len - sectlen);
1220 
1221                     newei->refcount--;
1222 
1223                     Status = delete_tree_item(Vcb, &tp);
1224                     if (!NT_SUCCESS(Status)) {
1225                         ERR("delete_tree_item returned %08lx\n", Status);
1226                         return Status;
1227                     }
1228 
1229                     Status = insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, neweilen, NULL, Irp);
1230                     if (!NT_SUCCESS(Status)) {
1231                         ERR("insert_tree_item returned %08lx\n", Status);
1232                         return Status;
1233                     }
1234 
1235                     return STATUS_SUCCESS;
1236                 }
1237             } else {
1238                 ERR("unhandled extent type %x\n", type);
1239                 return STATUS_INTERNAL_ERROR;
1240             }
1241         }
1242 
1243         len -= sectlen;
1244         ptr += sizeof(uint8_t) + sectlen;
1245         inline_rc += sectcount;
1246     }
1247 
1248     if (inline_rc == ei->refcount) {
1249         ERR("entry not found in inline extent item for address %I64x\n", address);
1250         return STATUS_INTERNAL_ERROR;
1251     }
1252 
1253     if (type == TYPE_SHARED_DATA_REF)
1254         datalen = sizeof(uint32_t);
1255     else if (type == TYPE_TREE_BLOCK_REF || type == TYPE_SHARED_BLOCK_REF)
1256         datalen = 0;
1257 
1258     searchkey.obj_id = address;
1259     searchkey.obj_type = type;
1260     searchkey.offset = (type == TYPE_SHARED_DATA_REF || type == TYPE_EXTENT_REF_V0) ? parent : get_extent_hash(type, data);
1261 
1262     Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, false, Irp);
1263     if (!NT_SUCCESS(Status)) {
1264         ERR("error - find_item returned %08lx\n", Status);
1265         return Status;
1266     }
1267 
1268     if (keycmp(tp2.item->key, searchkey)) {
1269         ERR("(%I64x,%x,%I64x) not found\n", tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset);
1270         return STATUS_INTERNAL_ERROR;
1271     }
1272 
1273     if (tp2.item->size < datalen) {
1274         ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %lu\n", tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, tp2.item->size, datalen);
1275         return STATUS_INTERNAL_ERROR;
1276     }
1277 
1278     if (type == TYPE_EXTENT_DATA_REF) {
1279         EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)tp2.item->data;
1280         EXTENT_DATA_REF* edr = (EXTENT_DATA_REF*)data;
1281 
1282         if (sectedr->root == edr->root && sectedr->objid == edr->objid && sectedr->offset == edr->offset) {
1283             EXTENT_ITEM* newei;
1284 
1285             if (ei->refcount == edr->count) {
1286                 Status = delete_tree_item(Vcb, &tp);
1287                 if (!NT_SUCCESS(Status)) {
1288                     ERR("delete_tree_item returned %08lx\n", Status);
1289                     return Status;
1290                 }
1291 
1292                 Status = delete_tree_item(Vcb, &tp2);
1293                 if (!NT_SUCCESS(Status)) {
1294                     ERR("delete_tree_item returned %08lx\n", Status);
1295                     return Status;
1296                 }
1297 
1298                 if (!superseded)
1299                     add_checksum_entry(Vcb, address, (ULONG)(size / Vcb->superblock.sector_size), NULL, Irp);
1300 
1301                 return STATUS_SUCCESS;
1302             }
1303 
1304             if (sectedr->count < edr->count) {
1305                 ERR("error - extent section has refcount %x, trying to reduce by %x\n", sectedr->count, edr->count);
1306                 return STATUS_INTERNAL_ERROR;
1307             }
1308 
1309             Status = delete_tree_item(Vcb, &tp2);
1310             if (!NT_SUCCESS(Status)) {
1311                 ERR("delete_tree_item returned %08lx\n", Status);
1312                 return Status;
1313             }
1314 
1315             if (sectedr->count > edr->count) {
1316                 EXTENT_DATA_REF* newedr = ExAllocatePoolWithTag(PagedPool, tp2.item->size, ALLOC_TAG);
1317 
1318                 if (!newedr) {
1319                     ERR("out of memory\n");
1320                     return STATUS_INSUFFICIENT_RESOURCES;
1321                 }
1322 
1323                 RtlCopyMemory(newedr, sectedr, tp2.item->size);
1324 
1325                 newedr->count -= edr->count;
1326 
1327                 Status = insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, newedr, tp2.item->size, NULL, Irp);
1328                 if (!NT_SUCCESS(Status)) {
1329                     ERR("insert_tree_item returned %08lx\n", Status);
1330                     return Status;
1331                 }
1332             }
1333 
1334             newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
1335             if (!newei) {
1336                 ERR("out of memory\n");
1337                 return STATUS_INSUFFICIENT_RESOURCES;
1338             }
1339 
1340             RtlCopyMemory(newei, tp.item->data, tp.item->size);
1341 
1342             newei->refcount -= rc;
1343 
1344             Status = delete_tree_item(Vcb, &tp);
1345             if (!NT_SUCCESS(Status)) {
1346                 ERR("delete_tree_item returned %08lx\n", Status);
1347                 return Status;
1348             }
1349 
1350             Status = insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp);
1351             if (!NT_SUCCESS(Status)) {
1352                 ERR("insert_tree_item returned %08lx\n", Status);
1353                 return Status;
1354             }
1355 
1356             return STATUS_SUCCESS;
1357         } else {
1358             ERR("error - hash collision?\n");
1359             return STATUS_INTERNAL_ERROR;
1360         }
1361     } else if (type == TYPE_SHARED_DATA_REF) {
1362         SHARED_DATA_REF* sdr = (SHARED_DATA_REF*)data;
1363 
1364         if (tp2.item->key.offset == sdr->offset) {
1365             uint32_t* sectsdrcount = (uint32_t*)tp2.item->data;
1366             EXTENT_ITEM* newei;
1367 
1368             if (ei->refcount == sdr->count) {
1369                 Status = delete_tree_item(Vcb, &tp);
1370                 if (!NT_SUCCESS(Status)) {
1371                     ERR("delete_tree_item returned %08lx\n", Status);
1372                     return Status;
1373                 }
1374 
1375                 Status = delete_tree_item(Vcb, &tp2);
1376                 if (!NT_SUCCESS(Status)) {
1377                     ERR("delete_tree_item returned %08lx\n", Status);
1378                     return Status;
1379                 }
1380 
1381                 if (!superseded)
1382                     add_checksum_entry(Vcb, address, (ULONG)(size / Vcb->superblock.sector_size), NULL, Irp);
1383 
1384                 return STATUS_SUCCESS;
1385             }
1386 
1387             if (*sectsdrcount < sdr->count) {
1388                 ERR("error - extent section has refcount %x, trying to reduce by %x\n", *sectsdrcount, sdr->count);
1389                 return STATUS_INTERNAL_ERROR;
1390             }
1391 
1392             Status = delete_tree_item(Vcb, &tp2);
1393             if (!NT_SUCCESS(Status)) {
1394                 ERR("delete_tree_item returned %08lx\n", Status);
1395                 return Status;
1396             }
1397 
1398             if (*sectsdrcount > sdr->count) {
1399                 uint32_t* newsdr = ExAllocatePoolWithTag(PagedPool, tp2.item->size, ALLOC_TAG);
1400 
1401                 if (!newsdr) {
1402                     ERR("out of memory\n");
1403                     return STATUS_INSUFFICIENT_RESOURCES;
1404                 }
1405 
1406                 *newsdr = *sectsdrcount - sdr->count;
1407 
1408                 Status = insert_tree_item(Vcb, Vcb->extent_root, tp2.item->key.obj_id, tp2.item->key.obj_type, tp2.item->key.offset, newsdr, tp2.item->size, NULL, Irp);
1409                 if (!NT_SUCCESS(Status)) {
1410                     ERR("insert_tree_item returned %08lx\n", Status);
1411                     return Status;
1412                 }
1413             }
1414 
1415             newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
1416             if (!newei) {
1417                 ERR("out of memory\n");
1418                 return STATUS_INSUFFICIENT_RESOURCES;
1419             }
1420 
1421             RtlCopyMemory(newei, tp.item->data, tp.item->size);
1422 
1423             newei->refcount -= rc;
1424 
1425             Status = delete_tree_item(Vcb, &tp);
1426             if (!NT_SUCCESS(Status)) {
1427                 ERR("delete_tree_item returned %08lx\n", Status);
1428                 return Status;
1429             }
1430 
1431             Status = insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp);
1432             if (!NT_SUCCESS(Status)) {
1433                 ERR("insert_tree_item returned %08lx\n", Status);
1434                 return Status;
1435             }
1436 
1437             return STATUS_SUCCESS;
1438         } else {
1439             ERR("error - collision?\n");
1440             return STATUS_INTERNAL_ERROR;
1441         }
1442     } else if (type == TYPE_TREE_BLOCK_REF || type == TYPE_SHARED_BLOCK_REF) {
1443         EXTENT_ITEM* newei;
1444 
1445         if (ei->refcount == 1) {
1446             Status = delete_tree_item(Vcb, &tp);
1447             if (!NT_SUCCESS(Status)) {
1448                 ERR("delete_tree_item returned %08lx\n", Status);
1449                 return Status;
1450             }
1451 
1452             Status = delete_tree_item(Vcb, &tp2);
1453             if (!NT_SUCCESS(Status)) {
1454                 ERR("delete_tree_item returned %08lx\n", Status);
1455                 return Status;
1456             }
1457 
1458             return STATUS_SUCCESS;
1459         }
1460 
1461         Status = delete_tree_item(Vcb, &tp2);
1462         if (!NT_SUCCESS(Status)) {
1463             ERR("delete_tree_item returned %08lx\n", Status);
1464             return Status;
1465         }
1466 
1467         newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
1468         if (!newei) {
1469             ERR("out of memory\n");
1470             return STATUS_INSUFFICIENT_RESOURCES;
1471         }
1472 
1473         RtlCopyMemory(newei, tp.item->data, tp.item->size);
1474 
1475         newei->refcount -= rc;
1476 
1477         Status = delete_tree_item(Vcb, &tp);
1478         if (!NT_SUCCESS(Status)) {
1479             ERR("delete_tree_item returned %08lx\n", Status);
1480             return Status;
1481         }
1482 
1483         Status = insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp);
1484         if (!NT_SUCCESS(Status)) {
1485             ERR("insert_tree_item returned %08lx\n", Status);
1486             return Status;
1487         }
1488 
1489         return STATUS_SUCCESS;
1490     } else if (type == TYPE_EXTENT_REF_V0) {
1491         EXTENT_REF_V0* erv0 = (EXTENT_REF_V0*)tp2.item->data;
1492         EXTENT_ITEM* newei;
1493 
1494         if (ei->refcount == erv0->count) {
1495             Status = delete_tree_item(Vcb, &tp);
1496             if (!NT_SUCCESS(Status)) {
1497                 ERR("delete_tree_item returned %08lx\n", Status);
1498                 return Status;
1499             }
1500 
1501             Status = delete_tree_item(Vcb, &tp2);
1502             if (!NT_SUCCESS(Status)) {
1503                 ERR("delete_tree_item returned %08lx\n", Status);
1504                 return Status;
1505             }
1506 
1507             if (!superseded)
1508                 add_checksum_entry(Vcb, address, (ULONG)(size / Vcb->superblock.sector_size), NULL, Irp);
1509 
1510             return STATUS_SUCCESS;
1511         }
1512 
1513         Status = delete_tree_item(Vcb, &tp2);
1514         if (!NT_SUCCESS(Status)) {
1515             ERR("delete_tree_item returned %08lx\n", Status);
1516             return Status;
1517         }
1518 
1519         newei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
1520         if (!newei) {
1521             ERR("out of memory\n");
1522             return STATUS_INSUFFICIENT_RESOURCES;
1523         }
1524 
1525         RtlCopyMemory(newei, tp.item->data, tp.item->size);
1526 
1527         newei->refcount -= rc;
1528 
1529         Status = delete_tree_item(Vcb, &tp);
1530         if (!NT_SUCCESS(Status)) {
1531             ERR("delete_tree_item returned %08lx\n", Status);
1532             return Status;
1533         }
1534 
1535         Status = insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newei, tp.item->size, NULL, Irp);
1536         if (!NT_SUCCESS(Status)) {
1537             ERR("insert_tree_item returned %08lx\n", Status);
1538             return Status;
1539         }
1540 
1541         return STATUS_SUCCESS;
1542     } else {
1543         ERR("unhandled extent type %x\n", type);
1544         return STATUS_INTERNAL_ERROR;
1545     }
1546 }
1547 
1548 NTSTATUS decrease_extent_refcount_data(device_extension* Vcb, uint64_t address, uint64_t size, uint64_t root, uint64_t inode,
1549                                        uint64_t offset, uint32_t refcount, bool superseded, PIRP Irp) {
1550     EXTENT_DATA_REF edr;
1551 
1552     edr.root = root;
1553     edr.objid = inode;
1554     edr.offset = offset;
1555     edr.count = refcount;
1556 
1557     return decrease_extent_refcount(Vcb, address, size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, 0, superseded, Irp);
1558 }
1559 
1560 NTSTATUS decrease_extent_refcount_tree(device_extension* Vcb, uint64_t address, uint64_t size, uint64_t root,
1561                                        uint8_t level, PIRP Irp) {
1562     TREE_BLOCK_REF tbr;
1563 
1564     tbr.offset = root;
1565 
1566     return decrease_extent_refcount(Vcb, address, size, TYPE_TREE_BLOCK_REF, &tbr, NULL/*FIXME*/, level, 0, false, Irp);
1567 }
1568 
1569 static uint32_t find_extent_data_refcount(device_extension* Vcb, uint64_t address, uint64_t size, uint64_t root, uint64_t objid, uint64_t offset, PIRP Irp) {
1570     NTSTATUS Status;
1571     KEY searchkey;
1572     traverse_ptr tp;
1573 
1574     searchkey.obj_id = address;
1575     searchkey.obj_type = TYPE_EXTENT_ITEM;
1576     searchkey.offset = 0xffffffffffffffff;
1577 
1578     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
1579     if (!NT_SUCCESS(Status)) {
1580         ERR("error - find_item returned %08lx\n", Status);
1581         return 0;
1582     }
1583 
1584     if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
1585         TRACE("could not find address %I64x in extent tree\n", address);
1586         return 0;
1587     }
1588 
1589     if (tp.item->key.offset != size) {
1590         ERR("extent %I64x had size %I64x, not %I64x as expected\n", address, tp.item->key.offset, size);
1591         return 0;
1592     }
1593 
1594     if (tp.item->size >= sizeof(EXTENT_ITEM)) {
1595         EXTENT_ITEM* ei = (EXTENT_ITEM*)tp.item->data;
1596         uint32_t len = tp.item->size - sizeof(EXTENT_ITEM);
1597         uint8_t* ptr = (uint8_t*)&ei[1];
1598 
1599         while (len > 0) {
1600             uint8_t secttype = *ptr;
1601             ULONG sectlen = get_extent_data_len(secttype);
1602             uint32_t sectcount = get_extent_data_refcount(secttype, ptr + sizeof(uint8_t));
1603 
1604             len--;
1605 
1606             if (sectlen > len) {
1607                 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);
1608                 return 0;
1609             }
1610 
1611             if (sectlen == 0) {
1612                 ERR("(%I64x,%x,%I64x): unrecognized extent type %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, secttype);
1613                 return 0;
1614             }
1615 
1616             if (secttype == TYPE_EXTENT_DATA_REF) {
1617                 EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)(ptr + sizeof(uint8_t));
1618 
1619                 if (sectedr->root == root && sectedr->objid == objid && sectedr->offset == offset)
1620                     return sectcount;
1621             }
1622 
1623             len -= sectlen;
1624             ptr += sizeof(uint8_t) + sectlen;
1625         }
1626     }
1627 
1628     searchkey.obj_id = address;
1629     searchkey.obj_type = TYPE_EXTENT_DATA_REF;
1630     searchkey.offset = get_extent_data_ref_hash2(root, objid, offset);
1631 
1632     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
1633     if (!NT_SUCCESS(Status)) {
1634         ERR("error - find_item returned %08lx\n", Status);
1635         return 0;
1636     }
1637 
1638     if (!keycmp(searchkey, tp.item->key)) {
1639         if (tp.item->size < sizeof(EXTENT_DATA_REF))
1640             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));
1641         else {
1642             EXTENT_DATA_REF* edr = (EXTENT_DATA_REF*)tp.item->data;
1643 
1644             return edr->count;
1645         }
1646     }
1647 
1648     return 0;
1649 }
1650 
1651 uint64_t get_extent_refcount(device_extension* Vcb, uint64_t address, uint64_t size, PIRP Irp) {
1652     KEY searchkey;
1653     traverse_ptr tp;
1654     NTSTATUS Status;
1655     EXTENT_ITEM* ei;
1656 
1657     searchkey.obj_id = address;
1658     searchkey.obj_type = Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA ? TYPE_METADATA_ITEM : TYPE_EXTENT_ITEM;
1659     searchkey.offset = 0xffffffffffffffff;
1660 
1661     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
1662     if (!NT_SUCCESS(Status)) {
1663         ERR("error - find_item returned %08lx\n", Status);
1664         return 0;
1665     }
1666 
1667     if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA && tp.item->key.obj_id == address &&
1668         tp.item->key.obj_type == TYPE_METADATA_ITEM && tp.item->size >= sizeof(EXTENT_ITEM)) {
1669         ei = (EXTENT_ITEM*)tp.item->data;
1670 
1671         return ei->refcount;
1672     }
1673 
1674     if (tp.item->key.obj_id != address || tp.item->key.obj_type != TYPE_EXTENT_ITEM) {
1675         ERR("couldn't find (%I64x,%x,%I64x) in extent tree\n", address, TYPE_EXTENT_ITEM, size);
1676         return 0;
1677     } else if (tp.item->key.offset != size) {
1678         ERR("extent %I64x had size %I64x, not %I64x as expected\n", address, tp.item->key.offset, size);
1679         return 0;
1680     }
1681 
1682     if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
1683         EXTENT_ITEM_V0* eiv0 = (EXTENT_ITEM_V0*)tp.item->data;
1684 
1685         return eiv0->refcount;
1686     } else if (tp.item->size < sizeof(EXTENT_ITEM)) {
1687         ERR("(%I64x,%x,%I64x) was %x bytes, expected at least %Ix\n", tp.item->key.obj_id, tp.item->key.obj_type,
1688                                                                       tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
1689         return 0;
1690     }
1691 
1692     ei = (EXTENT_ITEM*)tp.item->data;
1693 
1694     return ei->refcount;
1695 }
1696 
1697 bool is_extent_unique(device_extension* Vcb, uint64_t address, uint64_t size, PIRP Irp) {
1698     KEY searchkey;
1699     traverse_ptr tp, next_tp;
1700     NTSTATUS Status;
1701     uint64_t rc, rcrun, root = 0, inode = 0, offset = 0;
1702     uint32_t len;
1703     EXTENT_ITEM* ei;
1704     uint8_t* ptr;
1705     bool b;
1706 
1707     rc = get_extent_refcount(Vcb, address, size, Irp);
1708 
1709     if (rc == 1)
1710         return true;
1711 
1712     if (rc == 0)
1713         return false;
1714 
1715     searchkey.obj_id = address;
1716     searchkey.obj_type = TYPE_EXTENT_ITEM;
1717     searchkey.offset = size;
1718 
1719     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
1720     if (!NT_SUCCESS(Status)) {
1721         WARN("error - find_item returned %08lx\n", Status);
1722         return false;
1723     }
1724 
1725     if (keycmp(tp.item->key, searchkey)) {
1726         WARN("could not find (%I64x,%x,%I64x)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
1727         return false;
1728     }
1729 
1730     if (tp.item->size == sizeof(EXTENT_ITEM_V0))
1731         return false;
1732 
1733     if (tp.item->size < sizeof(EXTENT_ITEM)) {
1734         WARN("(%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));
1735         return false;
1736     }
1737 
1738     ei = (EXTENT_ITEM*)tp.item->data;
1739 
1740     len = tp.item->size - sizeof(EXTENT_ITEM);
1741     ptr = (uint8_t*)&ei[1];
1742 
1743     if (ei->flags & EXTENT_ITEM_TREE_BLOCK) {
1744         if (tp.item->size < sizeof(EXTENT_ITEM) + sizeof(EXTENT_ITEM2)) {
1745             WARN("(%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) + sizeof(EXTENT_ITEM2));
1746             return false;
1747         }
1748 
1749         len -= sizeof(EXTENT_ITEM2);
1750         ptr += sizeof(EXTENT_ITEM2);
1751     }
1752 
1753     rcrun = 0;
1754 
1755     // Loop through inline extent entries
1756 
1757     while (len > 0) {
1758         uint8_t secttype = *ptr;
1759         ULONG sectlen = get_extent_data_len(secttype);
1760         uint64_t sectcount = get_extent_data_refcount(secttype, ptr + sizeof(uint8_t));
1761 
1762         len--;
1763 
1764         if (sectlen > len) {
1765             WARN("(%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);
1766             return false;
1767         }
1768 
1769         if (sectlen == 0) {
1770             WARN("(%I64x,%x,%I64x): unrecognized extent type %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, secttype);
1771             return false;
1772         }
1773 
1774         if (secttype == TYPE_EXTENT_DATA_REF) {
1775             EXTENT_DATA_REF* sectedr = (EXTENT_DATA_REF*)(ptr + sizeof(uint8_t));
1776 
1777             if (root == 0 && inode == 0) {
1778                 root = sectedr->root;
1779                 inode = sectedr->objid;
1780                 offset = sectedr->offset;
1781             } else if (root != sectedr->root || inode != sectedr->objid || offset != sectedr->offset)
1782                 return false;
1783         } else
1784             return false;
1785 
1786         len -= sectlen;
1787         ptr += sizeof(uint8_t) + sectlen;
1788         rcrun += sectcount;
1789     }
1790 
1791     if (rcrun == rc)
1792         return true;
1793 
1794     // Loop through non-inlines if some refs still unaccounted for
1795 
1796     do {
1797         b = find_next_item(Vcb, &tp, &next_tp, false, Irp);
1798 
1799         if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == TYPE_EXTENT_DATA_REF) {
1800             EXTENT_DATA_REF* edr = (EXTENT_DATA_REF*)tp.item->data;
1801 
1802             if (tp.item->size < sizeof(EXTENT_DATA_REF)) {
1803                 WARN("(%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,
1804                      tp.item->size, sizeof(EXTENT_ITEM) + sizeof(EXTENT_ITEM2));
1805                 return false;
1806             }
1807 
1808             if (root == 0 && inode == 0) {
1809                 root = edr->root;
1810                 inode = edr->objid;
1811                 offset = edr->offset;
1812             } else if (root != edr->root || inode != edr->objid || offset != edr->offset)
1813                 return false;
1814 
1815             rcrun += edr->count;
1816         }
1817 
1818         if (rcrun == rc)
1819             return true;
1820 
1821         if (b) {
1822             tp = next_tp;
1823 
1824             if (tp.item->key.obj_id > searchkey.obj_id)
1825                 break;
1826         }
1827     } while (b);
1828 
1829     // If we reach this point, there's still some refs unaccounted for somewhere.
1830     // Return false in case we mess things up elsewhere.
1831 
1832     return false;
1833 }
1834 
1835 uint64_t get_extent_flags(device_extension* Vcb, uint64_t address, PIRP Irp) {
1836     KEY searchkey;
1837     traverse_ptr tp;
1838     NTSTATUS Status;
1839     EXTENT_ITEM* ei;
1840 
1841     searchkey.obj_id = address;
1842     searchkey.obj_type = Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA ? TYPE_METADATA_ITEM : TYPE_EXTENT_ITEM;
1843     searchkey.offset = 0xffffffffffffffff;
1844 
1845     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
1846     if (!NT_SUCCESS(Status)) {
1847         ERR("error - find_item returned %08lx\n", Status);
1848         return 0;
1849     }
1850 
1851     if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA && tp.item->key.obj_id == address &&
1852         tp.item->key.obj_type == TYPE_METADATA_ITEM && tp.item->size >= sizeof(EXTENT_ITEM)) {
1853         ei = (EXTENT_ITEM*)tp.item->data;
1854 
1855         return ei->flags;
1856     }
1857 
1858     if (tp.item->key.obj_id != address || tp.item->key.obj_type != TYPE_EXTENT_ITEM) {
1859         ERR("couldn't find %I64x in extent tree\n", address);
1860         return 0;
1861     }
1862 
1863     if (tp.item->size == sizeof(EXTENT_ITEM_V0))
1864         return 0;
1865     else if (tp.item->size < sizeof(EXTENT_ITEM)) {
1866         ERR("(%I64x,%x,%I64x) was %x bytes, expected at least %Ix\n", tp.item->key.obj_id, tp.item->key.obj_type,
1867                                                                       tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
1868         return 0;
1869     }
1870 
1871     ei = (EXTENT_ITEM*)tp.item->data;
1872 
1873     return ei->flags;
1874 }
1875 
1876 void update_extent_flags(device_extension* Vcb, uint64_t address, uint64_t flags, PIRP Irp) {
1877     KEY searchkey;
1878     traverse_ptr tp;
1879     NTSTATUS Status;
1880     EXTENT_ITEM* ei;
1881 
1882     searchkey.obj_id = address;
1883     searchkey.obj_type = Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA ? TYPE_METADATA_ITEM : TYPE_EXTENT_ITEM;
1884     searchkey.offset = 0xffffffffffffffff;
1885 
1886     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
1887     if (!NT_SUCCESS(Status)) {
1888         ERR("error - find_item returned %08lx\n", Status);
1889         return;
1890     }
1891 
1892     if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA && tp.item->key.obj_id == address &&
1893         tp.item->key.obj_type == TYPE_METADATA_ITEM && tp.item->size >= sizeof(EXTENT_ITEM)) {
1894         ei = (EXTENT_ITEM*)tp.item->data;
1895         ei->flags = flags;
1896         return;
1897     }
1898 
1899     if (tp.item->key.obj_id != address || tp.item->key.obj_type != TYPE_EXTENT_ITEM) {
1900         ERR("couldn't find %I64x in extent tree\n", address);
1901         return;
1902     }
1903 
1904     if (tp.item->size == sizeof(EXTENT_ITEM_V0))
1905         return;
1906     else if (tp.item->size < sizeof(EXTENT_ITEM)) {
1907         ERR("(%I64x,%x,%I64x) was %x bytes, expected at least %Ix\n", tp.item->key.obj_id, tp.item->key.obj_type,
1908                                                                       tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM));
1909         return;
1910     }
1911 
1912     ei = (EXTENT_ITEM*)tp.item->data;
1913     ei->flags = flags;
1914 }
1915 
1916 static changed_extent* get_changed_extent_item(chunk* c, uint64_t address, uint64_t size, bool no_csum) {
1917     LIST_ENTRY* le;
1918     changed_extent* ce;
1919 
1920     le = c->changed_extents.Flink;
1921     while (le != &c->changed_extents) {
1922         ce = CONTAINING_RECORD(le, changed_extent, list_entry);
1923 
1924         if (ce->address == address && ce->size == size)
1925             return ce;
1926 
1927         le = le->Flink;
1928     }
1929 
1930     ce = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent), ALLOC_TAG);
1931     if (!ce) {
1932         ERR("out of memory\n");
1933         return NULL;
1934     }
1935 
1936     ce->address = address;
1937     ce->size = size;
1938     ce->old_size = size;
1939     ce->count = 0;
1940     ce->old_count = 0;
1941     ce->no_csum = no_csum;
1942     ce->superseded = false;
1943     InitializeListHead(&ce->refs);
1944     InitializeListHead(&ce->old_refs);
1945 
1946     InsertTailList(&c->changed_extents, &ce->list_entry);
1947 
1948     return ce;
1949 }
1950 
1951 NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, uint64_t address, uint64_t size, uint64_t root, uint64_t objid, uint64_t offset, int32_t count,
1952                                    bool no_csum, bool superseded, PIRP Irp) {
1953     LIST_ENTRY* le;
1954     changed_extent* ce;
1955     changed_extent_ref* cer;
1956     NTSTATUS Status;
1957     KEY searchkey;
1958     traverse_ptr tp;
1959     uint32_t old_count;
1960 
1961     ExAcquireResourceExclusiveLite(&c->changed_extents_lock, true);
1962 
1963     ce = get_changed_extent_item(c, address, size, no_csum);
1964 
1965     if (!ce) {
1966         ERR("get_changed_extent_item failed\n");
1967         Status = STATUS_INTERNAL_ERROR;
1968         goto end;
1969     }
1970 
1971     if (IsListEmpty(&ce->refs) && IsListEmpty(&ce->old_refs)) { // new entry
1972         searchkey.obj_id = address;
1973         searchkey.obj_type = TYPE_EXTENT_ITEM;
1974         searchkey.offset = 0xffffffffffffffff;
1975 
1976         Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
1977         if (!NT_SUCCESS(Status)) {
1978             ERR("error - find_item returned %08lx\n", Status);
1979             goto end;
1980         }
1981 
1982         if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
1983             ERR("could not find address %I64x in extent tree\n", address);
1984             Status = STATUS_INTERNAL_ERROR;
1985             goto end;
1986         }
1987 
1988         if (tp.item->key.offset != size) {
1989             ERR("extent %I64x had size %I64x, not %I64x as expected\n", address, tp.item->key.offset, size);
1990             Status = STATUS_INTERNAL_ERROR;
1991             goto end;
1992         }
1993 
1994         if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
1995             EXTENT_ITEM_V0* eiv0 = (EXTENT_ITEM_V0*)tp.item->data;
1996 
1997             ce->count = ce->old_count = eiv0->refcount;
1998         } else if (tp.item->size >= sizeof(EXTENT_ITEM)) {
1999             EXTENT_ITEM* ei = (EXTENT_ITEM*)tp.item->data;
2000 
2001             ce->count = ce->old_count = ei->refcount;
2002         } else {
2003             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));
2004             Status = STATUS_INTERNAL_ERROR;
2005             goto end;
2006         }
2007     }
2008 
2009     le = ce->refs.Flink;
2010     while (le != &ce->refs) {
2011         cer = CONTAINING_RECORD(le, changed_extent_ref, list_entry);
2012 
2013         if (cer->type == TYPE_EXTENT_DATA_REF && cer->edr.root == root && cer->edr.objid == objid && cer->edr.offset == offset) {
2014             ce->count += count;
2015             cer->edr.count += count;
2016             Status = STATUS_SUCCESS;
2017 
2018             if (superseded)
2019                 ce->superseded = true;
2020 
2021             goto end;
2022         }
2023 
2024         le = le->Flink;
2025     }
2026 
2027     old_count = find_extent_data_refcount(Vcb, address, size, root, objid, offset, Irp);
2028 
2029     if (old_count > 0) {
2030         cer = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent_ref), ALLOC_TAG);
2031 
2032         if (!cer) {
2033             ERR("out of memory\n");
2034             Status = STATUS_INSUFFICIENT_RESOURCES;
2035             goto end;
2036         }
2037 
2038         cer->type = TYPE_EXTENT_DATA_REF;
2039         cer->edr.root = root;
2040         cer->edr.objid = objid;
2041         cer->edr.offset = offset;
2042         cer->edr.count = old_count;
2043 
2044         InsertTailList(&ce->old_refs, &cer->list_entry);
2045     }
2046 
2047     cer = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent_ref), ALLOC_TAG);
2048 
2049     if (!cer) {
2050         ERR("out of memory\n");
2051         Status = STATUS_INSUFFICIENT_RESOURCES;
2052         goto end;
2053     }
2054 
2055     cer->type = TYPE_EXTENT_DATA_REF;
2056     cer->edr.root = root;
2057     cer->edr.objid = objid;
2058     cer->edr.offset = offset;
2059     cer->edr.count = old_count + count;
2060 
2061     InsertTailList(&ce->refs, &cer->list_entry);
2062 
2063     ce->count += count;
2064 
2065     if (superseded)
2066         ce->superseded = true;
2067 
2068     Status = STATUS_SUCCESS;
2069 
2070 end:
2071     ExReleaseResourceLite(&c->changed_extents_lock);
2072 
2073     return Status;
2074 }
2075 
2076 void add_changed_extent_ref(chunk* c, uint64_t address, uint64_t size, uint64_t root, uint64_t objid, uint64_t offset, uint32_t count, bool no_csum) {
2077     changed_extent* ce;
2078     changed_extent_ref* cer;
2079     LIST_ENTRY* le;
2080 
2081     ce = get_changed_extent_item(c, address, size, no_csum);
2082 
2083     if (!ce) {
2084         ERR("get_changed_extent_item failed\n");
2085         return;
2086     }
2087 
2088     le = ce->refs.Flink;
2089     while (le != &ce->refs) {
2090         cer = CONTAINING_RECORD(le, changed_extent_ref, list_entry);
2091 
2092         if (cer->type == TYPE_EXTENT_DATA_REF && cer->edr.root == root && cer->edr.objid == objid && cer->edr.offset == offset) {
2093             ce->count += count;
2094             cer->edr.count += count;
2095             return;
2096         }
2097 
2098         le = le->Flink;
2099     }
2100 
2101     cer = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent_ref), ALLOC_TAG);
2102 
2103     if (!cer) {
2104         ERR("out of memory\n");
2105         return;
2106     }
2107 
2108     cer->type = TYPE_EXTENT_DATA_REF;
2109     cer->edr.root = root;
2110     cer->edr.objid = objid;
2111     cer->edr.offset = offset;
2112     cer->edr.count = count;
2113 
2114     InsertTailList(&ce->refs, &cer->list_entry);
2115 
2116     ce->count += count;
2117 }
2118 
2119 uint64_t find_extent_shared_tree_refcount(device_extension* Vcb, uint64_t address, uint64_t parent, PIRP Irp) {
2120     NTSTATUS Status;
2121     KEY searchkey;
2122     traverse_ptr tp;
2123     uint64_t inline_rc;
2124     EXTENT_ITEM* ei;
2125     uint32_t len;
2126     uint8_t* ptr;
2127 
2128     searchkey.obj_id = address;
2129     searchkey.obj_type = Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA ? TYPE_METADATA_ITEM : TYPE_EXTENT_ITEM;
2130     searchkey.offset = 0xffffffffffffffff;
2131 
2132     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
2133     if (!NT_SUCCESS(Status)) {
2134         ERR("error - find_item returned %08lx\n", Status);
2135         return 0;
2136     }
2137 
2138     if (tp.item->key.obj_id != searchkey.obj_id || (tp.item->key.obj_type != TYPE_EXTENT_ITEM && tp.item->key.obj_type != TYPE_METADATA_ITEM)) {
2139         TRACE("could not find address %I64x in extent tree\n", address);
2140         return 0;
2141     }
2142 
2143     if (tp.item->key.obj_type == TYPE_EXTENT_ITEM && tp.item->key.offset != Vcb->superblock.node_size) {
2144         ERR("extent %I64x had size %I64x, not %x as expected\n", address, tp.item->key.offset, Vcb->superblock.node_size);
2145         return 0;
2146     }
2147 
2148     if (tp.item->size < sizeof(EXTENT_ITEM)) {
2149         ERR("(%I64x,%x,%I64x): size was %u, 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));
2150         return 0;
2151     }
2152 
2153     ei = (EXTENT_ITEM*)tp.item->data;
2154     inline_rc = 0;
2155 
2156     len = tp.item->size - sizeof(EXTENT_ITEM);
2157     ptr = (uint8_t*)&ei[1];
2158 
2159     if (searchkey.obj_type == TYPE_EXTENT_ITEM && ei->flags & EXTENT_ITEM_TREE_BLOCK) {
2160         if (tp.item->size < sizeof(EXTENT_ITEM) + sizeof(EXTENT_ITEM2)) {
2161             ERR("(%I64x,%x,%I64x): size was %u, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset,
2162                                                                           tp.item->size, sizeof(EXTENT_ITEM) + sizeof(EXTENT_ITEM2));
2163             return 0;
2164         }
2165 
2166         len -= sizeof(EXTENT_ITEM2);
2167         ptr += sizeof(EXTENT_ITEM2);
2168     }
2169 
2170     while (len > 0) {
2171         uint8_t secttype = *ptr;
2172         ULONG sectlen = get_extent_data_len(secttype);
2173         uint64_t sectcount = get_extent_data_refcount(secttype, ptr + sizeof(uint8_t));
2174 
2175         len--;
2176 
2177         if (sectlen > len) {
2178             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);
2179             return 0;
2180         }
2181 
2182         if (sectlen == 0) {
2183             ERR("(%I64x,%x,%I64x): unrecognized extent type %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, secttype);
2184             return 0;
2185         }
2186 
2187         if (secttype == TYPE_SHARED_BLOCK_REF) {
2188             SHARED_BLOCK_REF* sectsbr = (SHARED_BLOCK_REF*)(ptr + sizeof(uint8_t));
2189 
2190             if (sectsbr->offset == parent)
2191                 return 1;
2192         }
2193 
2194         len -= sectlen;
2195         ptr += sizeof(uint8_t) + sectlen;
2196         inline_rc += sectcount;
2197     }
2198 
2199     // FIXME - what if old?
2200 
2201     if (inline_rc == ei->refcount)
2202         return 0;
2203 
2204     searchkey.obj_id = address;
2205     searchkey.obj_type = TYPE_SHARED_BLOCK_REF;
2206     searchkey.offset = parent;
2207 
2208     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
2209     if (!NT_SUCCESS(Status)) {
2210         ERR("error - find_item returned %08lx\n", Status);
2211         return 0;
2212     }
2213 
2214     if (!keycmp(searchkey, tp.item->key))
2215         return 1;
2216 
2217     return 0;
2218 }
2219 
2220 uint32_t find_extent_shared_data_refcount(device_extension* Vcb, uint64_t address, uint64_t parent, PIRP Irp) {
2221     NTSTATUS Status;
2222     KEY searchkey;
2223     traverse_ptr tp;
2224     uint64_t inline_rc;
2225     EXTENT_ITEM* ei;
2226     uint32_t len;
2227     uint8_t* ptr;
2228 
2229     searchkey.obj_id = address;
2230     searchkey.obj_type = Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA ? TYPE_METADATA_ITEM : TYPE_EXTENT_ITEM;
2231     searchkey.offset = 0xffffffffffffffff;
2232 
2233     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
2234     if (!NT_SUCCESS(Status)) {
2235         ERR("error - find_item returned %08lx\n", Status);
2236         return 0;
2237     }
2238 
2239     if (tp.item->key.obj_id != searchkey.obj_id || (tp.item->key.obj_type != TYPE_EXTENT_ITEM && tp.item->key.obj_type != TYPE_METADATA_ITEM)) {
2240         TRACE("could not find address %I64x in extent tree\n", address);
2241         return 0;
2242     }
2243 
2244     if (tp.item->size < sizeof(EXTENT_ITEM)) {
2245         ERR("(%I64x,%x,%I64x): size was %u, 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));
2246         return 0;
2247     }
2248 
2249     ei = (EXTENT_ITEM*)tp.item->data;
2250     inline_rc = 0;
2251 
2252     len = tp.item->size - sizeof(EXTENT_ITEM);
2253     ptr = (uint8_t*)&ei[1];
2254 
2255     while (len > 0) {
2256         uint8_t secttype = *ptr;
2257         ULONG sectlen = get_extent_data_len(secttype);
2258         uint64_t sectcount = get_extent_data_refcount(secttype, ptr + sizeof(uint8_t));
2259 
2260         len--;
2261 
2262         if (sectlen > len) {
2263             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);
2264             return 0;
2265         }
2266 
2267         if (sectlen == 0) {
2268             ERR("(%I64x,%x,%I64x): unrecognized extent type %x\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, secttype);
2269             return 0;
2270         }
2271 
2272         if (secttype == TYPE_SHARED_DATA_REF) {
2273             SHARED_DATA_REF* sectsdr = (SHARED_DATA_REF*)(ptr + sizeof(uint8_t));
2274 
2275             if (sectsdr->offset == parent)
2276                 return sectsdr->count;
2277         }
2278 
2279         len -= sectlen;
2280         ptr += sizeof(uint8_t) + sectlen;
2281         inline_rc += sectcount;
2282     }
2283 
2284     // FIXME - what if old?
2285 
2286     if (inline_rc == ei->refcount)
2287         return 0;
2288 
2289     searchkey.obj_id = address;
2290     searchkey.obj_type = TYPE_SHARED_DATA_REF;
2291     searchkey.offset = parent;
2292 
2293     Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp);
2294     if (!NT_SUCCESS(Status)) {
2295         ERR("error - find_item returned %08lx\n", Status);
2296         return 0;
2297     }
2298 
2299     if (!keycmp(searchkey, tp.item->key)) {
2300         if (tp.item->size < sizeof(uint32_t))
2301             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(uint32_t));
2302         else {
2303             uint32_t* count = (uint32_t*)tp.item->data;
2304             return *count;
2305         }
2306     }
2307 
2308     return 0;
2309 }
2310