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