[BTRFS]
[reactos.git] / reactos / drivers / filesystems / btrfs / write.c
1 /* Copyright (c) Mark Harmstone 2016
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 #define MAX_CSUM_SIZE (4096 - sizeof(tree_header) - sizeof(leaf_node))
21
22 // #define DEBUG_WRITE_LOOPS
23
24 // BOOL did_split;
25 BOOL chunk_test = FALSE;
26
27 typedef struct {
28 KEVENT Event;
29 IO_STATUS_BLOCK iosb;
30 } write_context;
31
32 typedef struct {
33 EXTENT_ITEM ei;
34 UINT8 type;
35 EXTENT_DATA_REF edr;
36 } EXTENT_ITEM_DATA_REF;
37
38 typedef struct {
39 EXTENT_ITEM_TREE eit;
40 UINT8 type;
41 TREE_BLOCK_REF tbr;
42 } EXTENT_ITEM_TREE2;
43
44 typedef struct {
45 EXTENT_ITEM ei;
46 UINT8 type;
47 TREE_BLOCK_REF tbr;
48 } EXTENT_ITEM_SKINNY_METADATA;
49
50 // static BOOL extent_item_is_shared(EXTENT_ITEM* ei, ULONG len);
51 static NTSTATUS STDCALL write_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr);
52 static void update_checksum_tree(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback);
53 static void remove_fcb_extent(fcb* fcb, extent* ext, LIST_ENTRY* rollback);
54
55 static NTSTATUS STDCALL write_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
56 write_context* context = conptr;
57
58 context->iosb = Irp->IoStatus;
59 KeSetEvent(&context->Event, 0, FALSE);
60
61 // return STATUS_SUCCESS;
62 return STATUS_MORE_PROCESSING_REQUIRED;
63 }
64
65 static NTSTATUS STDCALL write_data_phys(PDEVICE_OBJECT device, UINT64 address, void* data, UINT32 length) {
66 NTSTATUS Status;
67 LARGE_INTEGER offset;
68 PIRP Irp;
69 PIO_STACK_LOCATION IrpSp;
70 write_context* context = NULL;
71
72 TRACE("(%p, %llx, %p, %x)\n", device, address, data, length);
73
74 context = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_context), ALLOC_TAG);
75 if (!context) {
76 ERR("out of memory\n");
77 return STATUS_INSUFFICIENT_RESOURCES;
78 }
79
80 RtlZeroMemory(context, sizeof(write_context));
81
82 KeInitializeEvent(&context->Event, NotificationEvent, FALSE);
83
84 offset.QuadPart = address;
85
86 // Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, Vcb->device, data, length, &offset, NULL, &context->iosb);
87
88 Irp = IoAllocateIrp(device->StackSize, FALSE);
89
90 if (!Irp) {
91 ERR("IoAllocateIrp failed\n");
92 Status = STATUS_INTERNAL_ERROR;
93 goto exit2;
94 }
95
96 IrpSp = IoGetNextIrpStackLocation(Irp);
97 IrpSp->MajorFunction = IRP_MJ_WRITE;
98
99 if (device->Flags & DO_BUFFERED_IO) {
100 Irp->AssociatedIrp.SystemBuffer = data;
101
102 Irp->Flags = IRP_BUFFERED_IO;
103 } else if (device->Flags & DO_DIRECT_IO) {
104 Irp->MdlAddress = IoAllocateMdl(data, length, FALSE, FALSE, NULL);
105 if (!Irp->MdlAddress) {
106 DbgPrint("IoAllocateMdl failed\n");
107 goto exit;
108 }
109
110 MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess);
111 } else {
112 Irp->UserBuffer = data;
113 }
114
115 IrpSp->Parameters.Write.Length = length;
116 IrpSp->Parameters.Write.ByteOffset = offset;
117
118 Irp->UserIosb = &context->iosb;
119
120 Irp->UserEvent = &context->Event;
121
122 IoSetCompletionRoutine(Irp, write_completion, context, TRUE, TRUE, TRUE);
123
124 // FIXME - support multiple devices
125 Status = IoCallDriver(device, Irp);
126
127 if (Status == STATUS_PENDING) {
128 KeWaitForSingleObject(&context->Event, Executive, KernelMode, FALSE, NULL);
129 Status = context->iosb.Status;
130 }
131
132 if (!NT_SUCCESS(Status)) {
133 ERR("IoCallDriver returned %08x\n", Status);
134 }
135
136 if (device->Flags & DO_DIRECT_IO) {
137 MmUnlockPages(Irp->MdlAddress);
138 IoFreeMdl(Irp->MdlAddress);
139 }
140
141 exit:
142 IoFreeIrp(Irp);
143
144 exit2:
145 if (context)
146 ExFreePool(context);
147
148 return Status;
149 }
150
151 static NTSTATUS STDCALL write_superblock(device_extension* Vcb, device* device) {
152 NTSTATUS Status;
153 unsigned int i = 0;
154 UINT32 crc32;
155
156 #ifdef __REACTOS__
157 Status = STATUS_INTERNAL_ERROR;
158 #endif
159
160 RtlCopyMemory(&Vcb->superblock.dev_item, &device->devitem, sizeof(DEV_ITEM));
161
162 // FIXME - only write one superblock if on SSD (?)
163 while (superblock_addrs[i] > 0 && device->length >= superblock_addrs[i] + sizeof(superblock)) {
164 TRACE("writing superblock %u\n", i);
165
166 Vcb->superblock.sb_phys_addr = superblock_addrs[i];
167
168 crc32 = calc_crc32c(0xffffffff, (UINT8*)&Vcb->superblock.uuid, (ULONG)sizeof(superblock) - sizeof(Vcb->superblock.checksum));
169 crc32 = ~crc32;
170 TRACE("crc32 is %08x\n", crc32);
171 RtlCopyMemory(&Vcb->superblock.checksum, &crc32, sizeof(UINT32));
172
173 Status = write_data_phys(device->devobj, superblock_addrs[i], &Vcb->superblock, sizeof(superblock));
174
175 if (!NT_SUCCESS(Status))
176 break;
177
178 i++;
179 }
180
181 if (i == 0) {
182 ERR("no superblocks written!\n");
183 }
184
185 return Status;
186 }
187
188 static BOOL find_address_in_chunk(device_extension* Vcb, chunk* c, UINT64 length, UINT64* address) {
189 LIST_ENTRY* le;
190 space* s;
191
192 TRACE("(%p, %llx, %llx, %p)\n", Vcb, c->offset, length, address);
193
194 if (IsListEmpty(&c->space_size))
195 return FALSE;
196
197 le = c->space_size.Flink;
198 while (le != &c->space_size) {
199 s = CONTAINING_RECORD(le, space, list_entry_size);
200
201 if (s->size == length) {
202 *address = s->address;
203 return TRUE;
204 } else if (s->size < length) {
205 if (le == c->space_size.Flink)
206 return FALSE;
207
208 s = CONTAINING_RECORD(le->Blink, space, list_entry_size);
209
210 *address = s->address;
211 return TRUE;
212 }
213
214 le = le->Flink;
215 }
216
217 s = CONTAINING_RECORD(c->space_size.Blink, space, list_entry_size);
218
219 if (s->size > length) {
220 *address = s->address;
221 return TRUE;
222 }
223
224 return FALSE;
225 }
226
227 chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address) {
228 LIST_ENTRY* le2;
229 chunk* c;
230
231 ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE);
232
233 le2 = Vcb->chunks.Flink;
234 while (le2 != &Vcb->chunks) {
235 c = CONTAINING_RECORD(le2, chunk, list_entry);
236
237 // TRACE("chunk: %llx, %llx\n", c->offset, c->chunk_item->size);
238
239 if (address >= c->offset && address < c->offset + c->chunk_item->size) {
240 ExReleaseResourceLite(&Vcb->chunk_lock);
241 return c;
242 }
243
244 le2 = le2->Flink;
245 }
246
247 ExReleaseResourceLite(&Vcb->chunk_lock);
248
249 return NULL;
250 }
251
252 typedef struct {
253 space* dh;
254 device* device;
255 } stripe;
256
257 static UINT64 find_new_chunk_address(device_extension* Vcb, UINT64 size) {
258 UINT64 lastaddr;
259 LIST_ENTRY* le;
260
261 lastaddr = 0;
262
263 le = Vcb->chunks.Flink;
264 while (le != &Vcb->chunks) {
265 chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
266
267 if (c->offset >= lastaddr + size)
268 return lastaddr;
269
270 lastaddr = c->offset + c->chunk_item->size;
271
272 le = le->Flink;
273 }
274
275 return lastaddr;
276 }
277
278 static NTSTATUS update_dev_item(device_extension* Vcb, device* device, PIRP Irp, LIST_ENTRY* rollback) {
279 KEY searchkey;
280 traverse_ptr tp;
281 DEV_ITEM* di;
282 NTSTATUS Status;
283
284 searchkey.obj_id = 1;
285 searchkey.obj_type = TYPE_DEV_ITEM;
286 searchkey.offset = device->devitem.dev_id;
287
288 Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
289 if (!NT_SUCCESS(Status)) {
290 ERR("error - find_item returned %08x\n", Status);
291 return Status;
292 }
293
294 if (keycmp(&tp.item->key, &searchkey)) {
295 ERR("error - could not find DEV_ITEM for device %llx\n", device->devitem.dev_id);
296 return STATUS_INTERNAL_ERROR;
297 }
298
299 delete_tree_item(Vcb, &tp, rollback);
300
301 di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG);
302 if (!di) {
303 ERR("out of memory\n");
304 return STATUS_INSUFFICIENT_RESOURCES;
305 }
306
307 RtlCopyMemory(di, &device->devitem, sizeof(DEV_ITEM));
308
309 if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, device->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, Irp, rollback)) {
310 ERR("insert_tree_item failed\n");
311 return STATUS_INTERNAL_ERROR;
312 }
313
314 return STATUS_SUCCESS;
315 }
316
317 static void regen_bootstrap(device_extension* Vcb) {
318 sys_chunk* sc2;
319 USHORT i = 0;
320 LIST_ENTRY* le;
321
322 i = 0;
323 le = Vcb->sys_chunks.Flink;
324 while (le != &Vcb->sys_chunks) {
325 sc2 = CONTAINING_RECORD(le, sys_chunk, list_entry);
326
327 TRACE("%llx,%x,%llx\n", sc2->key.obj_id, sc2->key.obj_type, sc2->key.offset);
328
329 RtlCopyMemory(&Vcb->superblock.sys_chunk_array[i], &sc2->key, sizeof(KEY));
330 i += sizeof(KEY);
331
332 RtlCopyMemory(&Vcb->superblock.sys_chunk_array[i], sc2->data, sc2->size);
333 i += sc2->size;
334
335 le = le->Flink;
336 }
337 }
338
339 static NTSTATUS add_to_bootstrap(device_extension* Vcb, UINT64 obj_id, UINT8 obj_type, UINT64 offset, void* data, ULONG size) {
340 sys_chunk *sc, *sc2;
341 LIST_ENTRY* le;
342
343 if (Vcb->superblock.n + sizeof(KEY) + size > SYS_CHUNK_ARRAY_SIZE) {
344 ERR("error - bootstrap is full\n");
345 return STATUS_INTERNAL_ERROR;
346 }
347
348 sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG);
349 if (!sc) {
350 ERR("out of memory\n");
351 return STATUS_INSUFFICIENT_RESOURCES;
352 }
353
354 sc->key.obj_id = obj_id;
355 sc->key.obj_type = obj_type;
356 sc->key.offset = offset;
357 sc->size = size;
358 sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG);
359 if (!sc->data) {
360 ERR("out of memory\n");
361 ExFreePool(sc);
362 return STATUS_INSUFFICIENT_RESOURCES;
363 }
364
365 RtlCopyMemory(sc->data, data, sc->size);
366
367 le = Vcb->sys_chunks.Flink;
368 while (le != &Vcb->sys_chunks) {
369 sc2 = CONTAINING_RECORD(le, sys_chunk, list_entry);
370
371 if (keycmp(&sc2->key, &sc->key) == 1)
372 break;
373
374 le = le->Flink;
375 }
376 InsertTailList(le, &sc->list_entry);
377
378 Vcb->superblock.n += sizeof(KEY) + size;
379
380 regen_bootstrap(Vcb);
381
382 return STATUS_SUCCESS;
383 }
384
385 static BOOL find_new_dup_stripes(device_extension* Vcb, stripe* stripes, UINT64 max_stripe_size) {
386 UINT64 j, devnum, devusage = 0xffffffffffffffff;
387 space *devdh1 = NULL, *devdh2 = NULL;
388
389 for (j = 0; j < Vcb->superblock.num_devices; j++) {
390 UINT64 usage;
391
392 usage = (Vcb->devices[j].devitem.bytes_used * 4096) / Vcb->devices[j].devitem.num_bytes;
393
394 // favour devices which have been used the least
395 if (usage < devusage) {
396 if (!IsListEmpty(&Vcb->devices[j].space)) {
397 LIST_ENTRY* le;
398 space *dh1 = NULL, *dh2 = NULL;
399
400 le = Vcb->devices[j].space.Flink;
401 while (le != &Vcb->devices[j].space) {
402 space* dh = CONTAINING_RECORD(le, space, list_entry);
403
404 if (dh->size >= max_stripe_size && (!dh1 || dh->size < dh1->size)) {
405 dh2 = dh1;
406 dh1 = dh;
407 }
408
409 le = le->Flink;
410 }
411
412 if (dh1 && (dh2 || dh1->size >= 2 * max_stripe_size)) {
413 devnum = j;
414 devusage = usage;
415 devdh1 = dh1;
416 devdh2 = dh2 ? dh2 : dh1;
417 }
418 }
419 }
420 }
421
422 if (!devdh1)
423 return FALSE;
424
425 stripes[0].device = &Vcb->devices[devnum];
426 stripes[0].dh = devdh1;
427 stripes[1].device = stripes[0].device;
428 stripes[1].dh = devdh2;
429
430 return TRUE;
431 }
432
433 static BOOL find_new_stripe(device_extension* Vcb, stripe* stripes, UINT16 i, UINT64 max_stripe_size, UINT16 type) {
434 UINT64 j, k, devnum = 0xffffffffffffffff, devusage = 0xffffffffffffffff;
435 space* devdh = NULL;
436
437 for (j = 0; j < Vcb->superblock.num_devices; j++) {
438 UINT64 usage;
439 BOOL skip = FALSE;
440
441 // skip this device if it already has a stripe
442 if (i > 0) {
443 for (k = 0; k < i; k++) {
444 if (stripes[k].device == &Vcb->devices[j]) {
445 skip = TRUE;
446 break;
447 }
448 }
449 }
450
451 if (!skip) {
452 usage = (Vcb->devices[j].devitem.bytes_used * 4096) / Vcb->devices[j].devitem.num_bytes;
453
454 // favour devices which have been used the least
455 if (usage < devusage) {
456 if (!IsListEmpty(&Vcb->devices[j].space)) {
457 LIST_ENTRY* le;
458
459 le = Vcb->devices[j].space.Flink;
460 while (le != &Vcb->devices[j].space) {
461 space* dh = CONTAINING_RECORD(le, space, list_entry);
462
463 if ((devnum != j && dh->size >= max_stripe_size) ||
464 (devnum == j && dh->size >= max_stripe_size && dh->size < devdh->size)
465 ) {
466 devdh = dh;
467 devnum = j;
468 devusage = usage;
469 }
470
471 le = le->Flink;
472 }
473 }
474 }
475 }
476 }
477
478 if (!devdh)
479 return FALSE;
480
481 stripes[i].dh = devdh;
482 stripes[i].device = &Vcb->devices[devnum];
483
484 return TRUE;
485 }
486
487 chunk* alloc_chunk(device_extension* Vcb, UINT64 flags) {
488 UINT64 max_stripe_size, max_chunk_size, stripe_size, stripe_length, factor;
489 UINT64 total_size = 0, i, logaddr;
490 UINT16 type, num_stripes, sub_stripes, max_stripes, min_stripes;
491 stripe* stripes = NULL;
492 ULONG cisize;
493 CHUNK_ITEM_STRIPE* cis;
494 chunk* c = NULL;
495 space* s = NULL;
496 BOOL success = FALSE;
497
498 ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, TRUE);
499
500 for (i = 0; i < Vcb->superblock.num_devices; i++) {
501 total_size += Vcb->devices[i].devitem.num_bytes;
502 }
503 TRACE("total_size = %llx\n", total_size);
504
505 // We purposely check for DATA first - mixed blocks have the same size
506 // as DATA ones.
507 if (flags & BLOCK_FLAG_DATA) {
508 max_stripe_size = 0x40000000; // 1 GB
509 max_chunk_size = 10 * max_stripe_size;
510 } else if (flags & BLOCK_FLAG_METADATA) {
511 if (total_size > 0xC80000000) // 50 GB
512 max_stripe_size = 0x40000000; // 1 GB
513 else
514 max_stripe_size = 0x10000000; // 256 MB
515
516 max_chunk_size = max_stripe_size;
517 } else if (flags & BLOCK_FLAG_SYSTEM) {
518 max_stripe_size = 0x2000000; // 32 MB
519 max_chunk_size = 2 * max_stripe_size;
520 }
521
522 max_chunk_size = min(max_chunk_size, total_size / 10); // cap at 10%
523
524 TRACE("would allocate a new chunk of %llx bytes and stripe %llx\n", max_chunk_size, max_stripe_size);
525
526 if (flags & BLOCK_FLAG_DUPLICATE) {
527 min_stripes = 2;
528 max_stripes = 2;
529 sub_stripes = 0;
530 type = BLOCK_FLAG_DUPLICATE;
531 } else if (flags & BLOCK_FLAG_RAID0) {
532 min_stripes = 2;
533 max_stripes = Vcb->superblock.num_devices;
534 sub_stripes = 0;
535 type = BLOCK_FLAG_RAID0;
536 } else if (flags & BLOCK_FLAG_RAID1) {
537 min_stripes = 2;
538 max_stripes = 2;
539 sub_stripes = 1;
540 type = BLOCK_FLAG_RAID1;
541 } else if (flags & BLOCK_FLAG_RAID10) {
542 min_stripes = 4;
543 max_stripes = Vcb->superblock.num_devices;
544 sub_stripes = 2;
545 type = BLOCK_FLAG_RAID10;
546 } else if (flags & BLOCK_FLAG_RAID5) {
547 FIXME("RAID5 not yet supported\n");
548 goto end;
549 } else if (flags & BLOCK_FLAG_RAID6) {
550 FIXME("RAID6 not yet supported\n");
551 goto end;
552 } else { // SINGLE
553 min_stripes = 1;
554 max_stripes = 1;
555 sub_stripes = 1;
556 type = 0;
557 }
558
559 stripes = ExAllocatePoolWithTag(PagedPool, sizeof(stripe) * max_stripes, ALLOC_TAG);
560 if (!stripes) {
561 ERR("out of memory\n");
562 goto end;
563 }
564
565 num_stripes = 0;
566
567 if (type == BLOCK_FLAG_DUPLICATE) {
568 if (!find_new_dup_stripes(Vcb, stripes, max_stripe_size))
569 goto end;
570 else
571 num_stripes = max_stripes;
572 } else {
573 for (i = 0; i < max_stripes; i++) {
574 if (!find_new_stripe(Vcb, stripes, i, max_stripe_size, type))
575 break;
576 else
577 num_stripes++;
578 }
579 }
580
581 // for RAID10, round down to an even number of stripes
582 if (type == BLOCK_FLAG_RAID10 && (num_stripes % sub_stripes) != 0) {
583 num_stripes -= num_stripes % sub_stripes;
584 }
585
586 if (num_stripes < min_stripes) {
587 WARN("found %u stripes, needed at least %u\n", num_stripes, min_stripes);
588 goto end;
589 }
590
591 c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG);
592 if (!c) {
593 ERR("out of memory\n");
594 goto end;
595 }
596
597 cisize = sizeof(CHUNK_ITEM) + (num_stripes * sizeof(CHUNK_ITEM_STRIPE));
598 c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, cisize, ALLOC_TAG);
599 if (!c->chunk_item) {
600 ERR("out of memory\n");
601 goto end;
602 }
603
604 stripe_length = 0x10000; // FIXME? BTRFS_STRIPE_LEN in kernel
605
606 stripe_size = max_stripe_size;
607 for (i = 0; i < num_stripes; i++) {
608 if (stripes[i].dh->size < stripe_size)
609 stripe_size = stripes[i].dh->size;
610 }
611
612 if (type == 0 || type == BLOCK_FLAG_DUPLICATE || type == BLOCK_FLAG_RAID1)
613 factor = 1;
614 else if (type == BLOCK_FLAG_RAID0)
615 factor = num_stripes;
616 else if (type == BLOCK_FLAG_RAID10)
617 factor = num_stripes / sub_stripes;
618
619 if (stripe_size * factor > max_chunk_size)
620 stripe_size = max_chunk_size / factor;
621
622 if (stripe_size % stripe_length > 0)
623 stripe_size -= stripe_size % stripe_length;
624
625 if (stripe_size == 0)
626 goto end;
627
628 c->chunk_item->size = stripe_size * factor;
629 c->chunk_item->root_id = Vcb->extent_root->id;
630 c->chunk_item->stripe_length = stripe_length;
631 c->chunk_item->type = flags;
632 c->chunk_item->opt_io_alignment = c->chunk_item->stripe_length;
633 c->chunk_item->opt_io_width = c->chunk_item->stripe_length;
634 c->chunk_item->sector_size = stripes[0].device->devitem.minimal_io_size;
635 c->chunk_item->num_stripes = num_stripes;
636 c->chunk_item->sub_stripes = sub_stripes;
637
638 c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * num_stripes, ALLOC_TAG);
639 if (!c->devices) {
640 ERR("out of memory\n");
641 goto end;
642 }
643
644 cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
645 for (i = 0; i < num_stripes; i++) {
646 cis[i].dev_id = stripes[i].device->devitem.dev_id;
647
648 if (type == BLOCK_FLAG_DUPLICATE && i == 1 && stripes[i].dh == stripes[0].dh)
649 cis[i].offset = stripes[0].dh->address + stripe_size;
650 else
651 cis[i].offset = stripes[i].dh->address;
652
653 cis[i].dev_uuid = stripes[i].device->devitem.device_uuid;
654
655 c->devices[i] = stripes[i].device;
656 }
657
658 logaddr = find_new_chunk_address(Vcb, c->chunk_item->size);
659
660 Vcb->superblock.chunk_root_generation = Vcb->superblock.generation;
661
662 c->size = cisize;
663 c->offset = logaddr;
664 c->used = c->oldused = 0;
665 c->cache = NULL;
666 InitializeListHead(&c->space);
667 InitializeListHead(&c->space_size);
668 InitializeListHead(&c->deleting);
669 InitializeListHead(&c->changed_extents);
670
671 ExInitializeResourceLite(&c->lock);
672 ExInitializeResourceLite(&c->changed_extents_lock);
673
674 s = ExAllocatePoolWithTag(NonPagedPool, sizeof(space), ALLOC_TAG);
675 if (!s) {
676 ERR("out of memory\n");
677 goto end;
678 }
679
680 s->address = c->offset;
681 s->size = c->chunk_item->size;
682 InsertTailList(&c->space, &s->list_entry);
683 InsertTailList(&c->space_size, &s->list_entry_size);
684
685 protect_superblocks(Vcb, c);
686
687 for (i = 0; i < num_stripes; i++) {
688 stripes[i].device->devitem.bytes_used += stripe_size;
689
690 space_list_subtract2(&stripes[i].device->space, NULL, cis[i].offset, stripe_size, NULL);
691 }
692
693 success = TRUE;
694
695 end:
696 if (stripes)
697 ExFreePool(stripes);
698
699 if (!success) {
700 if (c && c->chunk_item) ExFreePool(c->chunk_item);
701 if (c) ExFreePool(c);
702 if (s) ExFreePool(s);
703 } else {
704 LIST_ENTRY* le;
705 BOOL done = FALSE;
706
707 le = Vcb->chunks.Flink;
708 while (le != &Vcb->chunks) {
709 chunk* c2 = CONTAINING_RECORD(le, chunk, list_entry);
710
711 if (c2->offset > c->offset) {
712 InsertHeadList(le->Blink, &c->list_entry);
713 done = TRUE;
714 break;
715 }
716
717 le = le->Flink;
718 }
719
720 if (!done)
721 InsertTailList(&Vcb->chunks, &c->list_entry);
722
723 c->created = TRUE;
724 InsertTailList(&Vcb->chunks_changed, &c->list_entry_changed);
725 }
726
727 ExReleaseResourceLite(&Vcb->chunk_lock);
728
729 return success ? c : NULL;
730 }
731
732 NTSTATUS STDCALL write_data(device_extension* Vcb, UINT64 address, void* data, BOOL need_free, UINT32 length, write_data_context* wtc, PIRP Irp, chunk* c) {
733 NTSTATUS Status;
734 UINT32 i;
735 CHUNK_ITEM_STRIPE* cis;
736 write_data_stripe* stripe;
737 UINT64 *stripestart = NULL, *stripeend = NULL;
738 UINT8** stripedata = NULL;
739 BOOL need_free2;
740
741 TRACE("(%p, %llx, %p, %x)\n", Vcb, address, data, length);
742
743 if (!c) {
744 c = get_chunk_from_address(Vcb, address);
745 if (!c) {
746 ERR("could not get chunk for address %llx\n", address);
747 return STATUS_INTERNAL_ERROR;
748 }
749 }
750
751 if (c->chunk_item->type & BLOCK_FLAG_RAID5) {
752 FIXME("RAID5 not yet supported\n");
753 return STATUS_NOT_IMPLEMENTED;
754 } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
755 FIXME("RAID6 not yet supported\n");
756 return STATUS_NOT_IMPLEMENTED;
757 }
758
759 stripestart = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes, ALLOC_TAG);
760 if (!stripestart) {
761 ERR("out of memory\n");
762 return STATUS_INSUFFICIENT_RESOURCES;
763 }
764
765 stripeend = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes, ALLOC_TAG);
766 if (!stripeend) {
767 ERR("out of memory\n");
768 ExFreePool(stripestart);
769 return STATUS_INSUFFICIENT_RESOURCES;
770 }
771
772 stripedata = ExAllocatePoolWithTag(PagedPool, sizeof(UINT8*) * c->chunk_item->num_stripes, ALLOC_TAG);
773 if (!stripedata) {
774 ERR("out of memory\n");
775 ExFreePool(stripeend);
776 ExFreePool(stripestart);
777 return STATUS_INSUFFICIENT_RESOURCES;
778 }
779 RtlZeroMemory(stripedata, sizeof(UINT8*) * c->chunk_item->num_stripes);
780
781 cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
782
783 if (c->chunk_item->type & BLOCK_FLAG_RAID0) {
784 UINT64 startoff, endoff;
785 UINT16 startoffstripe, endoffstripe, stripenum;
786 UINT64 pos, *stripeoff;
787
788 stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes, ALLOC_TAG);
789 if (!stripeoff) {
790 ERR("out of memory\n");
791 ExFreePool(stripedata);
792 ExFreePool(stripeend);
793 ExFreePool(stripestart);
794 return STATUS_INSUFFICIENT_RESOURCES;
795 }
796
797 get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &startoff, &startoffstripe);
798 get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &endoff, &endoffstripe);
799
800 for (i = 0; i < c->chunk_item->num_stripes; i++) {
801 if (startoffstripe > i) {
802 stripestart[i] = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
803 } else if (startoffstripe == i) {
804 stripestart[i] = startoff;
805 } else {
806 stripestart[i] = startoff - (startoff % c->chunk_item->stripe_length);
807 }
808
809 if (endoffstripe > i) {
810 stripeend[i] = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
811 } else if (endoffstripe == i) {
812 stripeend[i] = endoff + 1;
813 } else {
814 stripeend[i] = endoff - (endoff % c->chunk_item->stripe_length);
815 }
816
817 if (stripestart[i] != stripeend[i]) {
818 stripedata[i] = ExAllocatePoolWithTag(NonPagedPool, stripeend[i] - stripestart[i], ALLOC_TAG);
819
820 if (!stripedata[i]) {
821 ERR("out of memory\n");
822 ExFreePool(stripeoff);
823 Status = STATUS_INSUFFICIENT_RESOURCES;
824 goto end;
825 }
826 }
827 }
828
829 pos = 0;
830 RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes);
831
832 stripenum = startoffstripe;
833 while (pos < length) {
834 if (pos == 0) {
835 UINT32 writelen = min(stripeend[stripenum] - stripestart[stripenum],
836 c->chunk_item->stripe_length - (stripestart[stripenum] % c->chunk_item->stripe_length));
837
838 RtlCopyMemory(stripedata[stripenum], data, writelen);
839 stripeoff[stripenum] += writelen;
840 pos += writelen;
841 } else if (length - pos < c->chunk_item->stripe_length) {
842 RtlCopyMemory(stripedata[stripenum] + stripeoff[stripenum], (UINT8*)data + pos, length - pos);
843 break;
844 } else {
845 RtlCopyMemory(stripedata[stripenum] + stripeoff[stripenum], (UINT8*)data + pos, c->chunk_item->stripe_length);
846 stripeoff[stripenum] += c->chunk_item->stripe_length;
847 pos += c->chunk_item->stripe_length;
848 }
849
850 stripenum = (stripenum + 1) % c->chunk_item->num_stripes;
851 }
852
853 ExFreePool(stripeoff);
854
855 if (need_free)
856 ExFreePool(data);
857
858 need_free2 = TRUE;
859 } else if (c->chunk_item->type & BLOCK_FLAG_RAID10) {
860 UINT64 startoff, endoff;
861 UINT16 startoffstripe, endoffstripe, stripenum;
862 UINT64 pos, *stripeoff;
863
864 stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes / c->chunk_item->sub_stripes, ALLOC_TAG);
865 if (!stripeoff) {
866 ERR("out of memory\n");
867 ExFreePool(stripedata);
868 ExFreePool(stripeend);
869 ExFreePool(stripestart);
870 return STATUS_INSUFFICIENT_RESOURCES;
871 }
872
873 get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes / c->chunk_item->sub_stripes, &startoff, &startoffstripe);
874 get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes / c->chunk_item->sub_stripes, &endoff, &endoffstripe);
875
876 startoffstripe *= c->chunk_item->sub_stripes;
877 endoffstripe *= c->chunk_item->sub_stripes;
878
879 for (i = 0; i < c->chunk_item->num_stripes; i += c->chunk_item->sub_stripes) {
880 UINT16 j;
881
882 if (startoffstripe > i) {
883 stripestart[i] = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
884 } else if (startoffstripe == i) {
885 stripestart[i] = startoff;
886 } else {
887 stripestart[i] = startoff - (startoff % c->chunk_item->stripe_length);
888 }
889
890 if (endoffstripe > i) {
891 stripeend[i] = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
892 } else if (endoffstripe == i) {
893 stripeend[i] = endoff + 1;
894 } else {
895 stripeend[i] = endoff - (endoff % c->chunk_item->stripe_length);
896 }
897
898 if (stripestart[i] != stripeend[i]) {
899 stripedata[i] = ExAllocatePoolWithTag(NonPagedPool, stripeend[i] - stripestart[i], ALLOC_TAG);
900
901 if (!stripedata[i]) {
902 ERR("out of memory\n");
903 ExFreePool(stripeoff);
904 Status = STATUS_INSUFFICIENT_RESOURCES;
905 goto end;
906 }
907 }
908
909 for (j = 1; j < c->chunk_item->sub_stripes; j++) {
910 stripestart[i+j] = stripestart[i];
911 stripeend[i+j] = stripeend[i];
912 stripedata[i+j] = stripedata[i];
913 }
914 }
915
916 pos = 0;
917 RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes / c->chunk_item->sub_stripes);
918
919 stripenum = startoffstripe / c->chunk_item->sub_stripes;
920 while (pos < length) {
921 if (pos == 0) {
922 UINT32 writelen = min(stripeend[stripenum * c->chunk_item->sub_stripes] - stripestart[stripenum * c->chunk_item->sub_stripes],
923 c->chunk_item->stripe_length - (stripestart[stripenum * c->chunk_item->sub_stripes] % c->chunk_item->stripe_length));
924
925 RtlCopyMemory(stripedata[stripenum * c->chunk_item->sub_stripes], data, writelen);
926 stripeoff[stripenum] += writelen;
927 pos += writelen;
928 } else if (length - pos < c->chunk_item->stripe_length) {
929 RtlCopyMemory(stripedata[stripenum * c->chunk_item->sub_stripes] + stripeoff[stripenum], (UINT8*)data + pos, length - pos);
930 break;
931 } else {
932 RtlCopyMemory(stripedata[stripenum * c->chunk_item->sub_stripes] + stripeoff[stripenum], (UINT8*)data + pos, c->chunk_item->stripe_length);
933 stripeoff[stripenum] += c->chunk_item->stripe_length;
934 pos += c->chunk_item->stripe_length;
935 }
936
937 stripenum = (stripenum + 1) % (c->chunk_item->num_stripes / c->chunk_item->sub_stripes);
938 }
939
940 ExFreePool(stripeoff);
941
942 if (need_free)
943 ExFreePool(data);
944
945 need_free2 = TRUE;
946 } else {
947 for (i = 0; i < c->chunk_item->num_stripes; i++) {
948 stripestart[i] = address - c->offset;
949 stripeend[i] = stripestart[i] + length;
950 stripedata[i] = data;
951 }
952 need_free2 = need_free;
953 }
954
955 for (i = 0; i < c->chunk_item->num_stripes; i++) {
956 PIO_STACK_LOCATION IrpSp;
957
958 // FIXME - handle missing devices
959
960 stripe = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_stripe), ALLOC_TAG);
961 if (!stripe) {
962 ERR("out of memory\n");
963 Status = STATUS_INSUFFICIENT_RESOURCES;
964 goto end;
965 }
966
967 if (stripestart[i] == stripeend[i]) {
968 stripe->status = WriteDataStatus_Ignore;
969 stripe->Irp = NULL;
970 stripe->buf = NULL;
971 } else {
972 stripe->context = (struct _write_data_context*)wtc;
973 stripe->buf = stripedata[i];
974 stripe->need_free = need_free2;
975 stripe->device = c->devices[i];
976 RtlZeroMemory(&stripe->iosb, sizeof(IO_STATUS_BLOCK));
977 stripe->status = WriteDataStatus_Pending;
978
979 if (!Irp) {
980 stripe->Irp = IoAllocateIrp(stripe->device->devobj->StackSize, FALSE);
981
982 if (!stripe->Irp) {
983 ERR("IoAllocateIrp failed\n");
984 Status = STATUS_INTERNAL_ERROR;
985 goto end;
986 }
987 } else {
988 stripe->Irp = IoMakeAssociatedIrp(Irp, stripe->device->devobj->StackSize);
989
990 if (!stripe->Irp) {
991 ERR("IoMakeAssociatedIrp failed\n");
992 Status = STATUS_INTERNAL_ERROR;
993 goto end;
994 }
995 }
996
997 IrpSp = IoGetNextIrpStackLocation(stripe->Irp);
998 IrpSp->MajorFunction = IRP_MJ_WRITE;
999
1000 if (stripe->device->devobj->Flags & DO_BUFFERED_IO) {
1001 stripe->Irp->AssociatedIrp.SystemBuffer = stripedata[i];
1002
1003 stripe->Irp->Flags = IRP_BUFFERED_IO;
1004 } else if (stripe->device->devobj->Flags & DO_DIRECT_IO) {
1005 stripe->Irp->MdlAddress = IoAllocateMdl(stripedata[i], stripeend[i] - stripestart[i], FALSE, FALSE, NULL);
1006 if (!stripe->Irp->MdlAddress) {
1007 ERR("IoAllocateMdl failed\n");
1008 Status = STATUS_INTERNAL_ERROR;
1009 goto end;
1010 }
1011
1012 MmProbeAndLockPages(stripe->Irp->MdlAddress, KernelMode, IoWriteAccess);
1013 } else {
1014 stripe->Irp->UserBuffer = stripedata[i];
1015 }
1016
1017 IrpSp->Parameters.Write.Length = stripeend[i] - stripestart[i];
1018 IrpSp->Parameters.Write.ByteOffset.QuadPart = stripestart[i] + cis[i].offset;
1019
1020 stripe->Irp->UserIosb = &stripe->iosb;
1021 wtc->stripes_left++;
1022
1023 IoSetCompletionRoutine(stripe->Irp, write_data_completion, stripe, TRUE, TRUE, TRUE);
1024 }
1025
1026 InsertTailList(&wtc->stripes, &stripe->list_entry);
1027 }
1028
1029 Status = STATUS_SUCCESS;
1030
1031 end:
1032
1033 if (stripestart) ExFreePool(stripestart);
1034 if (stripeend) ExFreePool(stripeend);
1035 if (stripedata) ExFreePool(stripedata);
1036
1037 if (!NT_SUCCESS(Status)) {
1038 free_write_data_stripes(wtc);
1039 ExFreePool(wtc);
1040 }
1041
1042 return Status;
1043 }
1044
1045 NTSTATUS STDCALL write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp, chunk* c) {
1046 write_data_context* wtc;
1047 NTSTATUS Status;
1048
1049 wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_context), ALLOC_TAG);
1050 if (!wtc) {
1051 ERR("out of memory\n");
1052 return STATUS_INSUFFICIENT_RESOURCES;
1053 }
1054
1055 KeInitializeEvent(&wtc->Event, NotificationEvent, FALSE);
1056 InitializeListHead(&wtc->stripes);
1057 wtc->tree = FALSE;
1058 wtc->stripes_left = 0;
1059
1060 Status = write_data(Vcb, address, data, FALSE, length, wtc, Irp, c);
1061 if (!NT_SUCCESS(Status)) {
1062 ERR("write_data returned %08x\n", Status);
1063 free_write_data_stripes(wtc);
1064 ExFreePool(wtc);
1065 return Status;
1066 }
1067
1068 if (wtc->stripes.Flink != &wtc->stripes) {
1069 // launch writes and wait
1070 LIST_ENTRY* le = wtc->stripes.Flink;
1071 while (le != &wtc->stripes) {
1072 write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
1073
1074 if (stripe->status != WriteDataStatus_Ignore)
1075 IoCallDriver(stripe->device->devobj, stripe->Irp);
1076
1077 le = le->Flink;
1078 }
1079
1080 KeWaitForSingleObject(&wtc->Event, Executive, KernelMode, FALSE, NULL);
1081
1082 le = wtc->stripes.Flink;
1083 while (le != &wtc->stripes) {
1084 write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
1085
1086 if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) {
1087 Status = stripe->iosb.Status;
1088 break;
1089 }
1090
1091 le = le->Flink;
1092 }
1093
1094 free_write_data_stripes(wtc);
1095 }
1096
1097 ExFreePool(wtc);
1098
1099 return STATUS_SUCCESS;
1100 }
1101
1102 static void clean_space_cache_chunk(device_extension* Vcb, chunk* c) {
1103 // FIXME - loop through c->deleting and do TRIM if device supports it
1104 // FIXME - also find way of doing TRIM of dropped chunks
1105
1106 while (!IsListEmpty(&c->deleting)) {
1107 space* s = CONTAINING_RECORD(c->deleting.Flink, space, list_entry);
1108
1109 RemoveEntryList(&s->list_entry);
1110 ExFreePool(s);
1111 }
1112 }
1113
1114 static void clean_space_cache(device_extension* Vcb) {
1115 chunk* c;
1116
1117 TRACE("(%p)\n", Vcb);
1118
1119 while (!IsListEmpty(&Vcb->chunks_changed)) {
1120 c = CONTAINING_RECORD(Vcb->chunks_changed.Flink, chunk, list_entry_changed);
1121
1122 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
1123
1124 clean_space_cache_chunk(Vcb, c);
1125 RemoveEntryList(&c->list_entry_changed);
1126 c->list_entry_changed.Flink = NULL;
1127
1128 ExReleaseResourceLite(&c->lock);
1129 }
1130 }
1131
1132 static BOOL trees_consistent(device_extension* Vcb, LIST_ENTRY* rollback) {
1133 ULONG maxsize = Vcb->superblock.node_size - sizeof(tree_header);
1134 LIST_ENTRY* le;
1135
1136 le = Vcb->trees.Flink;
1137 while (le != &Vcb->trees) {
1138 tree* t = CONTAINING_RECORD(le, tree, list_entry);
1139
1140 if (t->write) {
1141 if (t->header.num_items == 0 && t->parent) {
1142 #ifdef DEBUG_WRITE_LOOPS
1143 ERR("empty tree found, looping again\n");
1144 #endif
1145 return FALSE;
1146 }
1147
1148 if (t->size > maxsize) {
1149 #ifdef DEBUG_WRITE_LOOPS
1150 ERR("overlarge tree found (%u > %u), looping again\n", t->size, maxsize);
1151 #endif
1152 return FALSE;
1153 }
1154
1155 if (!t->has_new_address) {
1156 #ifdef DEBUG_WRITE_LOOPS
1157 ERR("tree found without new address, looping again\n");
1158 #endif
1159 return FALSE;
1160 }
1161 }
1162
1163 le = le->Flink;
1164 }
1165
1166 return TRUE;
1167 }
1168
1169 static NTSTATUS add_parents(device_extension* Vcb, LIST_ENTRY* rollback) {
1170 UINT8 level;
1171 LIST_ENTRY* le;
1172
1173 for (level = 0; level <= 255; level++) {
1174 BOOL nothing_found = TRUE;
1175
1176 TRACE("level = %u\n", level);
1177
1178 le = Vcb->trees.Flink;
1179 while (le != &Vcb->trees) {
1180 tree* t = CONTAINING_RECORD(le, tree, list_entry);
1181
1182 if (t->write && t->header.level == level) {
1183 TRACE("tree %p: root = %llx, level = %x, parent = %p\n", t, t->header.tree_id, t->header.level, t->parent);
1184
1185 nothing_found = FALSE;
1186
1187 if (t->parent) {
1188 if (!t->parent->write)
1189 TRACE("adding tree %p (level %x)\n", t->parent, t->header.level);
1190
1191 t->parent->write = TRUE;
1192 }
1193 }
1194
1195 le = le->Flink;
1196 }
1197
1198 if (nothing_found)
1199 break;
1200 }
1201
1202 return STATUS_SUCCESS;
1203 }
1204
1205 static void add_parents_to_cache(device_extension* Vcb, tree* t) {
1206 while (t->parent) {
1207 t = t->parent;
1208 t->write = TRUE;
1209 }
1210 }
1211
1212 static BOOL insert_tree_extent_skinny(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64 address, PIRP Irp, LIST_ENTRY* rollback) {
1213 EXTENT_ITEM_SKINNY_METADATA* eism;
1214 traverse_ptr insert_tp;
1215
1216 eism = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_SKINNY_METADATA), ALLOC_TAG);
1217 if (!eism) {
1218 ERR("out of memory\n");
1219 return FALSE;
1220 }
1221
1222 eism->ei.refcount = 1;
1223 eism->ei.generation = Vcb->superblock.generation;
1224 eism->ei.flags = EXTENT_ITEM_TREE_BLOCK;
1225 eism->type = TYPE_TREE_BLOCK_REF;
1226 eism->tbr.offset = root_id;
1227
1228 if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, Irp, rollback)) {
1229 ERR("insert_tree_item failed\n");
1230 ExFreePool(eism);
1231 return FALSE;
1232 }
1233
1234 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
1235
1236 space_list_subtract(Vcb, c, FALSE, address, Vcb->superblock.node_size, rollback);
1237
1238 ExReleaseResourceLite(&c->lock);
1239
1240 add_parents_to_cache(Vcb, insert_tp.tree);
1241
1242 return TRUE;
1243 }
1244
1245 static BOOL insert_tree_extent(device_extension* Vcb, UINT8 level, UINT64 root_id, chunk* c, UINT64* new_address, PIRP Irp, LIST_ENTRY* rollback) {
1246 UINT64 address;
1247 EXTENT_ITEM_TREE2* eit2;
1248 traverse_ptr insert_tp;
1249
1250 TRACE("(%p, %x, %llx, %p, %p, %p, %p)\n", Vcb, level, root_id, c, new_address, rollback);
1251
1252 if (!find_address_in_chunk(Vcb, c, Vcb->superblock.node_size, &address))
1253 return FALSE;
1254
1255 if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
1256 BOOL b = insert_tree_extent_skinny(Vcb, level, root_id, c, address, Irp, rollback);
1257
1258 if (b)
1259 *new_address = address;
1260
1261 return b;
1262 }
1263
1264 eit2 = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_TREE2), ALLOC_TAG);
1265 if (!eit2) {
1266 ERR("out of memory\n");
1267 return FALSE;
1268 }
1269
1270 eit2->eit.extent_item.refcount = 1;
1271 eit2->eit.extent_item.generation = Vcb->superblock.generation;
1272 eit2->eit.extent_item.flags = EXTENT_ITEM_TREE_BLOCK;
1273 // eit2->eit.firstitem = wt->firstitem;
1274 eit2->eit.level = level;
1275 eit2->type = TYPE_TREE_BLOCK_REF;
1276 eit2->tbr.offset = root_id;
1277
1278 // #ifdef DEBUG_PARANOID
1279 // if (wt->firstitem.obj_type == 0xcc) { // TESTING
1280 // ERR("error - firstitem not set (wt = %p, tree = %p, address = %x)\n", wt, wt->tree, (UINT32)address);
1281 // ERR("num_items = %u, level = %u, root = %x, delete = %u\n", wt->tree->header.num_items, wt->tree->header.level, (UINT32)wt->tree->root->id, wt->delete);
1282 // int3;
1283 // }
1284 // #endif
1285
1286 if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp, Irp, rollback)) {
1287 ERR("insert_tree_item failed\n");
1288 ExFreePool(eit2);
1289 return FALSE;
1290 }
1291
1292 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
1293
1294 space_list_subtract(Vcb, c, FALSE, address, Vcb->superblock.node_size, rollback);
1295
1296 ExReleaseResourceLite(&c->lock);
1297
1298 add_parents_to_cache(Vcb, insert_tp.tree);
1299
1300 *new_address = address;
1301
1302 return TRUE;
1303 }
1304
1305 NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback) {
1306 chunk *origchunk = NULL, *c;
1307 LIST_ENTRY* le;
1308 UINT64 flags = t->flags, addr;
1309
1310 if (flags == 0) {
1311 if (t->root->id == BTRFS_ROOT_CHUNK)
1312 flags = BLOCK_FLAG_SYSTEM | BLOCK_FLAG_DUPLICATE;
1313 else if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS)
1314 flags = BLOCK_FLAG_DATA | BLOCK_FLAG_METADATA;
1315 else
1316 flags = BLOCK_FLAG_METADATA | BLOCK_FLAG_DUPLICATE;
1317 }
1318
1319 // TRACE("flags = %x\n", (UINT32)wt->flags);
1320
1321 // if (!chunk_test) { // TESTING
1322 // if ((c = alloc_chunk(Vcb, flags))) {
1323 // if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
1324 // if (insert_tree_extent(Vcb, t, c)) {
1325 // chunk_test = TRUE;
1326 // return STATUS_SUCCESS;
1327 // }
1328 // }
1329 // }
1330 // }
1331
1332 if (t->has_address) {
1333 origchunk = get_chunk_from_address(Vcb, t->header.address);
1334
1335 if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, origchunk, &addr, Irp, rollback)) {
1336 t->new_address = addr;
1337 t->has_new_address = TRUE;
1338 return STATUS_SUCCESS;
1339 }
1340 }
1341
1342 ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, TRUE);
1343
1344 le = Vcb->chunks.Flink;
1345 while (le != &Vcb->chunks) {
1346 c = CONTAINING_RECORD(le, chunk, list_entry);
1347
1348 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
1349
1350 if (c != origchunk && c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
1351 if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, Irp, rollback)) {
1352 ExReleaseResourceLite(&c->lock);
1353 ExReleaseResourceLite(&Vcb->chunk_lock);
1354 t->new_address = addr;
1355 t->has_new_address = TRUE;
1356 return STATUS_SUCCESS;
1357 }
1358 }
1359
1360 ExReleaseResourceLite(&c->lock);
1361
1362 le = le->Flink;
1363 }
1364
1365 // allocate new chunk if necessary
1366 if ((c = alloc_chunk(Vcb, flags))) {
1367 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
1368
1369 if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
1370 if (insert_tree_extent(Vcb, t->header.level, t->header.tree_id, c, &addr, Irp, rollback)) {
1371 ExReleaseResourceLite(&c->lock);
1372 ExReleaseResourceLite(&Vcb->chunk_lock);
1373 t->new_address = addr;
1374 t->has_new_address = TRUE;
1375 return STATUS_SUCCESS;
1376 }
1377 }
1378
1379 ExReleaseResourceLite(&c->lock);
1380 }
1381
1382 ExReleaseResourceLite(&Vcb->chunk_lock);
1383
1384 ERR("couldn't find any metadata chunks with %x bytes free\n", Vcb->superblock.node_size);
1385
1386 return STATUS_DISK_FULL;
1387 }
1388
1389 static BOOL reduce_tree_extent_skinny(device_extension* Vcb, UINT64 address, tree* t, PIRP Irp, LIST_ENTRY* rollback) {
1390 KEY searchkey;
1391 traverse_ptr tp;
1392 chunk* c;
1393 NTSTATUS Status;
1394
1395 searchkey.obj_id = address;
1396 searchkey.obj_type = TYPE_METADATA_ITEM;
1397 searchkey.offset = 0xffffffffffffffff;
1398
1399 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
1400 if (!NT_SUCCESS(Status)) {
1401 ERR("error - find_item returned %08x\n", Status);
1402 return FALSE;
1403 }
1404
1405 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
1406 TRACE("could not find %llx,%x,%llx in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
1407 return FALSE;
1408 }
1409
1410 if (tp.item->size < sizeof(EXTENT_ITEM_SKINNY_METADATA)) {
1411 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_SKINNY_METADATA));
1412 return FALSE;
1413 }
1414
1415 delete_tree_item(Vcb, &tp, rollback);
1416
1417 c = get_chunk_from_address(Vcb, address);
1418
1419 if (c) {
1420 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
1421
1422 decrease_chunk_usage(c, Vcb->superblock.node_size);
1423
1424 space_list_add(Vcb, c, TRUE, address, Vcb->superblock.node_size, rollback);
1425
1426 ExReleaseResourceLite(&c->lock);
1427 } else
1428 ERR("could not find chunk for address %llx\n", address);
1429
1430 return TRUE;
1431 }
1432
1433 // TESTING
1434 // static void check_tree_num_items(tree* t) {
1435 // LIST_ENTRY* le2;
1436 // UINT32 ni;
1437 //
1438 // le2 = t->itemlist.Flink;
1439 // ni = 0;
1440 // while (le2 != &t->itemlist) {
1441 // tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
1442 // if (!td->ignore)
1443 // ni++;
1444 // le2 = le2->Flink;
1445 // }
1446 //
1447 // if (t->header.num_items != ni) {
1448 // ERR("tree %p not okay: num_items was %x, expecting %x\n", t, ni, t->header.num_items);
1449 // int3;
1450 // } else {
1451 // ERR("tree %p okay\n", t);
1452 // }
1453 // }
1454 //
1455 // static void check_trees_num_items(LIST_ENTRY* tc) {
1456 // LIST_ENTRY* le = tc->Flink;
1457 // while (le != tc) {
1458 // tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
1459 //
1460 // check_tree_num_items(tc2->tree);
1461 //
1462 // le = le->Flink;
1463 // }
1464 // }
1465
1466 static void convert_old_tree_extent(device_extension* Vcb, tree_data* td, tree* t, PIRP Irp, LIST_ENTRY* rollback) {
1467 KEY searchkey;
1468 traverse_ptr tp, tp2, insert_tp;
1469 EXTENT_REF_V0* erv0;
1470 NTSTATUS Status;
1471
1472 TRACE("(%p, %p, %p)\n", Vcb, td, t);
1473
1474 searchkey.obj_id = td->treeholder.address;
1475 searchkey.obj_type = TYPE_EXTENT_REF_V0;
1476 searchkey.offset = 0xffffffffffffffff;
1477
1478 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
1479 if (!NT_SUCCESS(Status)) {
1480 ERR("error - find_item returned %08x\n", Status);
1481 return;
1482 }
1483
1484 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
1485 TRACE("could not find EXTENT_REF_V0 for %llx\n", searchkey.obj_id);
1486 return;
1487 }
1488
1489 searchkey.obj_id = td->treeholder.address;
1490 searchkey.obj_type = TYPE_EXTENT_ITEM;
1491 searchkey.offset = Vcb->superblock.node_size;
1492
1493 Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp);
1494 if (!NT_SUCCESS(Status)) {
1495 ERR("error - find_item returned %08x\n", Status);
1496 return;
1497 }
1498
1499 if (keycmp(&searchkey, &tp2.item->key)) {
1500 ERR("could not find %llx,%x,%llx\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
1501 return;
1502 }
1503
1504 if (tp.item->size < sizeof(EXTENT_REF_V0)) {
1505 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_REF_V0));
1506 return;
1507 }
1508
1509 erv0 = (EXTENT_REF_V0*)tp.item->data;
1510
1511 delete_tree_item(Vcb, &tp, rollback);
1512 delete_tree_item(Vcb, &tp2, rollback);
1513
1514 if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
1515 EXTENT_ITEM_SKINNY_METADATA* eism = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_SKINNY_METADATA), ALLOC_TAG);
1516
1517 if (!eism) {
1518 ERR("out of memory\n");
1519 return;
1520 }
1521
1522 eism->ei.refcount = 1;
1523 eism->ei.generation = erv0->gen;
1524 eism->ei.flags = EXTENT_ITEM_TREE_BLOCK;
1525 eism->type = TYPE_TREE_BLOCK_REF;
1526 eism->tbr.offset = t->header.tree_id;
1527
1528 if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_METADATA_ITEM, t->header.level -1, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, Irp, rollback)) {
1529 ERR("insert_tree_item failed\n");
1530 return;
1531 }
1532 } else {
1533 EXTENT_ITEM_TREE2* eit2 = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_TREE2), ALLOC_TAG);
1534
1535 if (!eit2) {
1536 ERR("out of memory\n");
1537 return;
1538 }
1539
1540 eit2->eit.extent_item.refcount = 1;
1541 eit2->eit.extent_item.generation = erv0->gen;
1542 eit2->eit.extent_item.flags = EXTENT_ITEM_TREE_BLOCK;
1543 eit2->eit.firstitem = td->key;
1544 eit2->eit.level = t->header.level - 1;
1545 eit2->type = TYPE_TREE_BLOCK_REF;
1546 eit2->tbr.offset = t->header.tree_id;
1547
1548 if (!insert_tree_item(Vcb, Vcb->extent_root, td->treeholder.address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp, Irp, rollback)) {
1549 ERR("insert_tree_item failed\n");
1550 return;
1551 }
1552 }
1553
1554 add_parents_to_cache(Vcb, insert_tp.tree);
1555 add_parents_to_cache(Vcb, tp.tree);
1556 add_parents_to_cache(Vcb, tp2.tree);
1557 }
1558
1559 static NTSTATUS reduce_tree_extent(device_extension* Vcb, UINT64 address, tree* t, PIRP Irp, LIST_ENTRY* rollback) {
1560 KEY searchkey;
1561 traverse_ptr tp;
1562 EXTENT_ITEM* ei;
1563 EXTENT_ITEM_V0* eiv0;
1564 chunk* c;
1565 NTSTATUS Status;
1566
1567 // FIXME - deal with refcounts > 1
1568
1569 TRACE("(%p, %llx, %p)\n", Vcb, address, t);
1570
1571 if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
1572 if (reduce_tree_extent_skinny(Vcb, address, t, Irp, rollback)) {
1573 return STATUS_SUCCESS;
1574 }
1575 }
1576
1577 searchkey.obj_id = address;
1578 searchkey.obj_type = TYPE_EXTENT_ITEM;
1579 searchkey.offset = Vcb->superblock.node_size;
1580
1581 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
1582 if (!NT_SUCCESS(Status)) {
1583 ERR("error - find_item returned %08x\n", Status);
1584 return Status;
1585 }
1586
1587 if (keycmp(&tp.item->key, &searchkey)) {
1588 ERR("could not find %llx,%x,%llx in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
1589 int3;
1590 return STATUS_INTERNAL_ERROR;
1591 }
1592
1593 if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
1594 eiv0 = (EXTENT_ITEM_V0*)tp.item->data;
1595
1596 if (eiv0->refcount > 1) {
1597 FIXME("FIXME - cannot deal with refcounts larger than 1 at present (eiv0->refcount == %llx)\n", eiv0->refcount);
1598 return STATUS_INTERNAL_ERROR;
1599 }
1600 } else {
1601 if (tp.item->size < sizeof(EXTENT_ITEM)) {
1602 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));
1603 return STATUS_INTERNAL_ERROR;
1604 }
1605
1606 ei = (EXTENT_ITEM*)tp.item->data;
1607
1608 if (ei->refcount > 1) {
1609 FIXME("FIXME - cannot deal with refcounts larger than 1 at present (ei->refcount == %llx)\n", ei->refcount);
1610 return STATUS_INTERNAL_ERROR;
1611 }
1612 }
1613
1614 delete_tree_item(Vcb, &tp, rollback);
1615
1616 // if EXTENT_ITEM_V0, delete corresponding B4 item
1617 if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
1618 traverse_ptr tp2;
1619
1620 searchkey.obj_id = address;
1621 searchkey.obj_type = TYPE_EXTENT_REF_V0;
1622 searchkey.offset = 0xffffffffffffffff;
1623
1624 Status = find_item(Vcb, Vcb->extent_root, &tp2, &searchkey, FALSE, Irp);
1625 if (!NT_SUCCESS(Status)) {
1626 ERR("error - find_item returned %08x\n", Status);
1627 return Status;
1628 }
1629
1630 if (tp2.item->key.obj_id == searchkey.obj_id && tp2.item->key.obj_type == searchkey.obj_type) {
1631 delete_tree_item(Vcb, &tp2, rollback);
1632 }
1633 }
1634
1635 if (t && !(t->header.flags & HEADER_FLAG_MIXED_BACKREF)) {
1636 LIST_ENTRY* le;
1637
1638 // when writing old internal trees, convert related extents
1639
1640 le = t->itemlist.Flink;
1641 while (le != &t->itemlist) {
1642 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
1643
1644 // ERR("%llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
1645
1646 if (!td->ignore && !td->inserted) {
1647 if (t->header.level > 0) {
1648 convert_old_tree_extent(Vcb, td, t, Irp, rollback);
1649 } else if (td->key.obj_type == TYPE_EXTENT_DATA && td->size >= sizeof(EXTENT_DATA)) {
1650 EXTENT_DATA* ed = (EXTENT_DATA*)td->data;
1651
1652 if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && td->size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
1653 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
1654
1655 if (ed2->address != 0) {
1656 TRACE("trying to convert old data extent %llx,%llx\n", ed2->address, ed2->size);
1657 convert_old_data_extent(Vcb, ed2->address, ed2->size, Irp, rollback);
1658 }
1659 }
1660 }
1661 }
1662
1663 le = le->Flink;
1664 }
1665 }
1666
1667 c = get_chunk_from_address(Vcb, address);
1668
1669 if (c) {
1670 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
1671
1672 decrease_chunk_usage(c, tp.item->key.offset);
1673
1674 space_list_add(Vcb, c, TRUE, address, tp.item->key.offset, rollback);
1675
1676 ExReleaseResourceLite(&c->lock);
1677 } else
1678 ERR("could not find chunk for address %llx\n", address);
1679
1680 return STATUS_SUCCESS;
1681 }
1682
1683 static NTSTATUS allocate_tree_extents(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
1684 LIST_ENTRY* le;
1685 NTSTATUS Status;
1686
1687 TRACE("(%p)\n", Vcb);
1688
1689 le = Vcb->trees.Flink;
1690 while (le != &Vcb->trees) {
1691 tree* t = CONTAINING_RECORD(le, tree, list_entry);
1692
1693 if (t->write && !t->has_new_address) {
1694 chunk* c;
1695
1696 Status = get_tree_new_address(Vcb, t, Irp, rollback);
1697 if (!NT_SUCCESS(Status)) {
1698 ERR("get_tree_new_address returned %08x\n", Status);
1699 return Status;
1700 }
1701
1702 TRACE("allocated extent %llx\n", t->new_address);
1703
1704 if (t->has_address) {
1705 Status = reduce_tree_extent(Vcb, t->header.address, t, Irp, rollback);
1706
1707 if (!NT_SUCCESS(Status)) {
1708 ERR("reduce_tree_extent returned %08x\n", Status);
1709 return Status;
1710 }
1711 }
1712
1713 c = get_chunk_from_address(Vcb, t->new_address);
1714
1715 if (c) {
1716 increase_chunk_usage(c, Vcb->superblock.node_size);
1717 } else {
1718 ERR("could not find chunk for address %llx\n", t->new_address);
1719 return STATUS_INTERNAL_ERROR;
1720 }
1721 }
1722
1723 le = le->Flink;
1724 }
1725
1726 return STATUS_SUCCESS;
1727 }
1728
1729 static NTSTATUS update_root_root(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
1730 LIST_ENTRY* le;
1731 NTSTATUS Status;
1732
1733 TRACE("(%p)\n", Vcb);
1734
1735 le = Vcb->trees.Flink;
1736 while (le != &Vcb->trees) {
1737 tree* t = CONTAINING_RECORD(le, tree, list_entry);
1738
1739 if (t->write && !t->parent) {
1740 if (t->root != Vcb->root_root && t->root != Vcb->chunk_root) {
1741 KEY searchkey;
1742 traverse_ptr tp;
1743
1744 searchkey.obj_id = t->root->id;
1745 searchkey.obj_type = TYPE_ROOT_ITEM;
1746 searchkey.offset = 0xffffffffffffffff;
1747
1748 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
1749 if (!NT_SUCCESS(Status)) {
1750 ERR("error - find_item returned %08x\n", Status);
1751 return Status;
1752 }
1753
1754 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
1755 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
1756 int3;
1757 return STATUS_INTERNAL_ERROR;
1758 }
1759
1760 TRACE("updating the address for root %llx to %llx\n", searchkey.obj_id, t->new_address);
1761
1762 t->root->root_item.block_number = t->new_address;
1763 t->root->root_item.root_level = t->header.level;
1764 t->root->root_item.generation = Vcb->superblock.generation;
1765 t->root->root_item.generation2 = Vcb->superblock.generation;
1766
1767 if (tp.item->size < sizeof(ROOT_ITEM)) { // if not full length, delete and create new entry
1768 ROOT_ITEM* ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
1769
1770 if (!ri) {
1771 ERR("out of memory\n");
1772 return STATUS_INSUFFICIENT_RESOURCES;
1773 }
1774
1775 RtlCopyMemory(ri, &t->root->root_item, sizeof(ROOT_ITEM));
1776
1777 delete_tree_item(Vcb, &tp, rollback);
1778
1779 if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, 0, ri, sizeof(ROOT_ITEM), NULL, Irp, rollback)) {
1780 ERR("insert_tree_item failed\n");
1781 return STATUS_INTERNAL_ERROR;
1782 }
1783 } else
1784 RtlCopyMemory(tp.item->data, &t->root->root_item, sizeof(ROOT_ITEM));
1785 }
1786
1787 t->root->treeholder.address = t->new_address;
1788 }
1789
1790 le = le->Flink;
1791 }
1792
1793 Status = update_chunk_caches(Vcb, Irp, rollback);
1794 if (!NT_SUCCESS(Status)) {
1795 ERR("update_chunk_caches returned %08x\n", Status);
1796 return Status;
1797 }
1798
1799 return STATUS_SUCCESS;
1800 }
1801
1802 static NTSTATUS STDCALL write_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
1803 write_data_stripe* stripe = conptr;
1804 write_data_context* context = (write_data_context*)stripe->context;
1805 LIST_ENTRY* le;
1806
1807 // FIXME - we need a lock here
1808
1809 if (stripe->status == WriteDataStatus_Cancelling) {
1810 stripe->status = WriteDataStatus_Cancelled;
1811 goto end;
1812 }
1813
1814 stripe->iosb = Irp->IoStatus;
1815
1816 if (NT_SUCCESS(Irp->IoStatus.Status)) {
1817 stripe->status = WriteDataStatus_Success;
1818 } else {
1819 le = context->stripes.Flink;
1820
1821 stripe->status = WriteDataStatus_Error;
1822
1823 while (le != &context->stripes) {
1824 write_data_stripe* s2 = CONTAINING_RECORD(le, write_data_stripe, list_entry);
1825
1826 if (s2->status == WriteDataStatus_Pending) {
1827 s2->status = WriteDataStatus_Cancelling;
1828 IoCancelIrp(s2->Irp);
1829 }
1830
1831 le = le->Flink;
1832 }
1833 }
1834
1835 end:
1836 if (InterlockedDecrement(&context->stripes_left) == 0)
1837 KeSetEvent(&context->Event, 0, FALSE);
1838
1839 return STATUS_MORE_PROCESSING_REQUIRED;
1840 }
1841
1842 void free_write_data_stripes(write_data_context* wtc) {
1843 LIST_ENTRY *le, *le2, *nextle;
1844
1845 le = wtc->stripes.Flink;
1846 while (le != &wtc->stripes) {
1847 write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
1848
1849 if (stripe->Irp) {
1850 if (stripe->device->devobj->Flags & DO_DIRECT_IO) {
1851 MmUnlockPages(stripe->Irp->MdlAddress);
1852 IoFreeMdl(stripe->Irp->MdlAddress);
1853 }
1854 }
1855
1856 le = le->Flink;
1857 }
1858
1859 le = wtc->stripes.Flink;
1860 while (le != &wtc->stripes) {
1861 write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
1862
1863 nextle = le->Flink;
1864
1865 if (stripe->buf && stripe->need_free) {
1866 ExFreePool(stripe->buf);
1867
1868 le2 = le->Flink;
1869 while (le2 != &wtc->stripes) {
1870 write_data_stripe* s2 = CONTAINING_RECORD(le2, write_data_stripe, list_entry);
1871
1872 if (s2->buf == stripe->buf)
1873 s2->buf = NULL;
1874
1875 le2 = le2->Flink;
1876 }
1877
1878 }
1879
1880 ExFreePool(stripe);
1881
1882 le = nextle;
1883 }
1884 }
1885
1886 static NTSTATUS write_trees(device_extension* Vcb, PIRP Irp) {
1887 UINT8 level;
1888 UINT8 *data, *body;
1889 UINT32 crc32;
1890 NTSTATUS Status;
1891 LIST_ENTRY* le;
1892 write_data_context* wtc;
1893
1894 TRACE("(%p)\n", Vcb);
1895
1896 for (level = 0; level <= 255; level++) {
1897 BOOL nothing_found = TRUE;
1898
1899 TRACE("level = %u\n", level);
1900
1901 le = Vcb->trees.Flink;
1902 while (le != &Vcb->trees) {
1903 tree* t = CONTAINING_RECORD(le, tree, list_entry);
1904
1905 if (t->write && t->header.level == level) {
1906 KEY firstitem, searchkey;
1907 LIST_ENTRY* le2;
1908 traverse_ptr tp;
1909 EXTENT_ITEM_TREE* eit;
1910
1911 if (!t->has_new_address) {
1912 ERR("error - tried to write tree with no new address\n");
1913 int3;
1914 }
1915
1916 le2 = t->itemlist.Flink;
1917 while (le2 != &t->itemlist) {
1918 tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
1919 if (!td->ignore) {
1920 firstitem = td->key;
1921 break;
1922 }
1923 le2 = le2->Flink;
1924 }
1925
1926 if (t->parent) {
1927 t->paritem->key = firstitem;
1928 t->paritem->treeholder.address = t->new_address;
1929 t->paritem->treeholder.generation = Vcb->superblock.generation;
1930 }
1931
1932 if (!(Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)) {
1933 searchkey.obj_id = t->new_address;
1934 searchkey.obj_type = TYPE_EXTENT_ITEM;
1935 searchkey.offset = Vcb->superblock.node_size;
1936
1937 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
1938 if (!NT_SUCCESS(Status)) {
1939 ERR("error - find_item returned %08x\n", Status);
1940 return Status;
1941 }
1942
1943 if (keycmp(&searchkey, &tp.item->key)) {
1944 // traverse_ptr next_tp;
1945 // BOOL b;
1946 // tree_data* paritem;
1947
1948 ERR("could not find %llx,%x,%llx in extent_root (found %llx,%x,%llx instead)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1949
1950 // searchkey.obj_id = 0;
1951 // searchkey.obj_type = 0;
1952 // searchkey.offset = 0;
1953 //
1954 // find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
1955 //
1956 // paritem = NULL;
1957 // do {
1958 // if (tp.tree->paritem != paritem) {
1959 // paritem = tp.tree->paritem;
1960 // ERR("paritem: %llx,%x,%llx\n", paritem->key.obj_id, paritem->key.obj_type, paritem->key.offset);
1961 // }
1962 //
1963 // ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1964 //
1965 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
1966 // if (b) {
1967 // free_traverse_ptr(&tp);
1968 // tp = next_tp;
1969 // }
1970 // } while (b);
1971 //
1972 // free_traverse_ptr(&tp);
1973
1974 return STATUS_INTERNAL_ERROR;
1975 }
1976
1977 if (tp.item->size < sizeof(EXTENT_ITEM_TREE)) {
1978 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_TREE));
1979 return STATUS_INTERNAL_ERROR;
1980 }
1981
1982 eit = (EXTENT_ITEM_TREE*)tp.item->data;
1983 eit->firstitem = firstitem;
1984 }
1985
1986 nothing_found = FALSE;
1987 }
1988
1989 le = le->Flink;
1990 }
1991
1992 if (nothing_found)
1993 break;
1994 }
1995
1996 TRACE("allocated tree extents\n");
1997
1998 wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_context), ALLOC_TAG);
1999 if (!wtc) {
2000 ERR("out of memory\n");
2001 return STATUS_INSUFFICIENT_RESOURCES;
2002 }
2003
2004 KeInitializeEvent(&wtc->Event, NotificationEvent, FALSE);
2005 InitializeListHead(&wtc->stripes);
2006 wtc->tree = TRUE;
2007 wtc->stripes_left = 0;
2008
2009 le = Vcb->trees.Flink;
2010 while (le != &Vcb->trees) {
2011 tree* t = CONTAINING_RECORD(le, tree, list_entry);
2012 #ifdef DEBUG_PARANOID
2013 UINT32 num_items = 0, size = 0;
2014 LIST_ENTRY* le2;
2015 BOOL crash = FALSE;
2016 #endif
2017
2018 if (t->write) {
2019 #ifdef DEBUG_PARANOID
2020 le2 = t->itemlist.Flink;
2021 while (le2 != &t->itemlist) {
2022 tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
2023 if (!td->ignore) {
2024 num_items++;
2025
2026 if (t->header.level == 0)
2027 size += td->size;
2028 }
2029 le2 = le2->Flink;
2030 }
2031
2032 if (t->header.level == 0)
2033 size += num_items * sizeof(leaf_node);
2034 else
2035 size += num_items * sizeof(internal_node);
2036
2037 if (num_items != t->header.num_items) {
2038 ERR("tree %llx, level %x: num_items was %x, expected %x\n", t->root->id, t->header.level, num_items, t->header.num_items);
2039 crash = TRUE;
2040 }
2041
2042 if (size != t->size) {
2043 ERR("tree %llx, level %x: size was %x, expected %x\n", t->root->id, t->header.level, size, t->size);
2044 crash = TRUE;
2045 }
2046
2047 if (t->header.num_items == 0 && t->parent) {
2048 ERR("tree %llx, level %x: tried to write empty tree with parent\n", t->root->id, t->header.level);
2049 crash = TRUE;
2050 }
2051
2052 if (t->size > Vcb->superblock.node_size - sizeof(tree_header)) {
2053 ERR("tree %llx, level %x: tried to write overlarge tree (%x > %x)\n", t->root->id, t->header.level, t->size, Vcb->superblock.node_size - sizeof(tree_header));
2054 crash = TRUE;
2055 }
2056
2057 if (crash) {
2058 ERR("tree %p\n", t);
2059 le2 = t->itemlist.Flink;
2060 while (le2 != &t->itemlist) {
2061 tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
2062 if (!td->ignore) {
2063 ERR("%llx,%x,%llx inserted=%u\n", td->key.obj_id, td->key.obj_type, td->key.offset, td->inserted);
2064 }
2065 le2 = le2->Flink;
2066 }
2067 int3;
2068 }
2069 #endif
2070 t->header.address = t->new_address;
2071 t->header.generation = Vcb->superblock.generation;
2072 t->header.flags |= HEADER_FLAG_MIXED_BACKREF;
2073 t->has_address = TRUE;
2074
2075 data = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG);
2076 if (!data) {
2077 ERR("out of memory\n");
2078 Status = STATUS_INSUFFICIENT_RESOURCES;
2079 goto end;
2080 }
2081
2082 body = data + sizeof(tree_header);
2083
2084 RtlCopyMemory(data, &t->header, sizeof(tree_header));
2085 RtlZeroMemory(body, Vcb->superblock.node_size - sizeof(tree_header));
2086
2087 if (t->header.level == 0) {
2088 leaf_node* itemptr = (leaf_node*)body;
2089 int i = 0;
2090 LIST_ENTRY* le2;
2091 UINT8* dataptr = data + Vcb->superblock.node_size;
2092
2093 le2 = t->itemlist.Flink;
2094 while (le2 != &t->itemlist) {
2095 tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
2096 if (!td->ignore) {
2097 dataptr = dataptr - td->size;
2098
2099 itemptr[i].key = td->key;
2100 itemptr[i].offset = (UINT8*)dataptr - (UINT8*)body;
2101 itemptr[i].size = td->size;
2102 i++;
2103
2104 if (td->size > 0)
2105 RtlCopyMemory(dataptr, td->data, td->size);
2106 }
2107
2108 le2 = le2->Flink;
2109 }
2110 } else {
2111 internal_node* itemptr = (internal_node*)body;
2112 int i = 0;
2113 LIST_ENTRY* le2;
2114
2115 le2 = t->itemlist.Flink;
2116 while (le2 != &t->itemlist) {
2117 tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
2118 if (!td->ignore) {
2119 itemptr[i].key = td->key;
2120 itemptr[i].address = td->treeholder.address;
2121 itemptr[i].generation = td->treeholder.generation;
2122 i++;
2123 }
2124
2125 le2 = le2->Flink;
2126 }
2127 }
2128
2129 crc32 = calc_crc32c(0xffffffff, (UINT8*)&((tree_header*)data)->fs_uuid, Vcb->superblock.node_size - sizeof(((tree_header*)data)->csum));
2130 crc32 = ~crc32;
2131 *((UINT32*)data) = crc32;
2132 TRACE("setting crc32 to %08x\n", crc32);
2133
2134 Status = write_data(Vcb, t->new_address, data, TRUE, Vcb->superblock.node_size, wtc, NULL, NULL);
2135 if (!NT_SUCCESS(Status)) {
2136 ERR("write_data returned %08x\n", Status);
2137 goto end;
2138 }
2139 }
2140
2141 le = le->Flink;
2142 }
2143
2144 Status = STATUS_SUCCESS;
2145
2146 if (wtc->stripes.Flink != &wtc->stripes) {
2147 // launch writes and wait
2148 le = wtc->stripes.Flink;
2149 while (le != &wtc->stripes) {
2150 write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
2151
2152 if (stripe->status != WriteDataStatus_Ignore)
2153 IoCallDriver(stripe->device->devobj, stripe->Irp);
2154
2155 le = le->Flink;
2156 }
2157
2158 KeWaitForSingleObject(&wtc->Event, Executive, KernelMode, FALSE, NULL);
2159
2160 le = wtc->stripes.Flink;
2161 while (le != &wtc->stripes) {
2162 write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
2163
2164 if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) {
2165 Status = stripe->iosb.Status;
2166 break;
2167 }
2168
2169 le = le->Flink;
2170 }
2171
2172 free_write_data_stripes(wtc);
2173 }
2174
2175 end:
2176 ExFreePool(wtc);
2177
2178 return Status;
2179 }
2180
2181 static void update_backup_superblock(device_extension* Vcb, superblock_backup* sb, PIRP Irp) {
2182 KEY searchkey;
2183 traverse_ptr tp;
2184
2185 RtlZeroMemory(sb, sizeof(superblock_backup));
2186
2187 sb->root_tree_addr = Vcb->superblock.root_tree_addr;
2188 sb->root_tree_generation = Vcb->superblock.generation;
2189 sb->root_level = Vcb->superblock.root_level;
2190
2191 sb->chunk_tree_addr = Vcb->superblock.chunk_tree_addr;
2192 sb->chunk_tree_generation = Vcb->superblock.chunk_root_generation;
2193 sb->chunk_root_level = Vcb->superblock.chunk_root_level;
2194
2195 searchkey.obj_id = BTRFS_ROOT_EXTENT;
2196 searchkey.obj_type = TYPE_ROOT_ITEM;
2197 searchkey.offset = 0xffffffffffffffff;
2198
2199 if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) {
2200 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
2201 ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
2202
2203 sb->extent_tree_addr = ri->block_number;
2204 sb->extent_tree_generation = ri->generation;
2205 sb->extent_root_level = ri->root_level;
2206 }
2207 }
2208
2209 searchkey.obj_id = BTRFS_ROOT_FSTREE;
2210
2211 if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) {
2212 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
2213 ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
2214
2215 sb->fs_tree_addr = ri->block_number;
2216 sb->fs_tree_generation = ri->generation;
2217 sb->fs_root_level = ri->root_level;
2218 }
2219 }
2220
2221 searchkey.obj_id = BTRFS_ROOT_DEVTREE;
2222
2223 if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) {
2224 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
2225 ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
2226
2227 sb->dev_root_addr = ri->block_number;
2228 sb->dev_root_generation = ri->generation;
2229 sb->dev_root_level = ri->root_level;
2230 }
2231 }
2232
2233 searchkey.obj_id = BTRFS_ROOT_CHECKSUM;
2234
2235 if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp))) {
2236 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) {
2237 ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data;
2238
2239 sb->csum_root_addr = ri->block_number;
2240 sb->csum_root_generation = ri->generation;
2241 sb->csum_root_level = ri->root_level;
2242 }
2243 }
2244
2245 sb->total_bytes = Vcb->superblock.total_bytes;
2246 sb->bytes_used = Vcb->superblock.bytes_used;
2247 sb->num_devices = Vcb->superblock.num_devices;
2248 }
2249
2250 static NTSTATUS write_superblocks(device_extension* Vcb, PIRP Irp) {
2251 UINT64 i;
2252 NTSTATUS Status;
2253 LIST_ENTRY* le;
2254
2255 TRACE("(%p)\n", Vcb);
2256
2257 le = Vcb->trees.Flink;
2258 while (le != &Vcb->trees) {
2259 tree* t = CONTAINING_RECORD(le, tree, list_entry);
2260
2261 if (t->write && !t->parent) {
2262 if (t->root == Vcb->root_root) {
2263 Vcb->superblock.root_tree_addr = t->new_address;
2264 Vcb->superblock.root_level = t->header.level;
2265 } else if (t->root == Vcb->chunk_root) {
2266 Vcb->superblock.chunk_tree_addr = t->new_address;
2267 Vcb->superblock.chunk_root_generation = t->header.generation;
2268 Vcb->superblock.chunk_root_level = t->header.level;
2269 }
2270 }
2271
2272 le = le->Flink;
2273 }
2274
2275 for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS - 1; i++) {
2276 RtlCopyMemory(&Vcb->superblock.backup[i], &Vcb->superblock.backup[i+1], sizeof(superblock_backup));
2277 }
2278
2279 update_backup_superblock(Vcb, &Vcb->superblock.backup[BTRFS_NUM_BACKUP_ROOTS - 1], Irp);
2280
2281 for (i = 0; i < Vcb->superblock.num_devices; i++) {
2282 if (Vcb->devices[i].devobj) {
2283 Status = write_superblock(Vcb, &Vcb->devices[i]);
2284 if (!NT_SUCCESS(Status)) {
2285 ERR("write_superblock returned %08x\n", Status);
2286 return Status;
2287 }
2288 }
2289 }
2290
2291 return STATUS_SUCCESS;
2292 }
2293
2294 static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_extent* ce, PIRP Irp, LIST_ENTRY* rollback) {
2295 LIST_ENTRY *le, *le2;
2296 NTSTATUS Status;
2297 UINT64 old_size;
2298
2299 le = ce->refs.Flink;
2300 while (le != &ce->refs) {
2301 changed_extent_ref* cer = CONTAINING_RECORD(le, changed_extent_ref, list_entry);
2302 LIST_ENTRY* le3 = le->Flink;
2303 UINT64 old_count = 0;
2304
2305 le2 = ce->old_refs.Flink;
2306 while (le2 != &ce->old_refs) {
2307 changed_extent_ref* cer2 = CONTAINING_RECORD(le2, changed_extent_ref, list_entry);
2308
2309 if (cer2->edr.root == cer->edr.root && cer2->edr.objid == cer->edr.objid && cer2->edr.offset == cer->edr.offset) {
2310 old_count = cer2->edr.count;
2311
2312 RemoveEntryList(&cer2->list_entry);
2313 ExFreePool(cer2);
2314 break;
2315 }
2316
2317 le2 = le2->Flink;
2318 }
2319
2320 old_size = ce->old_count > 0 ? ce->old_size : ce->size;
2321
2322 if (cer->edr.count > old_count) {
2323 Status = increase_extent_refcount_data(Vcb, ce->address, old_size, cer->edr.root, cer->edr.objid, cer->edr.offset, cer->edr.count - old_count, Irp, rollback);
2324
2325 if (!NT_SUCCESS(Status)) {
2326 ERR("increase_extent_refcount_data returned %08x\n", Status);
2327 return Status;
2328 }
2329 } else if (cer->edr.count < old_count) {
2330 Status = decrease_extent_refcount_data(Vcb, ce->address, old_size, cer->edr.root, cer->edr.objid, cer->edr.offset,
2331 old_count - cer->edr.count, Irp, rollback);
2332
2333 if (!NT_SUCCESS(Status)) {
2334 ERR("decrease_extent_refcount_data returned %08x\n", Status);
2335 return Status;
2336 }
2337 }
2338
2339 if (ce->size != ce->old_size && ce->old_count > 0) {
2340 KEY searchkey;
2341 traverse_ptr tp;
2342 void* data;
2343
2344 searchkey.obj_id = ce->address;
2345 searchkey.obj_type = TYPE_EXTENT_ITEM;
2346 searchkey.offset = ce->old_size;
2347
2348 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
2349 if (!NT_SUCCESS(Status)) {
2350 ERR("error - find_item returned %08x\n", Status);
2351 return Status;
2352 }
2353
2354 if (keycmp(&searchkey, &tp.item->key)) {
2355 ERR("could not find (%llx,%x,%llx) in extent tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
2356 return STATUS_INTERNAL_ERROR;
2357 }
2358
2359 if (tp.item->size > 0) {
2360 data = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
2361
2362 if (!data) {
2363 ERR("out of memory\n");
2364 return STATUS_INSUFFICIENT_RESOURCES;
2365 }
2366
2367 RtlCopyMemory(data, tp.item->data, tp.item->size);
2368 } else
2369 data = NULL;
2370
2371 if (!insert_tree_item(Vcb, Vcb->extent_root, ce->address, TYPE_EXTENT_ITEM, ce->size, data, tp.item->size, NULL, Irp, rollback)) {
2372 ERR("insert_tree_item failed\n");
2373 return STATUS_INTERNAL_ERROR;
2374 }
2375
2376 delete_tree_item(Vcb, &tp, rollback);
2377 }
2378
2379 RemoveEntryList(&cer->list_entry);
2380 ExFreePool(cer);
2381
2382 le = le3;
2383 }
2384
2385 #ifdef DEBUG_PARANOID
2386 if (!IsListEmpty(&ce->old_refs))
2387 WARN("old_refs not empty\n");
2388 #endif
2389
2390 if (ce->count == 0) {
2391 if (!ce->no_csum) {
2392 LIST_ENTRY changed_sector_list;
2393
2394 changed_sector* sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
2395 if (!sc) {
2396 ERR("out of memory\n");
2397 return STATUS_INSUFFICIENT_RESOURCES;
2398 }
2399
2400 sc->ol.key = ce->address;
2401 sc->checksums = NULL;
2402 sc->length = ce->size / Vcb->superblock.sector_size;
2403
2404 sc->deleted = TRUE;
2405
2406 InitializeListHead(&changed_sector_list);
2407 insert_into_ordered_list(&changed_sector_list, &sc->ol);
2408
2409 ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE);
2410 commit_checksum_changes(Vcb, &changed_sector_list);
2411 ExReleaseResourceLite(&Vcb->checksum_lock);
2412 }
2413
2414 decrease_chunk_usage(c, ce->size);
2415
2416 space_list_add(Vcb, c, TRUE, ce->address, ce->size, rollback);
2417 }
2418
2419 RemoveEntryList(&ce->list_entry);
2420 ExFreePool(ce);
2421
2422 return STATUS_SUCCESS;
2423 }
2424
2425 static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
2426 LIST_ENTRY *le = Vcb->chunks.Flink, *le2;
2427 chunk* c;
2428 KEY searchkey;
2429 traverse_ptr tp;
2430 BLOCK_GROUP_ITEM* bgi;
2431 NTSTATUS Status;
2432 BOOL flushed_extents = FALSE;
2433
2434 TRACE("(%p)\n", Vcb);
2435
2436 ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE);
2437
2438 while (le != &Vcb->chunks) {
2439 c = CONTAINING_RECORD(le, chunk, list_entry);
2440
2441 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
2442
2443 le2 = c->changed_extents.Flink;
2444 while (le2 != &c->changed_extents) {
2445 LIST_ENTRY* le3 = le2->Flink;
2446 changed_extent* ce = CONTAINING_RECORD(le2, changed_extent, list_entry);
2447
2448 Status = flush_changed_extent(Vcb, c, ce, Irp, rollback);
2449 if (!NT_SUCCESS(Status)) {
2450 ERR("flush_changed_extent returned %08x\n", Status);
2451 ExReleaseResourceLite(&c->lock);
2452 goto end;
2453 }
2454
2455 flushed_extents = TRUE;
2456
2457 le2 = le3;
2458 }
2459
2460 if (c->used != c->oldused) {
2461 searchkey.obj_id = c->offset;
2462 searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
2463 searchkey.offset = c->chunk_item->size;
2464
2465 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
2466 if (!NT_SUCCESS(Status)) {
2467 ERR("error - find_item returned %08x\n", Status);
2468 ExReleaseResourceLite(&c->lock);
2469 goto end;
2470 }
2471
2472 if (keycmp(&searchkey, &tp.item->key)) {
2473 ERR("could not find (%llx,%x,%llx) in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
2474 int3;
2475 Status = STATUS_INTERNAL_ERROR;
2476 ExReleaseResourceLite(&c->lock);
2477 goto end;
2478 }
2479
2480 if (tp.item->size < sizeof(BLOCK_GROUP_ITEM)) {
2481 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(BLOCK_GROUP_ITEM));
2482 Status = STATUS_INTERNAL_ERROR;
2483 ExReleaseResourceLite(&c->lock);
2484 goto end;
2485 }
2486
2487 bgi = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
2488 if (!bgi) {
2489 ERR("out of memory\n");
2490 Status = STATUS_INSUFFICIENT_RESOURCES;
2491 ExReleaseResourceLite(&c->lock);
2492 goto end;
2493 }
2494
2495 RtlCopyMemory(bgi, tp.item->data, tp.item->size);
2496 bgi->used = c->used;
2497
2498 TRACE("adjusting usage of chunk %llx to %llx\n", c->offset, c->used);
2499
2500 delete_tree_item(Vcb, &tp, rollback);
2501
2502 if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, bgi, tp.item->size, NULL, Irp, rollback)) {
2503 ERR("insert_tree_item failed\n");
2504 ExFreePool(bgi);
2505 Status = STATUS_INTERNAL_ERROR;
2506 ExReleaseResourceLite(&c->lock);
2507 goto end;
2508 }
2509
2510 TRACE("bytes_used = %llx\n", Vcb->superblock.bytes_used);
2511 TRACE("chunk_item type = %llx\n", c->chunk_item->type);
2512
2513 if (c->chunk_item->type & BLOCK_FLAG_RAID0) {
2514 Vcb->superblock.bytes_used += c->used - c->oldused;
2515 } else if (c->chunk_item->type & BLOCK_FLAG_RAID1 || c->chunk_item->type & BLOCK_FLAG_DUPLICATE || c->chunk_item->type & BLOCK_FLAG_RAID10) {
2516 Vcb->superblock.bytes_used += 2 * (c->used - c->oldused);
2517 } else if (c->chunk_item->type & BLOCK_FLAG_RAID5) {
2518 FIXME("RAID5 not yet supported\n");
2519 ExFreePool(bgi);
2520 Status = STATUS_INTERNAL_ERROR;
2521 ExReleaseResourceLite(&c->lock);
2522 goto end;
2523 } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
2524 FIXME("RAID6 not yet supported\n");
2525 ExFreePool(bgi);
2526 Status = STATUS_INTERNAL_ERROR;
2527 ExReleaseResourceLite(&c->lock);
2528 goto end;
2529 } else { // SINGLE
2530 Vcb->superblock.bytes_used += c->used - c->oldused;
2531 }
2532
2533 TRACE("bytes_used = %llx\n", Vcb->superblock.bytes_used);
2534
2535 c->oldused = c->used;
2536 }
2537
2538 ExReleaseResourceLite(&c->lock);
2539
2540 le = le->Flink;
2541 }
2542
2543 if (flushed_extents) {
2544 ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE);
2545 if (!IsListEmpty(&Vcb->sector_checksums)) {
2546 update_checksum_tree(Vcb, Irp, rollback);
2547 }
2548 ExReleaseResourceLite(&Vcb->checksum_lock);
2549 }
2550
2551 Status = STATUS_SUCCESS;
2552
2553 end:
2554 ExReleaseResourceLite(&Vcb->chunk_lock);
2555
2556 return Status;
2557 }
2558
2559 static void get_first_item(tree* t, KEY* key) {
2560 LIST_ENTRY* le;
2561
2562 le = t->itemlist.Flink;
2563 while (le != &t->itemlist) {
2564 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
2565
2566 *key = td->key;
2567 return;
2568 }
2569 }
2570
2571 static NTSTATUS STDCALL split_tree_at(device_extension* Vcb, tree* t, tree_data* newfirstitem, UINT32 numitems, UINT32 size) {
2572 tree *nt, *pt;
2573 tree_data* td;
2574 tree_data* oldlastitem;
2575 // write_tree* wt2;
2576 // // tree_data *firsttd, *lasttd;
2577 // // LIST_ENTRY* le;
2578 // #ifdef DEBUG_PARANOID
2579 // KEY lastkey1, lastkey2;
2580 // traverse_ptr tp, next_tp;
2581 // ULONG numitems1, numitems2;
2582 // #endif
2583
2584 TRACE("splitting tree in %llx at (%llx,%x,%llx)\n", t->root->id, newfirstitem->key.obj_id, newfirstitem->key.obj_type, newfirstitem->key.offset);
2585
2586 // #ifdef DEBUG_PARANOID
2587 // lastkey1.obj_id = 0xffffffffffffffff;
2588 // lastkey1.obj_type = 0xff;
2589 // lastkey1.offset = 0xffffffffffffffff;
2590 //
2591 // if (!find_item(Vcb, t->root, &tp, &lastkey1, NULL, FALSE))
2592 // ERR("error - find_item failed\n");
2593 // else {
2594 // lastkey1 = tp.item->key;
2595 // numitems1 = 0;
2596 // while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
2597 // free_traverse_ptr(&tp);
2598 // tp = next_tp;
2599 // numitems1++;
2600 // }
2601 // free_traverse_ptr(&tp);
2602 // }
2603 // #endif
2604
2605 nt = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
2606 if (!nt) {
2607 ERR("out of memory\n");
2608 return STATUS_INSUFFICIENT_RESOURCES;
2609 }
2610
2611 RtlCopyMemory(&nt->header, &t->header, sizeof(tree_header));
2612 nt->header.address = 0;
2613 nt->header.generation = Vcb->superblock.generation;
2614 nt->header.num_items = t->header.num_items - numitems;
2615 nt->header.flags = HEADER_FLAG_MIXED_BACKREF;
2616
2617 nt->has_address = FALSE;
2618 nt->Vcb = Vcb;
2619 nt->parent = t->parent;
2620 nt->root = t->root;
2621 // nt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
2622 nt->new_address = 0;
2623 nt->has_new_address = FALSE;
2624 nt->flags = t->flags;
2625 InitializeListHead(&nt->itemlist);
2626
2627 // ExInitializeResourceLite(&nt->nonpaged->load_tree_lock);
2628
2629 oldlastitem = CONTAINING_RECORD(newfirstitem->list_entry.Blink, tree_data, list_entry);
2630
2631 // // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
2632 // // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
2633 // //
2634 // // TRACE("old tree in %x was from (%x,%x,%x) to (%x,%x,%x)\n",
2635 // // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
2636 // // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
2637 // //
2638 // // le = wt->tree->itemlist.Flink;
2639 // // while (le != &wt->tree->itemlist) {
2640 // // td = CONTAINING_RECORD(le, tree_data, list_entry);
2641 // // TRACE("old tree item was (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2642 // // le = le->Flink;
2643 // // }
2644
2645 nt->itemlist.Flink = &newfirstitem->list_entry;
2646 nt->itemlist.Blink = t->itemlist.Blink;
2647 nt->itemlist.Flink->Blink = &nt->itemlist;
2648 nt->itemlist.Blink->Flink = &nt->itemlist;
2649
2650 t->itemlist.Blink = &oldlastitem->list_entry;
2651 t->itemlist.Blink->Flink = &t->itemlist;
2652
2653 // // le = wt->tree->itemlist.Flink;
2654 // // while (le != &wt->tree->itemlist) {
2655 // // td = CONTAINING_RECORD(le, tree_data, list_entry);
2656 // // TRACE("old tree item now (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2657 // // le = le->Flink;
2658 // // }
2659 // //
2660 // // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
2661 // // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
2662 // //
2663 // // TRACE("old tree in %x is now from (%x,%x,%x) to (%x,%x,%x)\n",
2664 // // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
2665 // // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
2666
2667 nt->size = t->size - size;
2668 t->size = size;
2669 t->header.num_items = numitems;
2670 nt->write = TRUE;
2671
2672 InterlockedIncrement(&Vcb->open_trees);
2673 InsertTailList(&Vcb->trees, &nt->list_entry);
2674
2675 // // // TESTING
2676 // // td = wt->tree->items;
2677 // // while (td) {
2678 // // if (!td->ignore) {
2679 // // TRACE("old tree item: (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2680 // // }
2681 // // td = td->next;
2682 // // }
2683
2684 // // oldlastitem->next = NULL;
2685 // // wt->tree->lastitem = oldlastitem;
2686
2687 // // TRACE("last item is now (%x,%x,%x)\n", (UINT32)oldlastitem->key.obj_id, oldlastitem->key.obj_type, (UINT32)oldlastitem->key.offset);
2688
2689 if (nt->header.level > 0) {
2690 LIST_ENTRY* le = nt->itemlist.Flink;
2691
2692 while (le != &nt->itemlist) {
2693 tree_data* td2 = CONTAINING_RECORD(le, tree_data, list_entry);
2694
2695 if (td2->treeholder.tree)
2696 td2->treeholder.tree->parent = nt;
2697
2698 le = le->Flink;
2699 }
2700 }
2701
2702 if (nt->parent) {
2703 td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
2704 if (!td) {
2705 ERR("out of memory\n");
2706 return STATUS_INSUFFICIENT_RESOURCES;
2707 }
2708
2709 td->key = newfirstitem->key;
2710
2711 InsertHeadList(&t->paritem->list_entry, &td->list_entry);
2712
2713 td->ignore = FALSE;
2714 td->inserted = TRUE;
2715 td->treeholder.tree = nt;
2716 // td->treeholder.nonpaged->status = tree_holder_loaded;
2717 nt->paritem = td;
2718
2719 nt->parent->header.num_items++;
2720 nt->parent->size += sizeof(internal_node);
2721
2722 goto end;
2723 }
2724
2725 TRACE("adding new tree parent\n");
2726
2727 if (nt->header.level == 255) {
2728 ERR("cannot add parent to tree at level 255\n");
2729 return STATUS_INTERNAL_ERROR;
2730 }
2731
2732 pt = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG);
2733 if (!pt) {
2734 ERR("out of memory\n");
2735 return STATUS_INSUFFICIENT_RESOURCES;
2736 }
2737
2738 RtlCopyMemory(&pt->header, &nt->header, sizeof(tree_header));
2739 pt->header.address = 0;
2740 pt->header.num_items = 2;
2741 pt->header.level = nt->header.level + 1;
2742 pt->header.flags = HEADER_FLAG_MIXED_BACKREF;
2743
2744 pt->has_address = FALSE;
2745 pt->Vcb = Vcb;
2746 pt->parent = NULL;
2747 pt->paritem = NULL;
2748 pt->root = t->root;
2749 pt->new_address = 0;
2750 pt->has_new_address = FALSE;
2751 // pt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
2752 pt->size = pt->header.num_items * sizeof(internal_node);
2753 pt->flags = t->flags;
2754 InitializeListHead(&pt->itemlist);
2755
2756 // ExInitializeResourceLite(&pt->nonpaged->load_tree_lock);
2757
2758 InterlockedIncrement(&Vcb->open_trees);
2759 InsertTailList(&Vcb->trees, &pt->list_entry);
2760
2761 td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
2762 if (!td) {
2763 ERR("out of memory\n");
2764 return STATUS_INSUFFICIENT_RESOURCES;
2765 }
2766
2767 get_first_item(t, &td->key);
2768 td->ignore = FALSE;
2769 td->inserted = FALSE;
2770 td->treeholder.address = 0;
2771 td->treeholder.generation = Vcb->superblock.generation;
2772 td->treeholder.tree = t;
2773 // td->treeholder.nonpaged->status = tree_holder_loaded;
2774 InsertTailList(&pt->itemlist, &td->list_entry);
2775 t->paritem = td;
2776
2777 td = ExAllocatePoolWithTag(PagedPool, sizeof(tree_data), ALLOC_TAG);
2778 if (!td) {
2779 ERR("out of memory\n");
2780 return STATUS_INSUFFICIENT_RESOURCES;
2781 }
2782
2783 td->key = newfirstitem->key;
2784 td->ignore = FALSE;
2785 td->inserted = FALSE;
2786 td->treeholder.address = 0;
2787 td->treeholder.generation = Vcb->superblock.generation;
2788 td->treeholder.tree = nt;
2789 // td->treeholder.nonpaged->status = tree_holder_loaded;
2790 InsertTailList(&pt->itemlist, &td->list_entry);
2791 nt->paritem = td;
2792
2793 pt->write = TRUE;
2794
2795 t->root->treeholder.tree = pt;
2796
2797 t->parent = pt;
2798 nt->parent = pt;
2799
2800 end:
2801 t->root->root_item.bytes_used += Vcb->superblock.node_size;
2802
2803 // #ifdef DEBUG_PARANOID
2804 // lastkey2.obj_id = 0xffffffffffffffff;
2805 // lastkey2.obj_type = 0xff;
2806 // lastkey2.offset = 0xffffffffffffffff;
2807 //
2808 // if (!find_item(Vcb, wt->tree->root, &tp, &lastkey2, NULL, FALSE))
2809 // ERR("error - find_item failed\n");
2810 // else {
2811 // lastkey2 = tp.item->key;
2812 //
2813 // numitems2 = 0;
2814 // while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
2815 // free_traverse_ptr(&tp);
2816 // tp = next_tp;
2817 // numitems2++;
2818 // }
2819 // free_traverse_ptr(&tp);
2820 // }
2821 //
2822 // ERR("lastkey1 = %llx,%x,%llx\n", lastkey1.obj_id, lastkey1.obj_type, lastkey1.offset);
2823 // ERR("lastkey2 = %llx,%x,%llx\n", lastkey2.obj_id, lastkey2.obj_type, lastkey2.offset);
2824 // ERR("numitems1 = %u\n", numitems1);
2825 // ERR("numitems2 = %u\n", numitems2);
2826 // #endif
2827
2828 return STATUS_SUCCESS;
2829 }
2830
2831 static NTSTATUS STDCALL split_tree(device_extension* Vcb, tree* t) {
2832 LIST_ENTRY* le;
2833 UINT32 size, ds, numitems;
2834
2835 size = 0;
2836 numitems = 0;
2837
2838 // FIXME - naïve implementation: maximizes number of filled trees
2839
2840 le = t->itemlist.Flink;
2841 while (le != &t->itemlist) {
2842 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
2843
2844 if (!td->ignore) {
2845 if (t->header.level == 0)
2846 ds = sizeof(leaf_node) + td->size;
2847 else
2848 ds = sizeof(internal_node);
2849
2850 // FIXME - move back if previous item was deleted item with same key
2851 if (size + ds > Vcb->superblock.node_size - sizeof(tree_header))
2852 return split_tree_at(Vcb, t, td, numitems, size);
2853
2854 size += ds;
2855 numitems++;
2856 }
2857
2858 le = le->Flink;
2859 }
2860
2861 return STATUS_SUCCESS;
2862 }
2863
2864 static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback) {
2865 LIST_ENTRY* le;
2866 tree_data* nextparitem = NULL;
2867 NTSTATUS Status;
2868 tree *next_tree, *par;
2869 BOOL loaded;
2870
2871 TRACE("trying to amalgamate tree in root %llx, level %x (size %u)\n", t->root->id, t->header.level, t->size);
2872
2873 // FIXME - doesn't capture everything, as it doesn't ascend
2874 // FIXME - write proper function and put it in treefuncs.c
2875 le = t->paritem->list_entry.Flink;
2876 while (le != &t->parent->itemlist) {
2877 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
2878
2879 if (!td->ignore) {
2880 nextparitem = td;
2881 break;
2882 }
2883
2884 le = le->Flink;
2885 }
2886
2887 if (!nextparitem)
2888 return STATUS_SUCCESS;
2889
2890 // FIXME - loop, and capture more than one tree if we can
2891
2892 TRACE("nextparitem: key = %llx,%x,%llx\n", nextparitem->key.obj_id, nextparitem->key.obj_type, nextparitem->key.offset);
2893 // nextparitem = t->paritem;
2894
2895 // ExAcquireResourceExclusiveLite(&t->parent->nonpaged->load_tree_lock, TRUE);
2896
2897 Status = do_load_tree(Vcb, &nextparitem->treeholder, t->root, t->parent, nextparitem, &loaded, NULL);
2898 if (!NT_SUCCESS(Status)) {
2899 ERR("do_load_tree returned %08x\n", Status);
2900 return Status;
2901 }
2902
2903 // ExReleaseResourceLite(&t->parent->nonpaged->load_tree_lock);
2904
2905 next_tree = nextparitem->treeholder.tree;
2906
2907 if (t->size + next_tree->size <= Vcb->superblock.node_size - sizeof(tree_header)) {
2908 // merge two trees into one
2909
2910 t->header.num_items += next_tree->header.num_items;
2911 t->size += next_tree->size;
2912
2913 if (next_tree->header.level > 0) {
2914 le = next_tree->itemlist.Flink;
2915
2916 while (le != &next_tree->itemlist) {
2917 tree_data* td2 = CONTAINING_RECORD(le, tree_data, list_entry);
2918
2919 if (td2->treeholder.tree)
2920 td2->treeholder.tree->parent = t;
2921
2922 le = le->Flink;
2923 }
2924 }
2925
2926 t->itemlist.Blink->Flink = next_tree->itemlist.Flink;
2927 t->itemlist.Blink->Flink->Blink = t->itemlist.Blink;
2928 t->itemlist.Blink = next_tree->itemlist.Blink;
2929 t->itemlist.Blink->Flink = &t->itemlist;
2930
2931 // // TESTING
2932 // le = t->itemlist.Flink;
2933 // while (le != &t->itemlist) {
2934 // tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
2935 // if (!td->ignore) {
2936 // ERR("key: %llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
2937 // }
2938 // le = le->Flink;
2939 // }
2940
2941 next_tree->itemlist.Flink = next_tree->itemlist.Blink = &next_tree->itemlist;
2942
2943 next_tree->header.num_items = 0;
2944 next_tree->size = 0;
2945
2946 if (next_tree->has_new_address) { // delete associated EXTENT_ITEM
2947 Status = reduce_tree_extent(Vcb, next_tree->new_address, next_tree, Irp, rollback);
2948
2949 if (!NT_SUCCESS(Status)) {
2950 ERR("reduce_tree_extent returned %08x\n", Status);
2951 return Status;
2952 }
2953 } else if (next_tree->has_address) {
2954 Status = reduce_tree_extent(Vcb, next_tree->header.address, next_tree, Irp, rollback);
2955
2956 if (!NT_SUCCESS(Status)) {
2957 ERR("reduce_tree_extent returned %08x\n", Status);
2958 return Status;
2959 }
2960 }
2961
2962 if (!nextparitem->ignore) {
2963 nextparitem->ignore = TRUE;
2964 next_tree->parent->header.num_items--;
2965 next_tree->parent->size -= sizeof(internal_node);
2966 }
2967
2968 par = next_tree->parent;
2969 while (par) {
2970 par->write = TRUE;
2971 par = par->parent;
2972 }
2973
2974 RemoveEntryList(&nextparitem->list_entry);
2975 ExFreePool(next_tree->paritem);
2976 next_tree->paritem = NULL;
2977
2978 next_tree->root->root_item.bytes_used -= Vcb->superblock.node_size;
2979
2980 free_tree(next_tree);
2981 } else {
2982 // rebalance by moving items from second tree into first
2983 ULONG avg_size = (t->size + next_tree->size) / 2;
2984 KEY firstitem = {0, 0, 0};
2985
2986 TRACE("attempting rebalance\n");
2987
2988 le = next_tree->itemlist.Flink;
2989 while (le != &next_tree->itemlist && t->size < avg_size && next_tree->header.num_items > 1) {
2990 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
2991 ULONG size;
2992
2993 if (!td->ignore) {
2994 if (next_tree->header.level == 0)
2995 size = sizeof(leaf_node) + td->size;
2996 else
2997 size = sizeof(internal_node);
2998 } else
2999 size = 0;
3000
3001 if (t->size + size < Vcb->superblock.node_size - sizeof(tree_header)) {
3002 RemoveEntryList(&td->list_entry);
3003 InsertTailList(&t->itemlist, &td->list_entry);
3004
3005 if (next_tree->header.level > 0 && td->treeholder.tree)
3006 td->treeholder.tree->parent = t;
3007
3008 if (!td->ignore) {
3009 next_tree->size -= size;
3010 t->size += size;
3011 next_tree->header.num_items--;
3012 t->header.num_items++;
3013 }
3014 } else
3015 break;
3016
3017 le = next_tree->itemlist.Flink;
3018 }
3019
3020 le = next_tree->itemlist.Flink;
3021 while (le != &next_tree->itemlist) {
3022 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
3023
3024 if (!td->ignore) {
3025 firstitem = td->key;
3026 break;
3027 }
3028
3029 le = le->Flink;
3030 }
3031
3032 // ERR("firstitem = %llx,%x,%llx\n", firstitem.obj_id, firstitem.obj_type, firstitem.offset);
3033
3034 // FIXME - once ascension is working, make this work with parent's parent, etc.
3035 if (next_tree->paritem)
3036 next_tree->paritem->key = firstitem;
3037
3038 par = next_tree;
3039 while (par) {
3040 par->write = TRUE;
3041 par = par->parent;
3042 }
3043 }
3044
3045 return STATUS_SUCCESS;
3046 }
3047
3048 static NTSTATUS update_extent_level(device_extension* Vcb, UINT64 address, tree* t, UINT8 level, PIRP Irp, LIST_ENTRY* rollback) {
3049 KEY searchkey;
3050 traverse_ptr tp;
3051 NTSTATUS Status;
3052
3053 if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) {
3054 searchkey.obj_id = address;
3055 searchkey.obj_type = TYPE_METADATA_ITEM;
3056 searchkey.offset = t->header.level;
3057
3058 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
3059 if (!NT_SUCCESS(Status)) {
3060 ERR("error - find_item returned %08x\n", Status);
3061 return Status;
3062 }
3063
3064 if (!keycmp(&tp.item->key, &searchkey)) {
3065 EXTENT_ITEM_SKINNY_METADATA* eism;
3066
3067 if (tp.item->size > 0) {
3068 eism = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
3069
3070 if (!eism) {
3071 ERR("out of memory\n");
3072 return STATUS_INSUFFICIENT_RESOURCES;
3073 }
3074
3075 RtlCopyMemory(eism, tp.item->data, tp.item->size);
3076 } else
3077 eism = NULL;
3078
3079 delete_tree_item(Vcb, &tp, rollback);
3080
3081 if (!insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, tp.item->size, NULL, Irp, rollback)) {
3082 ERR("insert_tree_item failed\n");
3083 ExFreePool(eism);
3084 return STATUS_INTERNAL_ERROR;
3085 }
3086
3087 return STATUS_SUCCESS;
3088 }
3089 }
3090
3091 searchkey.obj_id = address;
3092 searchkey.obj_type = TYPE_EXTENT_ITEM;
3093 searchkey.offset = 0xffffffffffffffff;
3094
3095 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
3096 if (!NT_SUCCESS(Status)) {
3097 ERR("error - find_item returned %08x\n", Status);
3098 return Status;
3099 }
3100
3101 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
3102 EXTENT_ITEM_TREE* eit;
3103
3104 if (tp.item->size < sizeof(EXTENT_ITEM_TREE)) {
3105 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_TREE));
3106 return STATUS_INTERNAL_ERROR;
3107 }
3108
3109 eit = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
3110
3111 if (!eit) {
3112 ERR("out of memory\n");
3113 return STATUS_INSUFFICIENT_RESOURCES;
3114 }
3115
3116 RtlCopyMemory(eit, tp.item->data, tp.item->size);
3117
3118 delete_tree_item(Vcb, &tp, rollback);
3119
3120 eit->level = level;
3121
3122 if (!insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, eit, tp.item->size, NULL, Irp, rollback)) {
3123 ERR("insert_tree_item failed\n");
3124 ExFreePool(eit);
3125 return STATUS_INTERNAL_ERROR;
3126 }
3127
3128 return STATUS_SUCCESS;
3129 }
3130
3131 ERR("could not find EXTENT_ITEM for address %llx\n", address);
3132
3133 return STATUS_INTERNAL_ERROR;
3134 }
3135
3136 static NTSTATUS STDCALL do_splits(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
3137 // LIST_ENTRY *le, *le2;
3138 // write_tree* wt;
3139 // tree_data* td;
3140 UINT8 level, max_level;
3141 UINT32 min_size;
3142 BOOL empty, done_deletions = FALSE;
3143 NTSTATUS Status;
3144 tree* t;
3145
3146 TRACE("(%p)\n", Vcb);
3147
3148 max_level = 0;
3149
3150 for (level = 0; level <= 255; level++) {
3151 LIST_ENTRY *le, *nextle;
3152
3153 empty = TRUE;
3154
3155 TRACE("doing level %u\n", level);
3156
3157 le = Vcb->trees.Flink;
3158
3159 while (le != &Vcb->trees) {
3160 t = CONTAINING_RECORD(le, tree, list_entry);
3161
3162 nextle = le->Flink;
3163
3164 if (t->write && t->header.level == level) {
3165 empty = FALSE;
3166
3167 if (t->header.num_items == 0) {
3168 if (t->parent) {
3169 LIST_ENTRY* le2;
3170 KEY firstitem = {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
3171 #ifdef __REACTOS__
3172 (void)firstitem;
3173 #endif
3174
3175 done_deletions = TRUE;
3176
3177 le2 = t->itemlist.Flink;
3178 while (le2 != &t->itemlist) {
3179 tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
3180 firstitem = td->key;
3181 break;
3182 }
3183
3184 TRACE("deleting tree in root %llx (first item was %llx,%x,%llx)\n",
3185 t->root->id, firstitem.obj_id, firstitem.obj_type, firstitem.offset);
3186
3187 t->root->root_item.bytes_used -= Vcb->superblock.node_size;
3188
3189 if (t->has_new_address) { // delete associated EXTENT_ITEM
3190 Status = reduce_tree_extent(Vcb, t->new_address, t, Irp, rollback);
3191
3192 if (!NT_SUCCESS(Status)) {
3193 ERR("reduce_tree_extent returned %08x\n", Status);
3194 return Status;
3195 }
3196
3197 t->has_new_address = FALSE;
3198 } else if (t->has_address) {
3199 Status = reduce_tree_extent(Vcb,t->header.address, t, Irp, rollback);
3200
3201 if (!NT_SUCCESS(Status)) {
3202 ERR("reduce_tree_extent returned %08x\n", Status);
3203 return Status;
3204 }
3205
3206 t->has_address = FALSE;
3207 }
3208
3209 if (!t->paritem->ignore) {
3210 t->paritem->ignore = TRUE;
3211 t->parent->header.num_items--;
3212 t->parent->size -= sizeof(internal_node);
3213 }
3214
3215 RemoveEntryList(&t->paritem->list_entry);
3216 ExFreePool(t->paritem);
3217 t->paritem = NULL;
3218
3219 free_tree(t);
3220 } else if (t->header.level != 0) {
3221 if (t->has_new_address) {
3222 Status = update_extent_level(Vcb, t->new_address, t, 0, Irp, rollback);
3223
3224 if (!NT_SUCCESS(Status)) {
3225 ERR("update_extent_level returned %08x\n", Status);
3226 return Status;
3227 }
3228 }
3229
3230 t->header.level = 0;
3231 }
3232 } else if (t->size > Vcb->superblock.node_size - sizeof(tree_header)) {
3233 TRACE("splitting overlarge tree (%x > %x)\n", t->size, Vcb->superblock.node_size - sizeof(tree_header));
3234 Status = split_tree(Vcb, t);
3235
3236 if (!NT_SUCCESS(Status)) {
3237 ERR("split_tree returned %08x\n", Status);
3238 return Status;
3239 }
3240 }
3241 }
3242
3243 le = nextle;
3244 }
3245
3246 if (!empty) {
3247 max_level = level;
3248 } else {
3249 TRACE("nothing found for level %u\n", level);
3250 break;
3251 }
3252 }
3253
3254 min_size = (Vcb->superblock.node_size - sizeof(tree_header)) / 2;
3255
3256 for (level = 0; level <= max_level; level++) {
3257 LIST_ENTRY* le;
3258
3259 le = Vcb->trees.Flink;
3260
3261 while (le != &Vcb->trees) {
3262 t = CONTAINING_RECORD(le, tree, list_entry);
3263
3264 if (t->write && t->header.level == level && t->header.num_items > 0 && t->parent && t->size < min_size) {
3265 Status = try_tree_amalgamate(Vcb, t, Irp, rollback);
3266 if (!NT_SUCCESS(Status)) {
3267 ERR("try_tree_amalgamate returned %08x\n", Status);
3268 return Status;
3269 }
3270 }
3271
3272 le = le->Flink;
3273 }
3274 }
3275
3276 // simplify trees if top tree only has one entry
3277
3278 if (done_deletions) {
3279 for (level = max_level; level > 0; level--) {
3280 LIST_ENTRY *le, *nextle;
3281
3282 le = Vcb->trees.Flink;
3283 while (le != &Vcb->trees) {
3284 nextle = le->Flink;
3285 t = CONTAINING_RECORD(le, tree, list_entry);
3286
3287 if (t->write && t->header.level == level) {
3288 if (!t->parent && t->header.num_items == 1) {
3289 LIST_ENTRY* le2 = t->itemlist.Flink;
3290 tree_data* td;
3291 tree* child_tree = NULL;
3292
3293 while (le2 != &t->itemlist) {
3294 td = CONTAINING_RECORD(le2, tree_data, list_entry);
3295 if (!td->ignore)
3296 break;
3297 le2 = le2->Flink;
3298 }
3299
3300 TRACE("deleting top-level tree in root %llx with one item\n", t->root->id);
3301
3302 if (t->has_new_address) { // delete associated EXTENT_ITEM
3303 Status = reduce_tree_extent(Vcb, t->new_address, t, Irp, rollback);
3304
3305 if (!NT_SUCCESS(Status)) {
3306 ERR("reduce_tree_extent returned %08x\n", Status);
3307 return Status;
3308 }
3309
3310 t->has_new_address = FALSE;
3311 } else if (t->has_address) {
3312 Status = reduce_tree_extent(Vcb,t->header.address, t, Irp, rollback);
3313
3314 if (!NT_SUCCESS(Status)) {
3315 ERR("reduce_tree_extent returned %08x\n", Status);
3316 return Status;
3317 }
3318
3319 t->has_address = FALSE;
3320 }
3321
3322 if (!td->treeholder.tree) { // load first item if not already loaded
3323 KEY searchkey = {0,0,0};
3324 traverse_ptr tp;
3325
3326 Status = find_item(Vcb, t->root, &tp, &searchkey, FALSE, Irp);
3327 if (!NT_SUCCESS(Status)) {
3328 ERR("error - find_item returned %08x\n", Status);
3329 return Status;
3330 }
3331 }
3332
3333 child_tree = td->treeholder.tree;
3334
3335 if (child_tree) {
3336 child_tree->parent = NULL;
3337 child_tree->paritem = NULL;
3338 }
3339
3340 t->root->root_item.bytes_used -= Vcb->superblock.node_size;
3341
3342 free_tree(t);
3343
3344 if (child_tree)
3345 child_tree->root->treeholder.tree = child_tree;
3346 }
3347 }
3348
3349 le = nextle;
3350 }
3351 }
3352 }
3353
3354 return STATUS_SUCCESS;
3355 }
3356
3357 static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder* th, UINT8 level, PIRP Irp, LIST_ENTRY* rollback) {
3358 NTSTATUS Status;
3359
3360 if (level > 0) {
3361 if (!th->tree) {
3362 Status = load_tree(Vcb, th->address, r, &th->tree, NULL, NULL);
3363
3364 if (!NT_SUCCESS(Status)) {
3365 ERR("load_tree(%llx) returned %08x\n", th->address, Status);
3366 return Status;
3367 }
3368 }
3369
3370 if (th->tree->header.level > 0) {
3371 LIST_ENTRY* le = th->tree->itemlist.Flink;
3372
3373 while (le != &th->tree->itemlist) {
3374 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
3375
3376 if (!td->ignore) {
3377 Status = remove_root_extents(Vcb, r, &td->treeholder, th->tree->header.level - 1, Irp, rollback);
3378
3379 if (!NT_SUCCESS(Status)) {
3380 ERR("remove_root_extents returned %08x\n", Status);
3381 return Status;
3382 }
3383 }
3384
3385 le = le->Flink;
3386 }
3387 }
3388 }
3389
3390 if (!th->tree || th->tree->has_address) {
3391 Status = reduce_tree_extent(Vcb, th->address, NULL, Irp, rollback);
3392
3393 if (!NT_SUCCESS(Status)) {
3394 ERR("reduce_tree_extent(%llx) returned %08x\n", th->address, Status);
3395 return Status;
3396 }
3397 }
3398
3399 return STATUS_SUCCESS;
3400 }
3401
3402 static NTSTATUS drop_root(device_extension* Vcb, root* r, PIRP Irp, LIST_ENTRY* rollback) {
3403 NTSTATUS Status;
3404 KEY searchkey;
3405 traverse_ptr tp;
3406
3407 Status = remove_root_extents(Vcb, r, &r->treeholder, r->root_item.root_level, Irp, rollback);
3408 if (!NT_SUCCESS(Status)) {
3409 ERR("remove_root_extents returned %08x\n", Status);
3410 return Status;
3411 }
3412
3413 // remove entry in uuid root (tree 9)
3414 if (Vcb->uuid_root) {
3415 RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid.uuid[0], sizeof(UINT64));
3416 searchkey.obj_type = TYPE_SUBVOL_UUID;
3417 RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(UINT64)], sizeof(UINT64));
3418
3419 if (searchkey.obj_id != 0 || searchkey.offset != 0) {
3420 Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, FALSE, Irp);
3421 if (!NT_SUCCESS(Status)) {
3422 WARN("find_item returned %08x\n", Status);
3423 } else {
3424 if (!keycmp(&tp.item->key, &searchkey))
3425 delete_tree_item(Vcb, &tp, rollback);
3426 else
3427 WARN("could not find (%llx,%x,%llx) in uuid tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
3428 }
3429 }
3430 }
3431
3432 // delete ROOT_ITEM
3433
3434 searchkey.obj_id = r->id;
3435 searchkey.obj_type = TYPE_ROOT_ITEM;
3436 searchkey.offset = 0xffffffffffffffff;
3437
3438 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
3439 if (!NT_SUCCESS(Status)) {
3440 ERR("find_item returned %08x\n", Status);
3441 return Status;
3442 }
3443
3444 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
3445 delete_tree_item(Vcb, &tp, rollback);
3446 else
3447 WARN("could not find (%llx,%x,%llx) in root_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
3448
3449 // delete items in tree cache
3450
3451 free_trees_root(Vcb, r);
3452
3453 return STATUS_SUCCESS;
3454 }
3455
3456 static NTSTATUS drop_roots(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
3457 LIST_ENTRY *le = Vcb->drop_roots.Flink, *le2;
3458 NTSTATUS Status;
3459
3460 while (le != &Vcb->drop_roots) {
3461 root* r = CONTAINING_RECORD(le, root, list_entry);
3462
3463 le2 = le->Flink;
3464
3465 Status = drop_root(Vcb, r, Irp, rollback);
3466 if (!NT_SUCCESS(Status)) {
3467 ERR("drop_root(%llx) returned %08x\n", r->id, Status);
3468 return Status;
3469 }
3470
3471 le = le2;
3472 }
3473
3474 return STATUS_SUCCESS;
3475 }
3476
3477 static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, PIRP Irp, LIST_ENTRY* rollback) {
3478 CHUNK_ITEM* ci;
3479 CHUNK_ITEM_STRIPE* cis;
3480 BLOCK_GROUP_ITEM* bgi;
3481 UINT16 i, factor;
3482 NTSTATUS Status;
3483
3484 ci = ExAllocatePoolWithTag(PagedPool, c->size, ALLOC_TAG);
3485 if (!ci) {
3486 ERR("out of memory\n");
3487 return STATUS_INSUFFICIENT_RESOURCES;
3488 }
3489
3490 RtlCopyMemory(ci, c->chunk_item, c->size);
3491
3492 if (!insert_tree_item(Vcb, Vcb->chunk_root, 0x100, TYPE_CHUNK_ITEM, c->offset, ci, c->size, NULL, Irp, rollback)) {
3493 ERR("insert_tree_item failed\n");
3494 ExFreePool(ci);
3495 return STATUS_INTERNAL_ERROR;
3496 }
3497
3498 if (c->chunk_item->type & BLOCK_FLAG_SYSTEM) {
3499 Status = add_to_bootstrap(Vcb, 0x100, TYPE_CHUNK_ITEM, c->offset, ci, c->size);
3500 if (!NT_SUCCESS(Status)) {
3501 ERR("add_to_bootstrap returned %08x\n", Status);
3502 return Status;
3503 }
3504 }
3505
3506 // add BLOCK_GROUP_ITEM to tree 2
3507
3508 bgi = ExAllocatePoolWithTag(PagedPool, sizeof(BLOCK_GROUP_ITEM), ALLOC_TAG);
3509 if (!bgi) {
3510 ERR("out of memory\n");
3511 return STATUS_INSUFFICIENT_RESOURCES;
3512 }
3513
3514 bgi->used = c->used;
3515 bgi->chunk_tree = 0x100;
3516 bgi->flags = c->chunk_item->type;
3517
3518 if (!insert_tree_item(Vcb, Vcb->extent_root, c->offset, TYPE_BLOCK_GROUP_ITEM, c->chunk_item->size, bgi, sizeof(BLOCK_GROUP_ITEM), NULL, Irp, rollback)) {
3519 ERR("insert_tree_item failed\n");
3520 ExFreePool(bgi);
3521 return STATUS_INSUFFICIENT_RESOURCES;
3522 }
3523
3524 if (c->chunk_item->type & BLOCK_FLAG_RAID0)
3525 factor = c->chunk_item->num_stripes;
3526 else if (c->chunk_item->type & BLOCK_FLAG_RAID10)
3527 factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes;
3528 else // SINGLE, DUPLICATE, RAID1
3529 factor = 1;
3530
3531 // add DEV_EXTENTs to tree 4
3532
3533 cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3534
3535 for (i = 0; i < c->chunk_item->num_stripes; i++) {
3536 DEV_EXTENT* de;
3537
3538 de = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_EXTENT), ALLOC_TAG);
3539 if (!de) {
3540 ERR("out of memory\n");
3541 return STATUS_INSUFFICIENT_RESOURCES;
3542 }
3543
3544 de->chunktree = Vcb->chunk_root->id;
3545 de->objid = 0x100;
3546 de->address = c->offset;
3547 de->length = c->chunk_item->size / factor;
3548 de->chunktree_uuid = Vcb->chunk_root->treeholder.tree->header.chunk_tree_uuid;
3549
3550 if (!insert_tree_item(Vcb, Vcb->dev_root, c->devices[i]->devitem.dev_id, TYPE_DEV_EXTENT, cis[i].offset, de, sizeof(DEV_EXTENT), NULL, Irp, rollback)) {
3551 ERR("insert_tree_item failed\n");
3552 ExFreePool(de);
3553 return STATUS_INTERNAL_ERROR;
3554 }
3555
3556 // FIXME - no point in calling this twice for the same device
3557 Status = update_dev_item(Vcb, c->devices[i], Irp, rollback);
3558 if (!NT_SUCCESS(Status)) {
3559 ERR("update_dev_item returned %08x\n", Status);
3560 return Status;
3561 }
3562 }
3563
3564 c->created = FALSE;
3565
3566 return STATUS_SUCCESS;
3567 }
3568
3569 static void remove_from_bootstrap(device_extension* Vcb, UINT64 obj_id, UINT8 obj_type, UINT64 offset) {
3570 sys_chunk* sc2;
3571 LIST_ENTRY* le;
3572
3573 le = Vcb->sys_chunks.Flink;
3574 while (le != &Vcb->sys_chunks) {
3575 sc2 = CONTAINING_RECORD(le, sys_chunk, list_entry);
3576
3577 if (sc2->key.obj_id == obj_id && sc2->key.obj_type == obj_type && sc2->key.offset == offset) {
3578 RemoveEntryList(&sc2->list_entry);
3579
3580 Vcb->superblock.n -= sizeof(KEY) + sc2->size;
3581
3582 ExFreePool(sc2->data);
3583 ExFreePool(sc2);
3584 regen_bootstrap(Vcb);
3585 return;
3586 }
3587
3588 le = le->Flink;
3589 }
3590 }
3591
3592 static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, PIRP Irp, LIST_ENTRY* rollback) {
3593 NTSTATUS Status;
3594 KEY searchkey;
3595 traverse_ptr tp;
3596 UINT64 i, factor;
3597 CHUNK_ITEM_STRIPE* cis;
3598
3599 TRACE("dropping chunk %llx\n", c->offset);
3600
3601 // remove free space cache
3602 if (c->cache) {
3603 c->cache->deleted = TRUE;
3604
3605 flush_fcb(c->cache, TRUE, Irp, rollback);
3606
3607 free_fcb(c->cache);
3608
3609 searchkey.obj_id = FREE_SPACE_CACHE_ID;
3610 searchkey.obj_type = 0;
3611 searchkey.offset = c->offset;
3612
3613 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
3614 if (!NT_SUCCESS(Status)) {
3615 ERR("error - find_item returned %08x\n", Status);
3616 return Status;
3617 }
3618
3619 if (!keycmp(&tp.item->key, &searchkey)) {
3620 delete_tree_item(Vcb, &tp, rollback);
3621 }
3622 }
3623
3624 if (c->chunk_item->type & BLOCK_FLAG_RAID0)
3625 factor = c->chunk_item->num_stripes;
3626 else if (c->chunk_item->type & BLOCK_FLAG_RAID10)
3627 factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes;
3628 else // SINGLE, DUPLICATE, RAID1
3629 factor = 1;
3630
3631 cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3632 for (i = 0; i < c->chunk_item->num_stripes; i++) {
3633 if (!c->created) {
3634 // remove DEV_EXTENTs from tree 4
3635 searchkey.obj_id = cis[i].dev_id;
3636 searchkey.obj_type = TYPE_DEV_EXTENT;
3637 searchkey.offset = cis[i].offset;
3638
3639 Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, FALSE, Irp);
3640 if (!NT_SUCCESS(Status)) {
3641 ERR("error - find_item returned %08x\n", Status);
3642 return Status;
3643 }
3644
3645 if (!keycmp(&tp.item->key, &searchkey)) {
3646 delete_tree_item(Vcb, &tp, rollback);
3647
3648 if (tp.item->size >= sizeof(DEV_EXTENT)) {
3649 DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data;
3650
3651 c->devices[i]->devitem.bytes_used -= de->length;
3652
3653 space_list_add2(&c->devices[i]->space, NULL, cis[i].offset, de->length, rollback);
3654 }
3655 } else
3656 WARN("could not find (%llx,%x,%llx) in dev tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset);
3657 } else {
3658 UINT64 len = c->chunk_item->size / factor;
3659
3660 c->devices[i]->devitem.bytes_used -= len;
3661 space_list_add2(&c->devices[i]->space, NULL, cis[i].offset, len, rollback);
3662 }
3663 }
3664
3665 // modify DEV_ITEMs in chunk tree
3666 for (i = 0; i < c->chunk_item->num_stripes; i++) {
3667 if (c->devices[i]) {
3668 UINT64 j;
3669 DEV_ITEM* di;
3670
3671 searchkey.obj_id = 1;
3672 searchkey.obj_type = TYPE_DEV_ITEM;
3673 searchkey.offset = c->devices[i]->devitem.dev_id;
3674
3675 Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
3676 if (!NT_SUCCESS(Status)) {
3677 ERR("error - find_item returned %08x\n", Status);
3678 return Status;
3679 }
3680
3681 if (keycmp(&tp.item->key, &searchkey)) {
3682 ERR("error - could not find DEV_ITEM for device %llx\n", searchkey.offset);
3683 return STATUS_INTERNAL_ERROR;
3684 }
3685
3686 delete_tree_item(Vcb, &tp, rollback);
3687
3688 di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG);
3689 if (!di) {
3690 ERR("out of memory\n");
3691 return STATUS_INSUFFICIENT_RESOURCES;
3692 }
3693
3694 RtlCopyMemory(di, &c->devices[i]->devitem, sizeof(DEV_ITEM));
3695
3696 if (!insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, c->devices[i]->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, Irp, rollback)) {
3697 ERR("insert_tree_item failed\n");
3698 return STATUS_INTERNAL_ERROR;
3699 }
3700
3701 for (j = i + 1; j < c->chunk_item->num_stripes; j++) {
3702 if (c->devices[j] == c->devices[i])
3703 c->devices[j] = NULL;
3704 }
3705 }
3706 }
3707
3708 if (!c->created) {
3709 // remove CHUNK_ITEM from chunk tree
3710 searchkey.obj_id = 0x100;
3711 searchkey.obj_type = TYPE_CHUNK_ITEM;
3712 searchkey.offset = c->offset;
3713
3714 Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, FALSE, Irp);
3715 if (!NT_SUCCESS(Status)) {
3716 ERR("error - find_item returned %08x\n", Status);
3717 return Status;
3718 }
3719
3720 if (!keycmp(&tp.item->key, &searchkey))
3721 delete_tree_item(Vcb, &tp, rollback);
3722 else
3723 WARN("could not find CHUNK_ITEM for chunk %llx\n", c->offset);
3724
3725 // remove BLOCK_GROUP_ITEM from extent tree
3726 searchkey.obj_id = c->offset;
3727 searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM;
3728 searchkey.offset = 0xffffffffffffffff;
3729
3730 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
3731 if (!NT_SUCCESS(Status)) {
3732 ERR("error - find_item returned %08x\n", Status);
3733 return Status;
3734 }
3735
3736 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
3737 delete_tree_item(Vcb, &tp, rollback);
3738 else
3739 WARN("could not find BLOCK_GROUP_ITEM for chunk %llx\n", c->offset);
3740 }
3741
3742 if (c->chunk_item->type & BLOCK_FLAG_SYSTEM)
3743 remove_from_bootstrap(Vcb, 0x100, TYPE_CHUNK_ITEM, c->offset);
3744
3745 RemoveEntryList(&c->list_entry);
3746
3747 if (c->list_entry_changed.Flink)
3748 RemoveEntryList(&c->list_entry_changed);
3749
3750 ExFreePool(c->chunk_item);
3751 ExFreePool(c->devices);
3752
3753 while (!IsListEmpty(&c->space)) {
3754 space* s = CONTAINING_RECORD(c->space.Flink, space, list_entry);
3755
3756 RemoveEntryList(&s->list_entry);
3757 ExFreePool(s);
3758 }
3759
3760 while (!IsListEmpty(&c->deleting)) {
3761 space* s = CONTAINING_RECORD(c->deleting.Flink, space, list_entry);
3762
3763 RemoveEntryList(&s->list_entry);
3764 ExFreePool(s);
3765 }
3766
3767 ExDeleteResourceLite(&c->lock);
3768 ExDeleteResourceLite(&c->changed_extents_lock);
3769
3770 ExFreePool(c);
3771
3772 return STATUS_SUCCESS;
3773 }
3774
3775 static NTSTATUS update_chunks(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
3776 LIST_ENTRY *le = Vcb->chunks_changed.Flink, *le2;
3777 NTSTATUS Status;
3778 UINT64 used_minus_cache;
3779
3780 ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, TRUE);
3781
3782 // FIXME - do tree chunks before data chunks
3783
3784 while (le != &Vcb->chunks_changed) {
3785 chunk* c = CONTAINING_RECORD(le, chunk, list_entry_changed);
3786
3787 le2 = le->Flink;
3788
3789 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
3790
3791 used_minus_cache = c->used;
3792
3793 // subtract self-hosted cache
3794 if (used_minus_cache > 0 && c->chunk_item->type & BLOCK_FLAG_DATA && c->cache && c->cache->inode_item.st_size == c->used) {
3795 LIST_ENTRY* le3;
3796
3797 le3 = c->cache->extents.Flink;
3798 while (le3 != &c->cache->extents) {
3799 extent* ext = CONTAINING_RECORD(le3, extent, list_entry);
3800 EXTENT_DATA* ed = ext->data;
3801
3802 if (!ext->ignore) {
3803 if (ext->datalen < sizeof(EXTENT_DATA)) {
3804 ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA));
3805 break;
3806 }
3807
3808 if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
3809 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
3810
3811 if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
3812 ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen,
3813 sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
3814 break;
3815 }
3816
3817 if (ed2->size != 0 && ed2->address >= c->offset && ed2->address + ed2->size <= c->offset + c->chunk_item->size)
3818 used_minus_cache -= ed2->size;
3819 }
3820 }
3821
3822 le3 = le3->Flink;
3823 }
3824 }
3825
3826 if (used_minus_cache == 0) {
3827 Status = drop_chunk(Vcb, c, Irp, rollback);
3828 if (!NT_SUCCESS(Status)) {
3829 ERR("drop_chunk returned %08x\n", Status);
3830 ExReleaseResourceLite(&c->lock);
3831 ExReleaseResourceLite(&Vcb->chunk_lock);
3832 return Status;
3833 }
3834 } else if (c->created) {
3835 Status = create_chunk(Vcb, c, Irp, rollback);
3836 if (!NT_SUCCESS(Status)) {
3837 ERR("create_chunk returned %08x\n", Status);
3838 ExReleaseResourceLite(&c->lock);
3839 ExReleaseResourceLite(&Vcb->chunk_lock);
3840 return Status;
3841 }
3842 }
3843
3844 if (used_minus_cache > 0)
3845 ExReleaseResourceLite(&c->lock);
3846
3847 le = le2;
3848 }
3849
3850 ExReleaseResourceLite(&Vcb->chunk_lock);
3851
3852 return STATUS_SUCCESS;
3853 }
3854
3855 static NTSTATUS STDCALL set_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, UINT8* data, UINT16 datalen, PIRP Irp, LIST_ENTRY* rollback) {
3856 KEY searchkey;
3857 traverse_ptr tp;
3858 ULONG xasize, maxlen;
3859 DIR_ITEM* xa;
3860 NTSTATUS Status;
3861
3862 TRACE("(%p, %llx, %llx, %s, %08x, %p, %u)\n", Vcb, subvol->id, inode, name, crc32, data, datalen);
3863
3864 searchkey.obj_id = inode;
3865 searchkey.obj_type = TYPE_XATTR_ITEM;
3866 searchkey.offset = crc32;
3867
3868 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
3869 if (!NT_SUCCESS(Status)) {
3870 ERR("error - find_item returned %08x\n", Status);
3871 return Status;
3872 }
3873
3874 xasize = sizeof(DIR_ITEM) - 1 + (ULONG)strlen(name) + datalen;
3875 maxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node);
3876
3877 if (!keycmp(&tp.item->key, &searchkey)) { // key exists
3878 UINT8* newdata;
3879 ULONG size = tp.item->size;
3880
3881 xa = (DIR_ITEM*)tp.item->data;
3882
3883 if (tp.item->size < sizeof(DIR_ITEM)) {
3884 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(DIR_ITEM));
3885 } else {
3886 while (TRUE) {
3887 ULONG oldxasize;
3888
3889 if (size < sizeof(DIR_ITEM) || size < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) {
3890 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
3891 break;
3892 }
3893
3894 oldxasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
3895
3896 if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
3897 UINT64 pos;
3898
3899 // replace
3900
3901 if (tp.item->size + xasize - oldxasize > maxlen) {
3902 ERR("DIR_ITEM would be over maximum size (%u + %u - %u > %u)\n", tp.item->size, xasize, oldxasize, maxlen);
3903 return STATUS_INTERNAL_ERROR;
3904 }
3905
3906 newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize - oldxasize, ALLOC_TAG);
3907 if (!newdata) {
3908 ERR("out of memory\n");
3909 return STATUS_INSUFFICIENT_RESOURCES;
3910 }
3911
3912 pos = (UINT8*)xa - tp.item->data;
3913 if (pos + oldxasize < tp.item->size) { // copy after changed xattr
3914 RtlCopyMemory(newdata + pos + xasize, tp.item->data + pos + oldxasize, tp.item->size - pos - oldxasize);
3915 }
3916
3917 if (pos > 0) { // copy before changed xattr
3918 RtlCopyMemory(newdata, tp.item->data, pos);
3919 xa = (DIR_ITEM*)(newdata + pos);
3920 } else
3921 xa = (DIR_ITEM*)newdata;
3922
3923 xa->key.obj_id = 0;
3924 xa->key.obj_type = 0;
3925 xa->key.offset = 0;
3926 xa->transid = Vcb->superblock.generation;
3927 xa->m = datalen;
3928 xa->n = (UINT16)strlen(name);
3929 xa->type = BTRFS_TYPE_EA;
3930 RtlCopyMemory(xa->name, name, strlen(name));
3931 RtlCopyMemory(xa->name + strlen(name), data, datalen);
3932
3933 delete_tree_item(Vcb, &tp, rollback);
3934 insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize - oldxasize, NULL, Irp, rollback);
3935
3936 break;
3937 }
3938
3939 if ((UINT8*)xa - (UINT8*)tp.item->data + oldxasize >= size) {
3940 // not found, add to end of data
3941
3942 if (tp.item->size + xasize > maxlen) {
3943 ERR("DIR_ITEM would be over maximum size (%u + %u > %u)\n", tp.item->size, xasize, maxlen);
3944 return STATUS_INTERNAL_ERROR;
3945 }
3946
3947 newdata = ExAllocatePoolWithTag(PagedPool, tp.item->size + xasize, ALLOC_TAG);
3948 if (!newdata) {
3949 ERR("out of memory\n");
3950 return STATUS_INSUFFICIENT_RESOURCES;
3951 }
3952
3953 RtlCopyMemory(newdata, tp.item->data, tp.item->size);
3954
3955 xa = (DIR_ITEM*)((UINT8*)newdata + tp.item->size);
3956 xa->key.obj_id = 0;
3957 xa->key.obj_type = 0;
3958 xa->key.offset = 0;
3959 xa->transid = Vcb->superblock.generation;
3960 xa->m = datalen;
3961 xa->n = (UINT16)strlen(name);
3962 xa->type = BTRFS_TYPE_EA;
3963 RtlCopyMemory(xa->name, name, strlen(name));
3964 RtlCopyMemory(xa->name + strlen(name), data, datalen);
3965
3966 delete_tree_item(Vcb, &tp, rollback);
3967 insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, tp.item->size + xasize, NULL, Irp, rollback);
3968
3969 break;
3970 } else {
3971 xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
3972 size -= oldxasize;
3973 }
3974 }
3975 }
3976 } else {
3977 if (xasize > maxlen) {
3978 ERR("DIR_ITEM would be over maximum size (%u > %u)\n", xasize, maxlen);
3979 return STATUS_INTERNAL_ERROR;
3980 }
3981
3982 xa = ExAllocatePoolWithTag(PagedPool, xasize, ALLOC_TAG);
3983 if (!xa) {
3984 ERR("out of memory\n");
3985 return STATUS_INSUFFICIENT_RESOURCES;
3986 }
3987
3988 xa->key.obj_id = 0;
3989 xa->key.obj_type = 0;
3990 xa->key.offset = 0;
3991 xa->transid = Vcb->superblock.generation;
3992 xa->m = datalen;
3993 xa->n = (UINT16)strlen(name);
3994 xa->type = BTRFS_TYPE_EA;
3995 RtlCopyMemory(xa->name, name, strlen(name));
3996 RtlCopyMemory(xa->name + strlen(name), data, datalen);
3997
3998 insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, NULL, Irp, rollback);
3999 }
4000
4001 return STATUS_SUCCESS;
4002 }
4003
4004 static BOOL STDCALL delete_xattr(device_extension* Vcb, root* subvol, UINT64 inode, char* name, UINT32 crc32, PIRP Irp, LIST_ENTRY* rollback) {
4005 KEY searchkey;
4006 traverse_ptr tp;
4007 DIR_ITEM* xa;
4008 NTSTATUS Status;
4009
4010 TRACE("(%p, %llx, %llx, %s, %08x)\n", Vcb, subvol->id, inode, name, crc32);
4011
4012 searchkey.obj_id = inode;
4013 searchkey.obj_type = TYPE_XATTR_ITEM;
4014 searchkey.offset = crc32;
4015
4016 Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE, Irp);
4017 if (!NT_SUCCESS(Status)) {
4018 ERR("error - find_item returned %08x\n", Status);
4019 return FALSE;
4020 }
4021
4022 if (!keycmp(&tp.item->key, &searchkey)) { // key exists
4023 ULONG size = tp.item->size;
4024
4025 if (tp.item->size < sizeof(DIR_ITEM)) {
4026 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(DIR_ITEM));
4027
4028 return FALSE;
4029 } else {
4030 xa = (DIR_ITEM*)tp.item->data;
4031
4032 while (TRUE) {
4033 ULONG oldxasize;
4034
4035 if (size < sizeof(DIR_ITEM) || size < sizeof(DIR_ITEM) - 1 + xa->m + xa->n) {
4036 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
4037
4038 return FALSE;
4039 }
4040
4041 oldxasize = sizeof(DIR_ITEM) - 1 + xa->m + xa->n;
4042
4043 if (xa->n == strlen(name) && RtlCompareMemory(name, xa->name, xa->n) == xa->n) {
4044 ULONG newsize;
4045 UINT8 *newdata, *dioff;
4046
4047 newsize = tp.item->size - (sizeof(DIR_ITEM) - 1 + xa->n + xa->m);
4048
4049 delete_tree_item(Vcb, &tp, rollback);
4050
4051 if (newsize == 0) {
4052 TRACE("xattr %s deleted\n", name);
4053
4054 return TRUE;
4055 }
4056
4057 // FIXME - deleting collisions almost certainly works, but we should test it properly anyway
4058 newdata = ExAllocatePoolWithTag(PagedPool, newsize, ALLOC_TAG);
4059 if (!newdata) {
4060 ERR("out of memory\n");
4061 return FALSE;
4062 }
4063
4064 if ((UINT8*)xa > tp.item->data) {
4065 RtlCopyMemory(newdata, tp.item->data, (UINT8*)xa - tp.item->data);
4066 dioff = newdata + ((UINT8*)xa - tp.item->data);
4067 } else {
4068 dioff = newdata;
4069 }
4070
4071 if ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data < tp.item->size)
4072 RtlCopyMemory(dioff, &xa->name[xa->n+xa->m], tp.item->size - ((UINT8*)&xa->name[xa->n+xa->m] - tp.item->data));
4073
4074 insert_tree_item(Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, newdata, newsize, NULL, Irp, rollback);
4075
4076
4077 return TRUE;
4078 }
4079
4080 if (xa->m + xa->n >= size) { // FIXME - test this works
4081 WARN("xattr %s not found\n", name);
4082
4083 return FALSE;
4084 } else {
4085 xa = (DIR_ITEM*)&xa->name[xa->m + xa->n];
4086 size -= oldxasize;
4087 }
4088 }
4089 }
4090 } else {
4091 WARN("xattr %s not found\n", name);
4092
4093 return FALSE;
4094 }
4095 }
4096
4097 static NTSTATUS insert_sparse_extent(fcb* fcb, UINT64 start, UINT64 length, PIRP Irp, LIST_ENTRY* rollback) {
4098 EXTENT_DATA* ed;
4099 EXTENT_DATA2* ed2;
4100
4101 TRACE("((%llx, %llx), %llx, %llx)\n", fcb->subvol->id, fcb->inode, start, length);
4102
4103 ed = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
4104 if (!ed) {
4105 ERR("out of memory\n");
4106 return STATUS_INSUFFICIENT_RESOURCES;
4107 }
4108
4109 ed->generation = fcb->Vcb->superblock.generation;
4110 ed->decoded_size = length;
4111 ed->compression = BTRFS_COMPRESSION_NONE;
4112 ed->encryption = BTRFS_ENCRYPTION_NONE;
4113 ed->encoding = BTRFS_ENCODING_NONE;
4114 ed->type = EXTENT_TYPE_REGULAR;
4115
4116 ed2 = (EXTENT_DATA2*)ed->data;
4117 ed2->address = 0;
4118 ed2->size = 0;
4119 ed2->offset = 0;
4120 ed2->num_bytes = length;
4121
4122 if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, start, ed, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), NULL, Irp, rollback)) {
4123 ERR("insert_tree_item failed\n");
4124 return STATUS_INTERNAL_ERROR;
4125 }
4126
4127 return STATUS_SUCCESS;
4128 }
4129
4130 void flush_fcb(fcb* fcb, BOOL cache, PIRP Irp, LIST_ENTRY* rollback) {
4131 traverse_ptr tp;
4132 KEY searchkey;
4133 NTSTATUS Status;
4134 INODE_ITEM* ii;
4135 UINT64 ii_offset;
4136 #ifdef DEBUG_PARANOID
4137 UINT64 old_size = 0;
4138 BOOL extents_changed;
4139 #endif
4140
4141 // ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
4142
4143 while (!IsListEmpty(&fcb->index_list)) {
4144 LIST_ENTRY* le = RemoveHeadList(&fcb->index_list);
4145 index_entry* ie = CONTAINING_RECORD(le, index_entry, list_entry);
4146
4147 if (ie->utf8.Buffer) ExFreePool(ie->utf8.Buffer);
4148 if (ie->filepart_uc.Buffer) ExFreePool(ie->filepart_uc.Buffer);
4149 ExFreePool(ie);
4150 }
4151
4152 fcb->index_loaded = FALSE;
4153
4154 if (fcb->ads) {
4155 if (fcb->deleted)
4156 delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, Irp, rollback);
4157 else {
4158 Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adshash, (UINT8*)fcb->adsdata.Buffer, fcb->adsdata.Length, Irp, rollback);
4159 if (!NT_SUCCESS(Status)) {
4160 ERR("set_xattr returned %08x\n", Status);
4161 goto end;
4162 }
4163 }
4164 goto end;
4165 }
4166
4167 #ifdef DEBUG_PARANOID
4168 extents_changed = fcb->extents_changed;
4169 #endif
4170
4171 if (fcb->extents_changed) {
4172 BOOL b;
4173 traverse_ptr next_tp;
4174 LIST_ENTRY* le;
4175 BOOL prealloc = FALSE, extents_inline = FALSE;
4176 UINT64 last_end;
4177
4178 // delete ignored extent items
4179 le = fcb->extents.Flink;
4180 while (le != &fcb->extents) {
4181 LIST_ENTRY* le2 = le->Flink;
4182 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
4183
4184 if (ext->ignore) {
4185 RemoveEntryList(&ext->list_entry);
4186 ExFreePool(ext->data);
4187 ExFreePool(ext);
4188 }
4189
4190 le = le2;
4191 }
4192
4193 le = fcb->extents.Flink;
4194 while (le != &fcb->extents) {
4195 LIST_ENTRY* le2 = le->Flink;
4196 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
4197
4198 if ((ext->data->type == EXTENT_TYPE_REGULAR || ext->data->type == EXTENT_TYPE_PREALLOC) && le->Flink != &fcb->extents) {
4199 extent* nextext = CONTAINING_RECORD(le->Flink, extent, list_entry);
4200
4201 if (ext->data->type == nextext->data->type) {
4202 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->data->data;
4203 EXTENT_DATA2* ned2 = (EXTENT_DATA2*)nextext->data->data;
4204
4205 if (ed2->size != 0 && ed2->address == ned2->address && ed2->size == ned2->size &&
4206 nextext->offset == ext->offset + ed2->num_bytes && ned2->offset == ed2->offset + ed2->num_bytes) {
4207 chunk* c;
4208
4209 ext->data->generation = fcb->Vcb->superblock.generation;
4210 ed2->num_bytes += ned2->num_bytes;
4211
4212 RemoveEntryList(&nextext->list_entry);
4213 ExFreePool(nextext->data);
4214 ExFreePool(nextext);
4215
4216 c = get_chunk_from_address(fcb->Vcb, ed2->address);
4217
4218 if (!c) {
4219 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
4220 } else {
4221 Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, -1,
4222 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
4223 if (!NT_SUCCESS(Status)) {
4224 ERR("update_changed_extent_ref returned %08x\n", Status);
4225 goto end;
4226 }
4227 }
4228
4229 le2 = le;
4230 }
4231 }
4232 }
4233
4234 le = le2;
4235 }
4236
4237 // delete existing EXTENT_DATA items
4238
4239 searchkey.obj_id = fcb->inode;
4240 searchkey.obj_type = TYPE_EXTENT_DATA;
4241 searchkey.offset = 0;
4242
4243 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
4244 if (!NT_SUCCESS(Status)) {
4245 ERR("error - find_item returned %08x\n", Status);
4246 goto end;
4247 }
4248
4249 do {
4250 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type)
4251 delete_tree_item(fcb->Vcb, &tp, rollback);
4252
4253 b = find_next_item(fcb->Vcb, &tp, &next_tp, FALSE, Irp);
4254
4255 if (b) {
4256 tp = next_tp;
4257
4258 if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > searchkey.obj_type))
4259 break;
4260 }
4261 } while (b);
4262
4263 if (!fcb->deleted) {
4264 // add new EXTENT_DATAs
4265
4266 last_end = 0;
4267
4268 le = fcb->extents.Flink;
4269 while (le != &fcb->extents) {
4270 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
4271 EXTENT_DATA* ed;
4272
4273 if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES) && ext->offset > last_end) {
4274 Status = insert_sparse_extent(fcb, last_end, ext->offset - last_end, Irp, rollback);
4275 if (!NT_SUCCESS(Status)) {
4276 ERR("insert_sparse_extent returned %08x\n", Status);
4277 goto end;
4278 }
4279 }
4280
4281 ed = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
4282 if (!ed) {
4283 ERR("out of memory\n");
4284 Status = STATUS_INSUFFICIENT_RESOURCES;
4285 goto end;
4286 }
4287
4288 RtlCopyMemory(ed, ext->data, ext->datalen);
4289
4290 if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, ext->offset, ed, ext->datalen, NULL, Irp, rollback)) {
4291 ERR("insert_tree_item failed\n");
4292 goto end;
4293 }
4294
4295 if (ext->datalen >= sizeof(EXTENT_DATA) && ed->type == EXTENT_TYPE_PREALLOC)
4296 prealloc = TRUE;
4297
4298 if (ext->datalen >= sizeof(EXTENT_DATA) && ed->type == EXTENT_TYPE_INLINE)
4299 extents_inline = TRUE;
4300
4301 if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES)) {
4302 if (ed->type == EXTENT_TYPE_INLINE)
4303 last_end = ext->offset + ed->decoded_size;
4304 else {
4305 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
4306
4307 last_end = ext->offset + ed2->num_bytes;
4308 }
4309 }
4310
4311 le = le->Flink;
4312 }
4313
4314 if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES) && !extents_inline &&
4315 sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) > last_end) {
4316 Status = insert_sparse_extent(fcb, last_end, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) - last_end, Irp, rollback);
4317 if (!NT_SUCCESS(Status)) {
4318 ERR("insert_sparse_extent returned %08x\n", Status);
4319 goto end;
4320 }
4321 }
4322
4323 // update prealloc flag in INODE_ITEM
4324
4325 if (!prealloc)
4326 fcb->inode_item.flags &= ~BTRFS_INODE_PREALLOC;
4327 else
4328 fcb->inode_item.flags |= BTRFS_INODE_PREALLOC;
4329 }
4330
4331 fcb->extents_changed = FALSE;
4332 }
4333
4334 if (!fcb->created || cache) {
4335 searchkey.obj_id = fcb->inode;
4336 searchkey.obj_type = TYPE_INODE_ITEM;
4337 searchkey.offset = 0xffffffffffffffff;
4338
4339 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
4340 if (!NT_SUCCESS(Status)) {
4341 ERR("error - find_item returned %08x\n", Status);
4342 goto end;
4343 }
4344
4345 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
4346 if (cache) {
4347 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
4348 if (!ii) {
4349 ERR("out of memory\n");
4350 goto end;
4351 }
4352
4353 RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
4354
4355 if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp, rollback)) {
4356 ERR("insert_tree_item failed\n");
4357 goto end;
4358 }
4359
4360 ii_offset = 0;
4361 } else {
4362 ERR("could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
4363 goto end;
4364 }
4365 } else {
4366 #ifdef DEBUG_PARANOID
4367 INODE_ITEM* ii2 = (INODE_ITEM*)tp.item->data;
4368
4369 old_size = ii2->st_size;
4370 #endif
4371
4372 ii_offset = tp.item->key.offset;
4373 }
4374
4375 if (!cache)
4376 delete_tree_item(fcb->Vcb, &tp, rollback);
4377 else {
4378 searchkey.obj_id = fcb->inode;
4379 searchkey.obj_type = TYPE_INODE_ITEM;
4380 searchkey.offset = ii_offset;
4381
4382 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
4383 if (!NT_SUCCESS(Status)) {
4384 ERR("error - find_item returned %08x\n", Status);
4385 goto end;
4386 }
4387
4388 if (keycmp(&tp.item->key, &searchkey)) {
4389 ERR("could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb->inode, fcb->subvol->id);
4390 goto end;
4391 } else
4392 RtlCopyMemory(tp.item->data, &fcb->inode_item, min(tp.item->size, sizeof(INODE_ITEM)));
4393 }
4394 } else
4395 ii_offset = 0;
4396
4397 #ifdef DEBUG_PARANOID
4398 if (!extents_changed && fcb->type != BTRFS_TYPE_DIRECTORY && old_size != fcb->inode_item.st_size) {
4399 ERR("error - size has changed but extents not marked as changed\n");
4400 int3;
4401 }
4402 #endif
4403
4404 fcb->created = FALSE;
4405
4406 if (fcb->deleted) {
4407 traverse_ptr tp2;
4408
4409 // delete XATTR_ITEMs
4410
4411 searchkey.obj_id = fcb->inode;
4412 searchkey.obj_type = TYPE_XATTR_ITEM;
4413 searchkey.offset = 0;
4414
4415 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp);
4416 if (!NT_SUCCESS(Status)) {
4417 ERR("error - find_item returned %08x\n", Status);
4418 goto end;
4419 }
4420
4421 while (find_next_item(fcb->Vcb, &tp, &tp2, FALSE, Irp)) {
4422 tp = tp2;
4423
4424 if (tp.item->key.obj_id == fcb->inode) {
4425 // FIXME - do metadata thing here too?
4426 if (tp.item->key.obj_type == TYPE_XATTR_ITEM) {
4427 delete_tree_item(fcb->Vcb, &tp, rollback);
4428 TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
4429 }
4430 } else
4431 break;
4432 }
4433
4434 goto end;
4435 }
4436
4437 if (!cache) {
4438 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG);
4439 if (!ii) {
4440 ERR("out of memory\n");
4441 goto end;
4442 }
4443
4444 RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM));
4445
4446 if (!insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, ii_offset, ii, sizeof(INODE_ITEM), NULL, Irp, rollback)) {
4447 ERR("insert_tree_item failed\n");
4448 goto end;
4449 }
4450 }
4451
4452 if (fcb->sd_dirty) {
4453 Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_NTACL, EA_NTACL_HASH, (UINT8*)fcb->sd, RtlLengthSecurityDescriptor(fcb->sd), Irp, rollback);
4454 if (!NT_SUCCESS(Status)) {
4455 ERR("set_xattr returned %08x\n", Status);
4456 }
4457
4458 fcb->sd_dirty = FALSE;
4459 }
4460
4461 if (fcb->atts_changed) {
4462 if (!fcb->atts_deleted) {
4463 char val[64];
4464
4465 TRACE("inserting new DOSATTRIB xattr\n");
4466 sprintf(val, "0x%lx", fcb->atts);
4467
4468 Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, (UINT8*)val, strlen(val), Irp, rollback);
4469 if (!NT_SUCCESS(Status)) {
4470 ERR("set_xattr returned %08x\n", Status);
4471 goto end;
4472 }
4473 } else
4474 delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_DOSATTRIB, EA_DOSATTRIB_HASH, Irp, rollback);
4475
4476 fcb->atts_changed = FALSE;
4477 fcb->atts_deleted = FALSE;
4478 }
4479
4480 if (fcb->reparse_xattr_changed) {
4481 if (fcb->reparse_xattr.Buffer && fcb->reparse_xattr.Length > 0) {
4482 Status = set_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, (UINT8*)fcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length, Irp, rollback);
4483 if (!NT_SUCCESS(Status)) {
4484 ERR("set_xattr returned %08x\n", Status);
4485 goto end;
4486 }
4487 } else
4488 delete_xattr(fcb->Vcb, fcb->subvol, fcb->inode, EA_REPARSE, EA_REPARSE_HASH, Irp, rollback);
4489
4490 fcb->reparse_xattr_changed = FALSE;
4491 }
4492
4493 end:
4494 fcb->dirty = FALSE;
4495
4496 // ExReleaseResourceLite(fcb->Header.Resource);
4497 return;
4498 }
4499
4500 static NTSTATUS delete_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, UINT64 parinode, PANSI_STRING utf8, PIRP Irp, LIST_ENTRY* rollback) {
4501 KEY searchkey;
4502 traverse_ptr tp;
4503 NTSTATUS Status;
4504
4505 searchkey.obj_id = parsubvolid;
4506 searchkey.obj_type = TYPE_ROOT_REF;
4507 searchkey.offset = subvolid;
4508
4509 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
4510 if (!NT_SUCCESS(Status)) {
4511 ERR("error - find_item returned %08x\n", Status);
4512 return Status;
4513 }
4514
4515 if (!keycmp(&searchkey, &tp.item->key)) {
4516 if (tp.item->size < sizeof(ROOT_REF)) {
4517 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(ROOT_REF));
4518 return STATUS_INTERNAL_ERROR;
4519 } else {
4520 ROOT_REF* rr;
4521 ULONG len;
4522
4523 rr = (ROOT_REF*)tp.item->data;
4524 len = tp.item->size;
4525
4526 do {
4527 ULONG itemlen;
4528
4529 if (len < sizeof(ROOT_REF) || len < sizeof(ROOT_REF) - 1 + rr->n) {
4530 ERR("(%llx,%x,%llx) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
4531 break;
4532 }
4533
4534 itemlen = sizeof(ROOT_REF) - sizeof(char) + rr->n;
4535
4536 if (rr->dir == parinode && rr->n == utf8->Length && RtlCompareMemory(rr->name, utf8->Buffer, rr->n) == rr->n) {
4537 ULONG newlen = tp.item->size - itemlen;
4538
4539 delete_tree_item(Vcb, &tp, rollback);
4540
4541 if (newlen == 0) {
4542 TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
4543 } else {
4544 UINT8 *newrr = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *rroff;
4545
4546 if (!newrr) {
4547 ERR("out of memory\n");
4548 return STATUS_INSUFFICIENT_RESOURCES;
4549 }
4550
4551 TRACE("modifying (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
4552
4553 if ((UINT8*)rr > tp.item->data) {
4554 RtlCopyMemory(newrr, tp.item->data, (UINT8*)rr - tp.item->data);
4555 rroff = newrr + ((UINT8*)rr - tp.item->data);
4556 } else {
4557 rroff = newrr;
4558 }
4559
4560 if ((UINT8*)&rr->name[rr->n] - tp.item->data < tp.item->size)
4561 RtlCopyMemory(rroff, &rr->name[rr->n], tp.item->size - ((UINT8*)&rr->name[rr->n] - tp.item->data));
4562
4563 insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newrr, newlen, NULL, Irp, rollback);
4564 }
4565
4566 break;
4567 }
4568
4569 if (len > itemlen) {
4570 len -= itemlen;
4571 rr = (ROOT_REF*)&rr->name[rr->n];
4572 } else
4573 break;
4574 } while (len > 0);
4575 }
4576 } else {
4577 WARN("could not find ROOT_REF entry for subvol %llx in %llx\n", searchkey.offset, searchkey.obj_id);
4578 return STATUS_NOT_FOUND;
4579 }
4580
4581 return STATUS_SUCCESS;
4582 }
4583
4584 static NTSTATUS add_root_ref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, ROOT_REF* rr, PIRP Irp, LIST_ENTRY* rollback) {
4585 KEY searchkey;
4586 traverse_ptr tp;
4587 NTSTATUS Status;
4588
4589 searchkey.obj_id = parsubvolid;
4590 searchkey.obj_type = TYPE_ROOT_REF;
4591 searchkey.offset = subvolid;
4592
4593 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
4594 if (!NT_SUCCESS(Status)) {
4595 ERR("error - find_item returned %08x\n", Status);
4596 return Status;
4597 }
4598
4599 if (!keycmp(&searchkey, &tp.item->key)) {
4600 ULONG rrsize = tp.item->size + sizeof(ROOT_REF) - 1 + rr->n;
4601 UINT8* rr2;
4602
4603 rr2 = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG);
4604 if (!rr2) {
4605 ERR("out of memory\n");
4606 return STATUS_INSUFFICIENT_RESOURCES;
4607 }
4608
4609 if (tp.item->size > 0)
4610 RtlCopyMemory(rr2, tp.item->data, tp.item->size);
4611
4612 RtlCopyMemory(rr2 + tp.item->size, rr, sizeof(ROOT_REF) - 1 + rr->n);
4613 ExFreePool(rr);
4614
4615 delete_tree_item(Vcb, &tp, rollback);
4616
4617 if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr2, rrsize, NULL, Irp, rollback)) {
4618 ERR("error - failed to insert item\n");
4619 ExFreePool(rr2);
4620 return STATUS_INTERNAL_ERROR;
4621 }
4622 } else {
4623 if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr, sizeof(ROOT_REF) - 1 + rr->n, NULL, Irp, rollback)) {
4624 ERR("error - failed to insert item\n");
4625 ExFreePool(rr);
4626 return STATUS_INTERNAL_ERROR;
4627 }
4628 }
4629
4630 return STATUS_SUCCESS;
4631 }
4632
4633 static NTSTATUS STDCALL update_root_backref(device_extension* Vcb, UINT64 subvolid, UINT64 parsubvolid, PIRP Irp, LIST_ENTRY* rollback) {
4634 KEY searchkey;
4635 traverse_ptr tp;
4636 UINT8* data;
4637 ULONG datalen;
4638 NTSTATUS Status;
4639
4640 searchkey.obj_id = parsubvolid;
4641 searchkey.obj_type = TYPE_ROOT_REF;
4642 searchkey.offset = subvolid;
4643
4644 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
4645 if (!NT_SUCCESS(Status)) {
4646 ERR("error - find_item returned %08x\n", Status);
4647 return Status;
4648 }
4649
4650 if (!keycmp(&tp.item->key, &searchkey) && tp.item->size > 0) {
4651 datalen = tp.item->size;
4652
4653 data = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG);
4654 if (!data) {
4655 ERR("out of memory\n");
4656 return STATUS_INSUFFICIENT_RESOURCES;
4657 }
4658
4659 RtlCopyMemory(data, tp.item->data, datalen);
4660 } else {
4661 datalen = 0;
4662 }
4663
4664 searchkey.obj_id = subvolid;
4665 searchkey.obj_type = TYPE_ROOT_BACKREF;
4666 searchkey.offset = parsubvolid;
4667
4668 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
4669 if (!NT_SUCCESS(Status)) {
4670 ERR("error - find_item returned %08x\n", Status);
4671
4672 if (datalen > 0)
4673 ExFreePool(data);
4674
4675 return Status;
4676 }
4677
4678 if (!keycmp(&tp.item->key, &searchkey))
4679 delete_tree_item(Vcb, &tp, rollback);
4680
4681 if (datalen > 0) {
4682 if (!insert_tree_item(Vcb, Vcb->root_root, subvolid, TYPE_ROOT_BACKREF, parsubvolid, data, datalen, NULL, Irp, rollback)) {
4683 ERR("error - failed to insert item\n");
4684 ExFreePool(data);
4685 return STATUS_INTERNAL_ERROR;
4686 }
4687 }
4688
4689 return STATUS_SUCCESS;
4690 }
4691
4692 static NTSTATUS flush_fileref(file_ref* fileref, PIRP Irp, LIST_ENTRY* rollback) {
4693 NTSTATUS Status;
4694
4695 // if fileref created and then immediately deleted, do nothing
4696 if (fileref->created && fileref->deleted) {
4697 fileref->dirty = FALSE;
4698 return STATUS_SUCCESS;
4699 }
4700
4701 if (fileref->fcb->ads) {
4702 fileref->dirty = FALSE;
4703 return STATUS_SUCCESS;
4704 }
4705
4706 if (fileref->created) {
4707 ULONG disize;
4708 DIR_ITEM *di, *di2;
4709 UINT32 crc32;
4710
4711 crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, fileref->utf8.Length);
4712
4713 disize = sizeof(DIR_ITEM) - 1 + fileref->utf8.Length;
4714 di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
4715 if (!di) {
4716 ERR("out of memory\n");
4717 return STATUS_INSUFFICIENT_RESOURCES;
4718 }
4719
4720 if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
4721 di->key.obj_id = fileref->fcb->inode;
4722 di->key.obj_type = TYPE_INODE_ITEM;
4723 di->key.offset = 0;
4724 } else { // subvolume
4725 di->key.obj_id = fileref->fcb->subvol->id;
4726 di->key.obj_type = TYPE_ROOT_ITEM;
4727 di->key.offset = 0xffffffffffffffff;
4728 }
4729
4730 di->transid = fileref->fcb->Vcb->superblock.generation;
4731 di->m = 0;
4732 di->n = (UINT16)fileref->utf8.Length;
4733 di->type = fileref->fcb->type;
4734 RtlCopyMemory(di->name, fileref->utf8.Buffer, fileref->utf8.Length);
4735
4736 di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
4737 if (!di2) {
4738 ERR("out of memory\n");
4739 return STATUS_INSUFFICIENT_RESOURCES;
4740 }
4741
4742 RtlCopyMemory(di2, di, disize);
4743
4744 if (!insert_tree_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, fileref->index, di, disize, NULL, Irp, rollback)) {
4745 ERR("insert_tree_item failed\n");
4746 Status = STATUS_INTERNAL_ERROR;
4747 return Status;
4748 }
4749
4750 Status = add_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, di2, disize, Irp, rollback);
4751 if (!NT_SUCCESS(Status)) {
4752 ERR("add_dir_item returned %08x\n", Status);
4753 return Status;
4754 }
4755
4756 if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
4757 Status = add_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, fileref->index, &fileref->utf8, Irp, rollback);
4758 if (!NT_SUCCESS(Status)) {
4759 ERR("add_inode_ref returned %08x\n", Status);
4760 return Status;
4761 }
4762 } else {
4763 ULONG rrlen;
4764 ROOT_REF* rr;
4765
4766 rrlen = sizeof(ROOT_REF) - 1 + fileref->utf8.Length;
4767
4768 rr = ExAllocatePoolWithTag(PagedPool, rrlen, ALLOC_TAG);
4769 if (!rr) {
4770 ERR("out of memory\n");
4771 return STATUS_INSUFFICIENT_RESOURCES;
4772 }
4773
4774 rr->dir = fileref->parent->fcb->inode;
4775 rr->index = fileref->index;
4776 rr->n = fileref->utf8.Length;
4777 RtlCopyMemory(rr->name, fileref->utf8.Buffer, fileref->utf8.Length);
4778
4779 Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, Irp, rollback);
4780 if (!NT_SUCCESS(Status)) {
4781 ERR("add_root_ref returned %08x\n", Status);
4782 return Status;
4783 }
4784
4785 Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp, rollback);
4786 if (!NT_SUCCESS(Status)) {
4787 ERR("update_root_backref returned %08x\n", Status);
4788 return Status;
4789 }
4790 }
4791
4792 fileref->created = FALSE;
4793 } else if (fileref->deleted) {
4794 UINT32 crc32;
4795 KEY searchkey;
4796 traverse_ptr tp;
4797 ANSI_STRING* name;
4798
4799 if (fileref->oldutf8.Buffer)
4800 name = &fileref->oldutf8;
4801 else
4802 name = &fileref->utf8;
4803
4804 crc32 = calc_crc32c(0xfffffffe, (UINT8*)name->Buffer, name->Length);
4805
4806 TRACE("deleting %.*S\n", file_desc_fileref(fileref));
4807
4808 // delete DIR_ITEM (0x54)
4809
4810 Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, name, Irp, rollback);
4811 if (!NT_SUCCESS(Status)) {
4812 ERR("delete_dir_item returned %08x\n", Status);
4813 return Status;
4814 }
4815
4816 if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
4817 // delete INODE_REF (0xc)
4818
4819 Status = delete_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, name, Irp, rollback);
4820 if (!NT_SUCCESS(Status)) {
4821 ERR("delete_inode_ref returned %08x\n", Status);
4822 return Status;
4823 }
4824 } else { // subvolume
4825 Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, name, Irp, rollback);
4826 if (!NT_SUCCESS(Status)) {
4827 ERR("delete_root_ref returned %08x\n", Status);
4828 }
4829
4830 Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp, rollback);
4831 if (!NT_SUCCESS(Status)) {
4832 ERR("update_root_backref returned %08x\n", Status);
4833 return Status;
4834 }
4835 }
4836
4837 // delete DIR_INDEX (0x60)
4838
4839 searchkey.obj_id = fileref->parent->fcb->inode;
4840 searchkey.obj_type = TYPE_DIR_INDEX;
4841 searchkey.offset = fileref->index;
4842
4843 Status = find_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE, Irp);
4844 if (!NT_SUCCESS(Status)) {
4845 ERR("error - find_item returned %08x\n", Status);
4846 Status = STATUS_INTERNAL_ERROR;
4847 return Status;
4848 }
4849
4850 if (!keycmp(&searchkey, &tp.item->key)) {
4851 delete_tree_item(fileref->fcb->Vcb, &tp, rollback);
4852 TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
4853 }
4854
4855 if (fileref->oldutf8.Buffer) {
4856 ExFreePool(fileref->oldutf8.Buffer);
4857 fileref->oldutf8.Buffer = NULL;
4858 }
4859 } else { // rename or change type
4860 PANSI_STRING oldutf8 = fileref->oldutf8.Buffer ? &fileref->oldutf8 : &fileref->utf8;
4861 UINT32 crc32, oldcrc32;
4862 ULONG disize;
4863 DIR_ITEM *di, *di2;
4864 KEY searchkey;
4865 traverse_ptr tp;
4866
4867 crc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->utf8.Buffer, fileref->utf8.Length);
4868
4869 if (!fileref->oldutf8.Buffer)
4870 oldcrc32 = crc32;
4871 else
4872 oldcrc32 = calc_crc32c(0xfffffffe, (UINT8*)fileref->oldutf8.Buffer, fileref->oldutf8.Length);
4873
4874 // delete DIR_ITEM (0x54)
4875
4876 Status = delete_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, oldcrc32, oldutf8, Irp, rollback);
4877 if (!NT_SUCCESS(Status)) {
4878 ERR("delete_dir_item returned %08x\n", Status);
4879 return Status;
4880 }
4881
4882 // add DIR_ITEM (0x54)
4883
4884 disize = sizeof(DIR_ITEM) - 1 + fileref->utf8.Length;
4885 di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
4886 if (!di) {
4887 ERR("out of memory\n");
4888 return STATUS_INSUFFICIENT_RESOURCES;
4889 }
4890
4891 di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG);
4892 if (!di2) {
4893 ERR("out of memory\n");
4894 ExFreePool(di);
4895 return STATUS_INSUFFICIENT_RESOURCES;
4896 }
4897
4898 if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
4899 di->key.obj_id = fileref->fcb->inode;
4900 di->key.obj_type = TYPE_INODE_ITEM;
4901 di->key.offset = 0;
4902 } else { // subvolume
4903 di->key.obj_id = fileref->fcb->subvol->id;
4904 di->key.obj_type = TYPE_ROOT_ITEM;
4905 di->key.offset = 0xffffffffffffffff;
4906 }
4907
4908 di->transid = fileref->fcb->Vcb->superblock.generation;
4909 di->m = 0;
4910 di->n = (UINT16)fileref->utf8.Length;
4911 di->type = fileref->fcb->type;
4912 RtlCopyMemory(di->name, fileref->utf8.Buffer, fileref->utf8.Length);
4913
4914 RtlCopyMemory(di2, di, disize);
4915
4916 Status = add_dir_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, crc32, di, disize, Irp, rollback);
4917 if (!NT_SUCCESS(Status)) {
4918 ERR("add_dir_item returned %08x\n", Status);
4919 return Status;
4920 }
4921
4922 if (fileref->parent->fcb->subvol == fileref->fcb->subvol) {
4923 // delete INODE_REF (0xc)
4924
4925 Status = delete_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, oldutf8, Irp, rollback);
4926 if (!NT_SUCCESS(Status)) {
4927 ERR("delete_inode_ref returned %08x\n", Status);
4928 return Status;
4929 }
4930
4931 // add INODE_REF (0xc)
4932
4933 Status = add_inode_ref(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, fileref->parent->fcb->inode, fileref->index, &fileref->utf8, Irp, rollback);
4934 if (!NT_SUCCESS(Status)) {
4935 ERR("add_inode_ref returned %08x\n", Status);
4936 return Status;
4937 }
4938 } else { // subvolume
4939 ULONG rrlen;
4940 ROOT_REF* rr;
4941
4942 // FIXME - make sure this works with duff subvols within snapshots
4943
4944 Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, oldutf8, Irp, rollback);
4945 if (!NT_SUCCESS(Status)) {
4946 ERR("delete_root_ref returned %08x\n", Status);
4947 }
4948
4949 rrlen = sizeof(ROOT_REF) - 1 + fileref->utf8.Length;
4950
4951 rr = ExAllocatePoolWithTag(PagedPool, rrlen, ALLOC_TAG);
4952 if (!rr) {
4953 ERR("out of memory\n");
4954 return STATUS_INSUFFICIENT_RESOURCES;
4955 }
4956
4957 rr->dir = fileref->parent->fcb->inode;
4958 rr->index = fileref->index;
4959 rr->n = fileref->utf8.Length;
4960 RtlCopyMemory(rr->name, fileref->utf8.Buffer, fileref->utf8.Length);
4961
4962 Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, Irp, rollback);
4963 if (!NT_SUCCESS(Status)) {
4964 ERR("add_root_ref returned %08x\n", Status);
4965 return Status;
4966 }
4967
4968 Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp, rollback);
4969 if (!NT_SUCCESS(Status)) {
4970 ERR("update_root_backref returned %08x\n", Status);
4971 return Status;
4972 }
4973 }
4974
4975 // delete DIR_INDEX (0x60)
4976
4977 searchkey.obj_id = fileref->parent->fcb->inode;
4978 searchkey.obj_type = TYPE_DIR_INDEX;
4979 searchkey.offset = fileref->index;
4980
4981 Status = find_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, &tp, &searchkey, FALSE, Irp);
4982 if (!NT_SUCCESS(Status)) {
4983 ERR("error - find_item returned %08x\n", Status);
4984 Status = STATUS_INTERNAL_ERROR;
4985 return Status;
4986 }
4987
4988 if (!keycmp(&searchkey, &tp.item->key)) {
4989 delete_tree_item(fileref->fcb->Vcb, &tp, rollback);
4990 TRACE("deleting (%llx,%x,%llx)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
4991 } else
4992 WARN("could not find (%llx,%x,%llx) in subvol %llx\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset, fileref->fcb->subvol->id);
4993
4994 // add DIR_INDEX (0x60)
4995
4996 if (!insert_tree_item(fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, fileref->index, di2, disize, NULL, Irp, rollback)) {
4997 ERR("insert_tree_item failed\n");
4998 Status = STATUS_INTERNAL_ERROR;
4999 return Status;
5000 }
5001
5002 if (fileref->oldutf8.Buffer) {
5003 ExFreePool(fileref->oldutf8.Buffer);
5004 fileref->oldutf8.Buffer = NULL;
5005 }
5006 }
5007
5008 fileref->dirty = FALSE;
5009
5010 return STATUS_SUCCESS;
5011 }
5012
5013 static void convert_shared_data_refs(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
5014 LIST_ENTRY* le;
5015 NTSTATUS Status;
5016
5017 le = Vcb->trees.Flink;
5018 while (le != &Vcb->trees) {
5019 tree* t = CONTAINING_RECORD(le, tree, list_entry);
5020
5021 if (t->write && t->header.level == 0 &&
5022 (t->header.flags & HEADER_FLAG_SHARED_BACKREF || !(t->header.flags & HEADER_FLAG_MIXED_BACKREF))) {
5023 LIST_ENTRY* le2;
5024 BOOL old = !(t->header.flags & HEADER_FLAG_MIXED_BACKREF);
5025
5026 le2 = Vcb->shared_extents.Flink;
5027 while (le2 != &Vcb->shared_extents) {
5028 shared_data* sd = CONTAINING_RECORD(le2, shared_data, list_entry);
5029
5030 if (sd->address == t->header.address) {
5031 LIST_ENTRY* le3 = sd->entries.Flink;
5032 while (le3 != &sd->entries) {
5033 shared_data_entry* sde = CONTAINING_RECORD(le3, shared_data_entry, list_entry);
5034
5035 TRACE("tree %llx; root %llx, objid %llx, offset %llx, count %x\n",
5036 t->header.address, sde->edr.root, sde->edr.objid, sde->edr.offset, sde->edr.count);
5037
5038 Status = increase_extent_refcount_data(Vcb, sde->address, sde->size, sde->edr.root, sde->edr.objid, sde->edr.offset, sde->edr.count, Irp, rollback);
5039
5040 if (!NT_SUCCESS(Status))
5041 WARN("increase_extent_refcount_data returned %08x\n", Status);
5042
5043 if (old) {
5044 Status = decrease_extent_refcount_old(Vcb, sde->address, sde->size, sd->address, Irp, rollback);
5045
5046 if (!NT_SUCCESS(Status))
5047 WARN("decrease_extent_refcount_old returned %08x\n", Status);
5048 } else {
5049 Status = decrease_extent_refcount_shared_data(Vcb, sde->address, sde->size, sd->address, sd->parent, Irp, rollback);
5050
5051 if (!NT_SUCCESS(Status))
5052 WARN("decrease_extent_refcount_shared_data returned %08x\n", Status);
5053 }
5054
5055 le3 = le3->Flink;
5056 }
5057 break;
5058 }
5059
5060 le2 = le2->Flink;
5061 }
5062
5063 t->header.flags &= ~HEADER_FLAG_SHARED_BACKREF;
5064 t->header.flags |= HEADER_FLAG_MIXED_BACKREF;
5065 }
5066
5067 le = le->Flink;
5068 }
5069 }
5070
5071 static NTSTATUS add_root_item_to_cache(device_extension* Vcb, UINT64 root, PIRP Irp, LIST_ENTRY* rollback) {
5072 KEY searchkey;
5073 traverse_ptr tp;
5074 NTSTATUS Status;
5075
5076 searchkey.obj_id = root;
5077 searchkey.obj_type = TYPE_ROOT_ITEM;
5078 searchkey.offset = 0xffffffffffffffff;
5079
5080 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
5081 if (!NT_SUCCESS(Status)) {
5082 ERR("error - find_item returned %08x\n", Status);
5083 return Status;
5084 }
5085
5086 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
5087 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey.obj_id);
5088 int3;
5089 return STATUS_INTERNAL_ERROR;
5090 }
5091
5092 if (tp.item->size < sizeof(ROOT_ITEM)) { // if not full length, create new entry with new bits zeroed
5093 ROOT_ITEM* ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG);
5094 if (!ri) {
5095 ERR("out of memory\n");
5096 return STATUS_INSUFFICIENT_RESOURCES;
5097 }
5098
5099 if (tp.item->size > 0)
5100 RtlCopyMemory(ri, tp.item->data, tp.item->size);
5101
5102 RtlZeroMemory(((UINT8*)ri) + tp.item->size, sizeof(ROOT_ITEM) - tp.item->size);
5103
5104 delete_tree_item(Vcb, &tp, rollback);
5105
5106 if (!insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, Irp, rollback)) {
5107 ERR("insert_tree_item failed\n");
5108 return STATUS_INTERNAL_ERROR;
5109 }
5110 } else {
5111 tp.tree->write = TRUE;
5112 }
5113
5114 return STATUS_SUCCESS;
5115 }
5116
5117 static NTSTATUS add_root_items_to_cache(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
5118 LIST_ENTRY* le;
5119 NTSTATUS Status;
5120
5121 le = Vcb->trees.Flink;
5122 while (le != &Vcb->trees) {
5123 tree* t = CONTAINING_RECORD(le, tree, list_entry);
5124
5125 if (t->write && t->root != Vcb->chunk_root && t->root != Vcb->root_root) {
5126 Status = add_root_item_to_cache(Vcb, t->root->id, Irp, rollback);
5127 if (!NT_SUCCESS(Status)) {
5128 ERR("add_root_item_to_cache returned %08x\n", Status);
5129 return Status;
5130 }
5131 }
5132
5133 le = le->Flink;
5134 }
5135
5136 // make sure we always update the extent tree
5137 Status = add_root_item_to_cache(Vcb, BTRFS_ROOT_EXTENT, Irp, rollback);
5138 if (!NT_SUCCESS(Status)) {
5139 ERR("add_root_item_to_cache returned %08x\n", Status);
5140 return Status;
5141 }
5142
5143 return STATUS_SUCCESS;
5144 }
5145
5146 NTSTATUS STDCALL do_write(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
5147 NTSTATUS Status;
5148 LIST_ENTRY* le;
5149 BOOL cache_changed = FALSE;
5150
5151 #ifdef DEBUG_WRITE_LOOPS
5152 UINT loops = 0;
5153 #endif
5154
5155 TRACE("(%p)\n", Vcb);
5156
5157 while (!IsListEmpty(&Vcb->dirty_filerefs)) {
5158 dirty_fileref* dirt;
5159
5160 le = RemoveHeadList(&Vcb->dirty_filerefs);
5161
5162 dirt = CONTAINING_RECORD(le, dirty_fileref, list_entry);
5163
5164 flush_fileref(dirt->fileref, Irp, rollback);
5165 free_fileref(dirt->fileref);
5166 ExFreePool(dirt);
5167 }
5168
5169 // We process deleted streams first, so we don't run over our xattr
5170 // limit unless we absolutely have to.
5171
5172 le = Vcb->dirty_fcbs.Flink;
5173 while (le != &Vcb->dirty_fcbs) {
5174 dirty_fcb* dirt;
5175 LIST_ENTRY* le2 = le->Flink;
5176
5177 dirt = CONTAINING_RECORD(le, dirty_fcb, list_entry);
5178
5179 if (dirt->fcb->deleted && dirt->fcb->ads) {
5180 RemoveEntryList(le);
5181
5182 flush_fcb(dirt->fcb, FALSE, Irp, rollback);
5183 free_fcb(dirt->fcb);
5184 ExFreePool(dirt);
5185 }
5186
5187 le = le2;
5188 }
5189
5190 le = Vcb->dirty_fcbs.Flink;
5191 while (le != &Vcb->dirty_fcbs) {
5192 dirty_fcb* dirt;
5193 LIST_ENTRY* le2 = le->Flink;
5194
5195 dirt = CONTAINING_RECORD(le, dirty_fcb, list_entry);
5196
5197 if (dirt->fcb->subvol != Vcb->root_root || dirt->fcb->deleted) {
5198 RemoveEntryList(le);
5199
5200 flush_fcb(dirt->fcb, FALSE, Irp, rollback);
5201 free_fcb(dirt->fcb);
5202 ExFreePool(dirt);
5203 }
5204
5205 le = le2;
5206 }
5207
5208 convert_shared_data_refs(Vcb, Irp, rollback);
5209
5210 ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE);
5211 if (!IsListEmpty(&Vcb->sector_checksums)) {
5212 update_checksum_tree(Vcb, Irp, rollback);
5213 }
5214 ExReleaseResourceLite(&Vcb->checksum_lock);
5215
5216 if (!IsListEmpty(&Vcb->drop_roots)) {
5217 Status = drop_roots(Vcb, Irp, rollback);
5218
5219 if (!NT_SUCCESS(Status)) {
5220 ERR("drop_roots returned %08x\n", Status);
5221 return Status;
5222 }
5223 }
5224
5225 if (!IsListEmpty(&Vcb->chunks_changed)) {
5226 Status = update_chunks(Vcb, Irp, rollback);
5227
5228 if (!NT_SUCCESS(Status)) {
5229 ERR("update_chunks returned %08x\n", Status);
5230 return Status;
5231 }
5232 }
5233
5234 // If only changing superblock, e.g. changing label, we still need to rewrite
5235 // the root tree so the generations match, otherwise you won't be able to mount on Linux.
5236 if (!Vcb->root_root->treeholder.tree || !Vcb->root_root->treeholder.tree->write) {
5237 KEY searchkey;
5238
5239 traverse_ptr tp;
5240
5241 searchkey.obj_id = 0;
5242 searchkey.obj_type = 0;
5243 searchkey.offset = 0;
5244
5245 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp);
5246 if (!NT_SUCCESS(Status)) {
5247 ERR("error - find_item returned %08x\n", Status);
5248 return Status;
5249 }
5250
5251 Vcb->root_root->treeholder.tree->write = TRUE;
5252 }
5253
5254 Status = add_root_items_to_cache(Vcb, Irp, rollback);
5255 if (!NT_SUCCESS(Status)) {
5256 ERR("add_root_items_to_cache returned %08x\n", Status);
5257 return Status;
5258 }
5259
5260 do {
5261 Status = add_parents(Vcb, rollback);
5262 if (!NT_SUCCESS(Status)) {
5263 ERR("add_parents returned %08x\n", Status);
5264 goto end;
5265 }
5266
5267 Status = do_splits(Vcb, Irp, rollback);
5268 if (!NT_SUCCESS(Status)) {
5269 ERR("do_splits returned %08x\n", Status);
5270 goto end;
5271 }
5272
5273 Status = allocate_tree_extents(Vcb, Irp, rollback);
5274 if (!NT_SUCCESS(Status)) {
5275 ERR("add_parents returned %08x\n", Status);
5276 goto end;
5277 }
5278
5279 Status = update_chunk_usage(Vcb, Irp, rollback);
5280 if (!NT_SUCCESS(Status)) {
5281 ERR("update_chunk_usage returned %08x\n", Status);
5282 goto end;
5283 }
5284
5285 Status = allocate_cache(Vcb, &cache_changed, Irp, rollback);
5286 if (!NT_SUCCESS(Status)) {
5287 ERR("allocate_cache returned %08x\n", Status);
5288 goto end;
5289 }
5290
5291 #ifdef DEBUG_WRITE_LOOPS
5292 loops++;
5293
5294 if (cache_changed)
5295 ERR("cache has changed, looping again\n");
5296 #endif
5297 } while (cache_changed || !trees_consistent(Vcb, rollback));
5298
5299 #ifdef DEBUG_WRITE_LOOPS
5300 ERR("%u loops\n", loops);
5301 #endif
5302
5303 TRACE("trees consistent\n");
5304
5305 Status = update_root_root(Vcb, Irp, rollback);
5306 if (!NT_SUCCESS(Status)) {
5307 ERR("update_root_root returned %08x\n", Status);
5308 goto end;
5309 }
5310
5311 Status = write_trees(Vcb, Irp);
5312 if (!NT_SUCCESS(Status)) {
5313 ERR("write_trees returned %08x\n", Status);
5314 goto end;
5315 }
5316
5317 Vcb->superblock.cache_generation = Vcb->superblock.generation;
5318
5319 Status = write_superblocks(Vcb, Irp);
5320 if (!NT_SUCCESS(Status)) {
5321 ERR("write_superblocks returned %08x\n", Status);
5322 goto end;
5323 }
5324
5325 clean_space_cache(Vcb);
5326
5327 Vcb->superblock.generation++;
5328
5329 Status = STATUS_SUCCESS;
5330
5331 le = Vcb->trees.Flink;
5332 while (le != &Vcb->trees) {
5333 tree* t = CONTAINING_RECORD(le, tree, list_entry);
5334
5335 #ifdef DEBUG_PARANOID
5336 KEY searchkey;
5337 traverse_ptr tp;
5338
5339 searchkey.obj_id = t->header.address;
5340 searchkey.obj_type = TYPE_METADATA_ITEM;
5341 searchkey.offset = 0xffffffffffffffff;
5342
5343 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
5344 if (!NT_SUCCESS(Status)) {
5345 ERR("error - find_item returned %08x\n", Status);
5346 int3;
5347 }
5348
5349 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
5350 searchkey.obj_id = t->header.address;
5351 searchkey.obj_type = TYPE_EXTENT_ITEM;
5352 searchkey.offset = 0xffffffffffffffff;
5353
5354 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
5355 if (!NT_SUCCESS(Status)) {
5356 ERR("error - find_item returned %08x\n", Status);
5357 int3;
5358 }
5359
5360 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
5361 ERR("error - could not find entry in extent tree for tree at %llx\n", t->header.address);
5362 int3;
5363 }
5364 }
5365 #endif
5366
5367 t->write = FALSE;
5368
5369 le = le->Flink;
5370 }
5371
5372 Vcb->need_write = FALSE;
5373
5374 while (!IsListEmpty(&Vcb->drop_roots)) {
5375 LIST_ENTRY* le = RemoveHeadList(&Vcb->drop_roots);
5376 root* r = CONTAINING_RECORD(le, root, list_entry);
5377
5378 ExDeleteResourceLite(&r->nonpaged->load_tree_lock);
5379 ExFreePool(r->nonpaged);
5380 ExFreePool(r);
5381 }
5382
5383 end:
5384 TRACE("do_write returning %08x\n", Status);
5385
5386 return Status;
5387 }
5388
5389 static __inline BOOL entry_in_ordered_list(LIST_ENTRY* list, UINT64 value) {
5390 LIST_ENTRY* le = list->Flink;
5391 ordered_list* ol;
5392
5393 while (le != list) {
5394 ol = (ordered_list*)le;
5395
5396 if (ol->key > value)
5397 return FALSE;
5398 else if (ol->key == value)
5399 return TRUE;
5400
5401 le = le->Flink;
5402 }
5403
5404 return FALSE;
5405 }
5406
5407 static changed_extent* get_changed_extent_item(chunk* c, UINT64 address, UINT64 size, BOOL no_csum) {
5408 LIST_ENTRY* le;
5409 changed_extent* ce;
5410
5411 le = c->changed_extents.Flink;
5412 while (le != &c->changed_extents) {
5413 ce = CONTAINING_RECORD(le, changed_extent, list_entry);
5414
5415 if (ce->address == address && ce->size == size)
5416 return ce;
5417
5418 le = le->Flink;
5419 }
5420
5421 ce = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent), ALLOC_TAG);
5422 if (!ce) {
5423 ERR("out of memory\n");
5424 return NULL;
5425 }
5426
5427 ce->address = address;
5428 ce->size = size;
5429 ce->old_size = size;
5430 ce->count = 0;
5431 ce->old_count = 0;
5432 ce->no_csum = no_csum;
5433 InitializeListHead(&ce->refs);
5434 InitializeListHead(&ce->old_refs);
5435
5436 InsertTailList(&c->changed_extents, &ce->list_entry);
5437
5438 return ce;
5439 }
5440
5441 NTSTATUS update_changed_extent_ref(device_extension* Vcb, chunk* c, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, signed long long count,
5442 BOOL no_csum, UINT64 new_size, PIRP Irp) {
5443 LIST_ENTRY* le;
5444 changed_extent* ce;
5445 changed_extent_ref* cer;
5446 NTSTATUS Status;
5447 KEY searchkey;
5448 traverse_ptr tp;
5449 UINT64 old_count;
5450
5451 ExAcquireResourceExclusiveLite(&c->changed_extents_lock, TRUE);
5452
5453 ce = get_changed_extent_item(c, address, size, no_csum);
5454
5455 if (!ce) {
5456 ERR("get_changed_extent_item failed\n");
5457 Status = STATUS_INTERNAL_ERROR;
5458 goto end;
5459 }
5460
5461 if (IsListEmpty(&ce->refs) && IsListEmpty(&ce->old_refs)) { // new entry
5462 searchkey.obj_id = address;
5463 searchkey.obj_type = TYPE_EXTENT_ITEM;
5464 searchkey.offset = 0xffffffffffffffff;
5465
5466 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE, Irp);
5467 if (!NT_SUCCESS(Status)) {
5468 ERR("error - find_item returned %08x\n", Status);
5469 goto end;
5470 }
5471
5472 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) {
5473 ERR("could not find address %llx in extent tree\n", address);
5474 Status = STATUS_INTERNAL_ERROR;
5475 goto end;
5476 }
5477
5478 if (tp.item->key.offset != size) {
5479 ERR("extent %llx had size %llx, not %llx as expected\n", address, tp.item->key.offset, size);
5480 Status = STATUS_INTERNAL_ERROR;
5481 goto end;
5482 }
5483
5484 if (tp.item->size == sizeof(EXTENT_ITEM_V0)) {
5485 EXTENT_ITEM_V0* eiv0 = (EXTENT_ITEM_V0*)tp.item->data;
5486
5487 ce->count = ce->old_count = eiv0->refcount;
5488 } else if (tp.item->size >= sizeof(EXTENT_ITEM)) {
5489 EXTENT_ITEM* ei = (EXTENT_ITEM*)tp.item->data;
5490
5491 ce->count = ce->old_count = ei->refcount;
5492 } else {
5493 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));
5494 Status = STATUS_INTERNAL_ERROR;
5495 goto end;
5496 }
5497 }
5498
5499 ce->size = new_size;
5500
5501 le = ce->refs.Flink;
5502 while (le != &ce->refs) {
5503 cer = CONTAINING_RECORD(le, changed_extent_ref, list_entry);
5504
5505 if (cer->edr.root == root && cer->edr.objid == objid && cer->edr.offset == offset) {
5506 ce->count += count;
5507 cer->edr.count += count;
5508 Status = STATUS_SUCCESS;
5509 goto end;
5510 }
5511
5512 le = le->Flink;
5513 }
5514
5515 old_count = find_extent_data_refcount(Vcb, address, size, root, objid, offset, Irp);
5516
5517 if (old_count > 0) {
5518 cer = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent_ref), ALLOC_TAG);
5519
5520 if (!cer) {
5521 ERR("out of memory\n");
5522 Status = STATUS_INSUFFICIENT_RESOURCES;
5523 goto end;
5524 }
5525
5526 cer->edr.root = root;
5527 cer->edr.objid = objid;
5528 cer->edr.offset = offset;
5529 cer->edr.count = old_count;
5530
5531 InsertTailList(&ce->old_refs, &cer->list_entry);
5532 }
5533
5534 cer = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent_ref), ALLOC_TAG);
5535
5536 if (!cer) {
5537 ERR("out of memory\n");
5538 Status = STATUS_INSUFFICIENT_RESOURCES;
5539 goto end;
5540 }
5541
5542 cer->edr.root = root;
5543 cer->edr.objid = objid;
5544 cer->edr.offset = offset;
5545 cer->edr.count = old_count + count;
5546
5547 InsertTailList(&ce->refs, &cer->list_entry);
5548
5549 ce->count += count;
5550
5551 Status = STATUS_SUCCESS;
5552
5553 end:
5554 ExReleaseResourceLite(&c->changed_extents_lock);
5555
5556 return Status;
5557 }
5558
5559 NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, PIRP Irp, LIST_ENTRY* rollback) {
5560 NTSTATUS Status;
5561 LIST_ENTRY* le;
5562
5563 le = fcb->extents.Flink;
5564
5565 while (le != &fcb->extents) {
5566 LIST_ENTRY* le2 = le->Flink;
5567 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
5568 EXTENT_DATA* ed = ext->data;
5569 EXTENT_DATA2* ed2;
5570 UINT64 len;
5571
5572 if (!ext->ignore) {
5573 if (ext->datalen < sizeof(EXTENT_DATA)) {
5574 ERR("extent at %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA));
5575 Status = STATUS_INTERNAL_ERROR;
5576 goto end;
5577 }
5578
5579 if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
5580 if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
5581 ERR("extent at %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
5582 Status = STATUS_INTERNAL_ERROR;
5583 goto end;
5584 }
5585
5586 ed2 = (EXTENT_DATA2*)ed->data;
5587 }
5588
5589 len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
5590
5591 if (ext->offset < end_data && ext->offset + len > start_data) {
5592 if (ed->type == EXTENT_TYPE_INLINE) {
5593 if (start_data <= ext->offset && end_data >= ext->offset + len) { // remove all
5594 remove_fcb_extent(fcb, ext, rollback);
5595
5596 fcb->inode_item.st_blocks -= len;
5597 } else if (start_data <= ext->offset && end_data < ext->offset + len) { // remove beginning
5598 EXTENT_DATA* ned;
5599 UINT64 size;
5600 extent* newext;
5601
5602 size = len - (end_data - ext->offset);
5603
5604 ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
5605 if (!ned) {
5606 ERR("out of memory\n");
5607 Status = STATUS_INSUFFICIENT_RESOURCES;
5608 goto end;
5609 }
5610
5611 newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
5612 if (!newext) {
5613 ERR("out of memory\n");
5614 Status = STATUS_INSUFFICIENT_RESOURCES;
5615 ExFreePool(ned);
5616 goto end;
5617 }
5618
5619 ned->generation = Vcb->superblock.generation;
5620 ned->decoded_size = size;
5621 ned->compression = ed->compression;
5622 ned->encryption = ed->encryption;
5623 ned->encoding = ed->encoding;
5624 ned->type = ed->type;
5625
5626 RtlCopyMemory(&ned->data[0], &ed->data[end_data - ext->offset], size);
5627
5628 newext->offset = end_data;
5629 newext->data = ned;
5630 newext->datalen = sizeof(EXTENT_DATA) - 1 + size;
5631 newext->unique = ext->unique;
5632 newext->ignore = FALSE;
5633 InsertHeadList(&ext->list_entry, &newext->list_entry);
5634
5635 remove_fcb_extent(fcb, ext, rollback);
5636
5637 fcb->inode_item.st_blocks -= end_data - ext->offset;
5638 } else if (start_data > ext->offset && end_data >= ext->offset + len) { // remove end
5639 EXTENT_DATA* ned;
5640 UINT64 size;
5641 extent* newext;
5642
5643 size = start_data - ext->offset;
5644
5645 ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
5646 if (!ned) {
5647 ERR("out of memory\n");
5648 Status = STATUS_INSUFFICIENT_RESOURCES;
5649 goto end;
5650 }
5651
5652 newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
5653 if (!newext) {
5654 ERR("out of memory\n");
5655 Status = STATUS_INSUFFICIENT_RESOURCES;
5656 ExFreePool(ned);
5657 goto end;
5658 }
5659
5660 ned->generation = Vcb->superblock.generation;
5661 ned->decoded_size = size;
5662 ned->compression = ed->compression;
5663 ned->encryption = ed->encryption;
5664 ned->encoding = ed->encoding;
5665 ned->type = ed->type;
5666
5667 RtlCopyMemory(&ned->data[0], &ed->data[0], size);
5668
5669 newext->offset = ext->offset;
5670 newext->data = ned;
5671 newext->datalen = sizeof(EXTENT_DATA) - 1 + size;
5672 newext->unique = ext->unique;
5673 newext->ignore = FALSE;
5674 InsertHeadList(&ext->list_entry, &newext->list_entry);
5675
5676 remove_fcb_extent(fcb, ext, rollback);
5677
5678 fcb->inode_item.st_blocks -= ext->offset + len - start_data;
5679 } else if (start_data > ext->offset && end_data < ext->offset + len) { // remove middle
5680 EXTENT_DATA *ned1, *ned2;
5681 UINT64 size;
5682 extent *newext1, *newext2;
5683
5684 size = start_data - ext->offset;
5685
5686 ned1 = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
5687 if (!ned1) {
5688 ERR("out of memory\n");
5689 Status = STATUS_INSUFFICIENT_RESOURCES;
5690 goto end;
5691 }
5692
5693 newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
5694 if (!newext1) {
5695 ERR("out of memory\n");
5696 Status = STATUS_INSUFFICIENT_RESOURCES;
5697 ExFreePool(ned1);
5698 goto end;
5699 }
5700
5701 ned1->generation = Vcb->superblock.generation;
5702 ned1->decoded_size = size;
5703 ned1->compression = ed->compression;
5704 ned1->encryption = ed->encryption;
5705 ned1->encoding = ed->encoding;
5706 ned1->type = ed->type;
5707
5708 RtlCopyMemory(&ned1->data[0], &ed->data[0], size);
5709
5710 newext1->offset = ext->offset;
5711 newext1->data = ned1;
5712 newext1->datalen = sizeof(EXTENT_DATA) - 1 + size;
5713 newext1->unique = FALSE;
5714 newext1->ignore = FALSE;
5715
5716 size = ext->offset + len - end_data;
5717
5718 ned2 = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + size, ALLOC_TAG);
5719 if (!ned2) {
5720 ERR("out of memory\n");
5721 Status = STATUS_INSUFFICIENT_RESOURCES;
5722 ExFreePool(ned1);
5723 ExFreePool(newext1);
5724 goto end;
5725 }
5726
5727 newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
5728 if (!newext2) {
5729 ERR("out of memory\n");
5730 Status = STATUS_INSUFFICIENT_RESOURCES;
5731 ExFreePool(ned1);
5732 ExFreePool(newext1);
5733 ExFreePool(ned2);
5734 goto end;
5735 }
5736
5737 ned2->generation = Vcb->superblock.generation;
5738 ned2->decoded_size = size;
5739 ned2->compression = ed->compression;
5740 ned2->encryption = ed->encryption;
5741 ned2->encoding = ed->encoding;
5742 ned2->type = ed->type;
5743
5744 RtlCopyMemory(&ned2->data[0], &ed->data[end_data - ext->offset], size);
5745
5746 newext2->offset = end_data;
5747 newext2->data = ned2;
5748 newext2->datalen = sizeof(EXTENT_DATA) - 1 + size;
5749 newext2->unique = FALSE;
5750 newext2->ignore = FALSE;
5751
5752 InsertHeadList(&ext->list_entry, &newext1->list_entry);
5753 InsertHeadList(&newext1->list_entry, &newext2->list_entry);
5754
5755 remove_fcb_extent(fcb, ext, rollback);
5756
5757 fcb->inode_item.st_blocks -= end_data - start_data;
5758 }
5759 } else if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) {
5760 if (start_data <= ext->offset && end_data >= ext->offset + len) { // remove all
5761 if (ed2->address != 0) {
5762 chunk* c;
5763
5764 fcb->inode_item.st_blocks -= len;
5765
5766 c = get_chunk_from_address(Vcb, ed2->address);
5767
5768 if (!c) {
5769 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
5770 } else {
5771 Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, -1,
5772 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
5773 if (!NT_SUCCESS(Status)) {
5774 ERR("update_changed_extent_ref returned %08x\n", Status);
5775 goto end;
5776 }
5777 }
5778 }
5779
5780 remove_fcb_extent(fcb, ext, rollback);
5781 } else if (start_data <= ext->offset && end_data < ext->offset + len) { // remove beginning
5782 EXTENT_DATA* ned;
5783 EXTENT_DATA2* ned2;
5784 extent* newext;
5785
5786 if (ed2->address != 0)
5787 fcb->inode_item.st_blocks -= end_data - ext->offset;
5788
5789 ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
5790 if (!ned) {
5791 ERR("out of memory\n");
5792 Status = STATUS_INSUFFICIENT_RESOURCES;
5793 goto end;
5794 }
5795
5796 newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
5797 if (!newext) {
5798 ERR("out of memory\n");
5799 Status = STATUS_INSUFFICIENT_RESOURCES;
5800 ExFreePool(ned);
5801 goto end;
5802 }
5803
5804 ned2 = (EXTENT_DATA2*)&ned->data[0];
5805
5806 ned->generation = Vcb->superblock.generation;
5807 ned->decoded_size = ed->decoded_size;
5808 ned->compression = ed->compression;
5809 ned->encryption = ed->encryption;
5810 ned->encoding = ed->encoding;
5811 ned->type = ed->type;
5812 ned2->address = ed2->address;
5813 ned2->size = ed2->size;
5814 ned2->offset = ed2->address == 0 ? 0 : (ed2->offset + (end_data - ext->offset));
5815 ned2->num_bytes = ed2->num_bytes - (end_data - ext->offset);
5816
5817 newext->offset = end_data;
5818 newext->data = ned;
5819 newext->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
5820 newext->unique = ext->unique;
5821 newext->ignore = FALSE;
5822 InsertHeadList(&ext->list_entry, &newext->list_entry);
5823
5824 remove_fcb_extent(fcb, ext, rollback);
5825 } else if (start_data > ext->offset && end_data >= ext->offset + len) { // remove end
5826 EXTENT_DATA* ned;
5827 EXTENT_DATA2* ned2;
5828 extent* newext;
5829
5830 if (ed2->address != 0)
5831 fcb->inode_item.st_blocks -= ext->offset + len - start_data;
5832
5833 ned = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
5834 if (!ned) {
5835 ERR("out of memory\n");
5836 Status = STATUS_INSUFFICIENT_RESOURCES;
5837 goto end;
5838 }
5839
5840 newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
5841 if (!newext) {
5842 ERR("out of memory\n");
5843 Status = STATUS_INSUFFICIENT_RESOURCES;
5844 ExFreePool(ned);
5845 goto end;
5846 }
5847
5848 ned2 = (EXTENT_DATA2*)&ned->data[0];
5849
5850 ned->generation = Vcb->superblock.generation;
5851 ned->decoded_size = ed->decoded_size;
5852 ned->compression = ed->compression;
5853 ned->encryption = ed->encryption;
5854 ned->encoding = ed->encoding;
5855 ned->type = ed->type;
5856 ned2->address = ed2->address;
5857 ned2->size = ed2->size;
5858 ned2->offset = ed2->address == 0 ? 0 : ed2->offset;
5859 ned2->num_bytes = start_data - ext->offset;
5860
5861 newext->offset = ext->offset;
5862 newext->data = ned;
5863 newext->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
5864 newext->unique = ext->unique;
5865 newext->ignore = FALSE;
5866 InsertHeadList(&ext->list_entry, &newext->list_entry);
5867
5868 remove_fcb_extent(fcb, ext, rollback);
5869 } else if (start_data > ext->offset && end_data < ext->offset + len) { // remove middle
5870 EXTENT_DATA *neda, *nedb;
5871 EXTENT_DATA2 *neda2, *nedb2;
5872 extent *newext1, *newext2;
5873
5874 if (ed2->address != 0) {
5875 chunk* c;
5876
5877 fcb->inode_item.st_blocks -= end_data - start_data;
5878
5879 c = get_chunk_from_address(Vcb, ed2->address);
5880
5881 if (!c) {
5882 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
5883 } else {
5884 Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
5885 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
5886 if (!NT_SUCCESS(Status)) {
5887 ERR("update_changed_extent_ref returned %08x\n", Status);
5888 goto end;
5889 }
5890 }
5891 }
5892
5893 neda = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
5894 if (!neda) {
5895 ERR("out of memory\n");
5896 Status = STATUS_INSUFFICIENT_RESOURCES;
5897 goto end;
5898 }
5899
5900 newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
5901 if (!newext1) {
5902 ERR("out of memory\n");
5903 Status = STATUS_INSUFFICIENT_RESOURCES;
5904 ExFreePool(neda);
5905 goto end;
5906 }
5907
5908 nedb = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
5909 if (!nedb) {
5910 ERR("out of memory\n");
5911 Status = STATUS_INSUFFICIENT_RESOURCES;
5912 ExFreePool(neda);
5913 ExFreePool(newext1);
5914 goto end;
5915 }
5916
5917 newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
5918 if (!newext1) {
5919 ERR("out of memory\n");
5920 Status = STATUS_INSUFFICIENT_RESOURCES;
5921 ExFreePool(neda);
5922 ExFreePool(newext1);
5923 ExFreePool(nedb);
5924 goto end;
5925 }
5926
5927 neda2 = (EXTENT_DATA2*)&neda->data[0];
5928
5929 neda->generation = Vcb->superblock.generation;
5930 neda->decoded_size = ed->decoded_size;
5931 neda->compression = ed->compression;
5932 neda->encryption = ed->encryption;
5933 neda->encoding = ed->encoding;
5934 neda->type = ed->type;
5935 neda2->address = ed2->address;
5936 neda2->size = ed2->size;
5937 neda2->offset = ed2->address == 0 ? 0 : ed2->offset;
5938 neda2->num_bytes = start_data - ext->offset;
5939
5940 nedb2 = (EXTENT_DATA2*)&nedb->data[0];
5941
5942 nedb->generation = Vcb->superblock.generation;
5943 nedb->decoded_size = ed->decoded_size;
5944 nedb->compression = ed->compression;
5945 nedb->encryption = ed->encryption;
5946 nedb->encoding = ed->encoding;
5947 nedb->type = ed->type;
5948 nedb2->address = ed2->address;
5949 nedb2->size = ed2->size;
5950 nedb2->offset = ed2->address == 0 ? 0 : (ed2->offset + (end_data - ext->offset));
5951 nedb2->num_bytes = ext->offset + len - end_data;
5952
5953 newext1->offset = ext->offset;
5954 newext1->data = neda;
5955 newext1->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
5956 newext1->unique = FALSE;
5957 newext1->ignore = FALSE;
5958
5959 newext2->offset = end_data;
5960 newext2->data = nedb;
5961 newext2->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
5962 newext2->unique = FALSE;
5963 newext2->ignore = FALSE;
5964
5965 InsertHeadList(&ext->list_entry, &newext1->list_entry);
5966 InsertHeadList(&newext1->list_entry, &newext2->list_entry);
5967
5968 remove_fcb_extent(fcb, ext, rollback);
5969 }
5970 }
5971 }
5972 }
5973
5974 le = le2;
5975 }
5976
5977 // FIXME - do bitmap analysis of changed extents, and free what we can
5978
5979 Status = STATUS_SUCCESS;
5980
5981 end:
5982 fcb->extents_changed = TRUE;
5983 mark_fcb_dirty(fcb);
5984
5985 return Status;
5986 }
5987
5988 static NTSTATUS do_write_data(device_extension* Vcb, UINT64 address, void* data, UINT64 length, LIST_ENTRY* changed_sector_list, PIRP Irp) {
5989 NTSTATUS Status;
5990 changed_sector* sc;
5991 int i;
5992
5993 Status = write_data_complete(Vcb, address, data, length, Irp, NULL);
5994 if (!NT_SUCCESS(Status)) {
5995 ERR("write_data returned %08x\n", Status);
5996 return Status;
5997 }
5998
5999 if (changed_sector_list) {
6000 sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
6001 if (!sc) {
6002 ERR("out of memory\n");
6003 return STATUS_INSUFFICIENT_RESOURCES;
6004 }
6005
6006 sc->ol.key = address;
6007 sc->length = length / Vcb->superblock.sector_size;
6008 sc->deleted = FALSE;
6009
6010 sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG);
6011 if (!sc->checksums) {
6012 ERR("out of memory\n");
6013 ExFreePool(sc);
6014 return STATUS_INSUFFICIENT_RESOURCES;
6015 }
6016
6017 for (i = 0; i < sc->length; i++) {
6018 sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
6019 }
6020
6021 insert_into_ordered_list(changed_sector_list, &sc->ol);
6022 }
6023
6024 return STATUS_SUCCESS;
6025 }
6026
6027 static void add_insert_extent_rollback(LIST_ENTRY* rollback, fcb* fcb, extent* ext) {
6028 rollback_extent* re;
6029
6030 re = ExAllocatePoolWithTag(NonPagedPool, sizeof(rollback_extent), ALLOC_TAG);
6031 if (!re) {
6032 ERR("out of memory\n");
6033 return;
6034 }
6035
6036 re->fcb = fcb;
6037 re->ext = ext;
6038
6039 add_rollback(rollback, ROLLBACK_INSERT_EXTENT, re);
6040 }
6041
6042 static BOOL add_extent_to_fcb(fcb* fcb, UINT64 offset, EXTENT_DATA* ed, ULONG edsize, BOOL unique, LIST_ENTRY* rollback) {
6043 extent* ext;
6044 LIST_ENTRY* le;
6045
6046 ext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
6047 if (!ext) {
6048 ERR("out of memory\n");
6049 return FALSE;
6050 }
6051
6052 ext->offset = offset;
6053 ext->data = ed;
6054 ext->datalen = edsize;
6055 ext->unique = unique;
6056 ext->ignore = FALSE;
6057
6058 le = fcb->extents.Flink;
6059 while (le != &fcb->extents) {
6060 extent* oldext = CONTAINING_RECORD(le, extent, list_entry);
6061
6062 if (!oldext->ignore) {
6063 if (oldext->offset > offset) {
6064 InsertHeadList(le->Blink, &ext->list_entry);
6065 goto end;
6066 }
6067 }
6068
6069 le = le->Flink;
6070 }
6071
6072 InsertTailList(&fcb->extents, &ext->list_entry);
6073
6074 end:
6075 add_insert_extent_rollback(rollback, fcb, ext);
6076
6077 return TRUE;
6078 }
6079
6080 static void remove_fcb_extent(fcb* fcb, extent* ext, LIST_ENTRY* rollback) {
6081 if (!ext->ignore) {
6082 rollback_extent* re;
6083
6084 ext->ignore = TRUE;
6085
6086 re = ExAllocatePoolWithTag(NonPagedPool, sizeof(rollback_extent), ALLOC_TAG);
6087 if (!re) {
6088 ERR("out of memory\n");
6089 return;
6090 }
6091
6092 re->fcb = fcb;
6093 re->ext = ext;
6094
6095 add_rollback(rollback, ROLLBACK_DELETE_EXTENT, re);
6096 }
6097 }
6098
6099 static void add_changed_extent_ref(chunk* c, UINT64 address, UINT64 size, UINT64 root, UINT64 objid, UINT64 offset, UINT32 count, BOOL no_csum) {
6100 changed_extent* ce;
6101 changed_extent_ref* cer;
6102 LIST_ENTRY* le;
6103
6104 ce = get_changed_extent_item(c, address, size, no_csum);
6105
6106 if (!ce) {
6107 ERR("get_changed_extent_item failed\n");
6108 return;
6109 }
6110
6111 le = ce->refs.Flink;
6112 while (le != &ce->refs) {
6113 cer = CONTAINING_RECORD(le, changed_extent_ref, list_entry);
6114
6115 if (cer->edr.root == root && cer->edr.objid == objid && cer->edr.offset == offset) {
6116 ce->count += count;
6117 cer->edr.count += count;
6118 return;
6119 }
6120
6121 le = le->Flink;
6122 }
6123
6124 cer = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent_ref), ALLOC_TAG);
6125
6126 if (!cer) {
6127 ERR("out of memory\n");
6128 return;
6129 }
6130
6131 cer->edr.root = root;
6132 cer->edr.objid = objid;
6133 cer->edr.offset = offset;
6134 cer->edr.count = count;
6135
6136 InsertTailList(&ce->refs, &cer->list_entry);
6137
6138 ce->count += count;
6139 }
6140
6141 BOOL insert_extent_chunk(device_extension* Vcb, fcb* fcb, chunk* c, UINT64 start_data, UINT64 length, BOOL prealloc, void* data,
6142 LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback, UINT8 compression, UINT64 decoded_size) {
6143 UINT64 address;
6144 NTSTATUS Status;
6145 EXTENT_DATA* ed;
6146 EXTENT_DATA2* ed2;
6147 ULONG edsize = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
6148 // #ifdef DEBUG_PARANOID
6149 // traverse_ptr tp;
6150 // KEY searchkey;
6151 // #endif
6152
6153 TRACE("(%p, (%llx, %llx), %llx, %llx, %llx, %u, %p, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, c->offset, start_data, length, prealloc, data, changed_sector_list, rollback);
6154
6155 if (!find_address_in_chunk(Vcb, c, length, &address))
6156 return FALSE;
6157
6158 // #ifdef DEBUG_PARANOID
6159 // searchkey.obj_id = address;
6160 // searchkey.obj_type = TYPE_EXTENT_ITEM;
6161 // searchkey.offset = 0xffffffffffffffff;
6162 //
6163 // Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
6164 // if (!NT_SUCCESS(Status)) {
6165 // ERR("error - find_item returned %08x\n", Status);
6166 // } else if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
6167 // ERR("address %llx already allocated\n", address);
6168 // int3;
6169 // }
6170 // #endif
6171
6172 if (data) {
6173 Status = do_write_data(Vcb, address, data, length, changed_sector_list, Irp);
6174 if (!NT_SUCCESS(Status)) {
6175 ERR("do_write_data returned %08x\n", Status);
6176 return FALSE;
6177 }
6178 }
6179
6180 // add extent data to inode
6181 ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
6182 if (!ed) {
6183 ERR("out of memory\n");
6184 return FALSE;
6185 }
6186
6187 ed->generation = Vcb->superblock.generation;
6188 ed->decoded_size = decoded_size;
6189 ed->compression = compression;
6190 ed->encryption = BTRFS_ENCRYPTION_NONE;
6191 ed->encoding = BTRFS_ENCODING_NONE;
6192 ed->type = prealloc ? EXTENT_TYPE_PREALLOC : EXTENT_TYPE_REGULAR;
6193
6194 ed2 = (EXTENT_DATA2*)ed->data;
6195 ed2->address = address;
6196 ed2->size = length;
6197 ed2->offset = 0;
6198 ed2->num_bytes = decoded_size;
6199
6200 if (!add_extent_to_fcb(fcb, start_data, ed, edsize, TRUE, rollback)) {
6201 ERR("add_extent_to_fcb failed\n");
6202 ExFreePool(ed);
6203 return FALSE;
6204 }
6205
6206 increase_chunk_usage(c, length);
6207 space_list_subtract(Vcb, c, FALSE, address, length, rollback);
6208
6209 fcb->inode_item.st_blocks += decoded_size;
6210
6211 fcb->extents_changed = TRUE;
6212 mark_fcb_dirty(fcb);
6213
6214 ExAcquireResourceExclusiveLite(&c->changed_extents_lock, TRUE);
6215
6216 add_changed_extent_ref(c, address, length, fcb->subvol->id, fcb->inode, start_data, 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM);
6217
6218 ExReleaseResourceLite(&c->changed_extents_lock);
6219
6220 return TRUE;
6221 }
6222
6223 static BOOL extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data,
6224 LIST_ENTRY* changed_sector_list, extent* ext, chunk* c, PIRP Irp, LIST_ENTRY* rollback) {
6225 EXTENT_DATA* ed;
6226 EXTENT_DATA2 *ed2, *ed2orig;
6227 extent* newext;
6228 UINT64 addr, origsize;
6229 NTSTATUS Status;
6230 LIST_ENTRY* le;
6231
6232 TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data,
6233 length, data, changed_sector_list, ext, c, rollback);
6234
6235 ed2orig = (EXTENT_DATA2*)ext->data->data;
6236
6237 origsize = ed2orig->size;
6238 addr = ed2orig->address + ed2orig->size;
6239
6240 Status = write_data_complete(Vcb, addr, data, length, Irp, c);
6241 if (!NT_SUCCESS(Status)) {
6242 ERR("write_data returned %08x\n", Status);
6243 return FALSE;
6244 }
6245
6246 le = fcb->extents.Flink;
6247 while (le != &fcb->extents) {
6248 extent* ext2 = CONTAINING_RECORD(le, extent, list_entry);
6249
6250 if (!ext2->ignore && (ext2->data->type == EXTENT_TYPE_REGULAR || ext2->data->type == EXTENT_TYPE_PREALLOC)) {
6251 EXTENT_DATA2* ed2b = (EXTENT_DATA2*)ext2->data->data;
6252
6253 if (ed2b->address == ed2orig->address) {
6254 ed2b->size = origsize + length;
6255 ext2->data->decoded_size = origsize + length;
6256 }
6257 }
6258
6259 le = le->Flink;
6260 }
6261
6262 ed = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
6263 if (!ed) {
6264 ERR("out of memory\n");
6265 return FALSE;
6266 }
6267
6268 newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
6269 if (!newext) {
6270 ERR("out of memory\n");
6271 ExFreePool(ed);
6272 return FALSE;
6273 }
6274
6275 RtlCopyMemory(ed, ext->data, ext->datalen);
6276
6277 ed2 = (EXTENT_DATA2*)ed->data;
6278 ed2->offset = ed2orig->offset + ed2orig->num_bytes;
6279 ed2->num_bytes = length;
6280
6281 RtlCopyMemory(newext, ext, sizeof(extent));
6282 newext->offset = ext->offset + ed2orig->num_bytes;
6283 newext->data = ed;
6284
6285 InsertHeadList(&ext->list_entry, &newext->list_entry);
6286
6287 add_insert_extent_rollback(rollback, fcb, newext);
6288
6289 Status = update_changed_extent_ref(Vcb, c, ed2orig->address, origsize, fcb->subvol->id, fcb->inode, newext->offset - ed2->offset,
6290 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
6291
6292 if (!NT_SUCCESS(Status)) {
6293 ERR("update_changed_extent_ref returned %08x\n", Status);
6294 return FALSE;
6295 }
6296
6297 if (changed_sector_list) {
6298 int i;
6299 changed_sector* sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
6300 if (!sc) {
6301 ERR("out of memory\n");
6302 return FALSE;
6303 }
6304
6305 sc->ol.key = addr;
6306 sc->length = length / Vcb->superblock.sector_size;
6307 sc->deleted = FALSE;
6308
6309 sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG);
6310 if (!sc->checksums) {
6311 ERR("out of memory\n");
6312 ExFreePool(sc);
6313 return FALSE;
6314 }
6315
6316 for (i = 0; i < sc->length; i++) {
6317 sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + (i * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
6318 }
6319 insert_into_ordered_list(changed_sector_list, &sc->ol);
6320 }
6321
6322 increase_chunk_usage(c, length);
6323
6324 space_list_subtract(Vcb, c, FALSE, addr, length, NULL); // no rollback as we don't reverse extending the extent
6325
6326 fcb->inode_item.st_blocks += length;
6327
6328 return TRUE;
6329 }
6330
6331 static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data,
6332 LIST_ENTRY* changed_sector_list, PIRP Irp, UINT64* written, LIST_ENTRY* rollback) {
6333 BOOL success = FALSE;
6334 EXTENT_DATA* ed;
6335 EXTENT_DATA2* ed2;
6336 chunk* c;
6337 LIST_ENTRY* le;
6338 space* s;
6339 extent* ext = NULL;
6340
6341 le = fcb->extents.Flink;
6342
6343 while (le != &fcb->extents) {
6344 extent* nextext = CONTAINING_RECORD(le, extent, list_entry);
6345
6346 if (!nextext->ignore) {
6347 if (nextext->offset == start_data) {
6348 ext = nextext;
6349 break;
6350 } else if (nextext->offset > start_data)
6351 break;
6352
6353 ext = nextext;
6354 }
6355
6356 le = le->Flink;
6357 }
6358
6359 if (!ext)
6360 return FALSE;
6361
6362 if (!ext->unique) {
6363 TRACE("extent was not unique\n");
6364 return FALSE;
6365 }
6366
6367 ed = ext->data;
6368
6369 if (ext->datalen < sizeof(EXTENT_DATA)) {
6370 ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA));
6371 return FALSE;
6372 }
6373
6374 if (ed->type != EXTENT_TYPE_REGULAR) {
6375 TRACE("not extending extent which is not EXTENT_TYPE_REGULAR\n");
6376 return FALSE;
6377 }
6378
6379 ed2 = (EXTENT_DATA2*)ed->data;
6380
6381 if (ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
6382 ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
6383 return FALSE;
6384 }
6385
6386 if (ext->offset + ed2->num_bytes != start_data) {
6387 TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", ext->offset, ed2->num_bytes, start_data);
6388 return FALSE;
6389 }
6390
6391 if (ed->compression != BTRFS_COMPRESSION_NONE) {
6392 TRACE("not extending a compressed extent\n");
6393 return FALSE;
6394 }
6395
6396 if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
6397 WARN("encryption not supported\n");
6398 return FALSE;
6399 }
6400
6401 if (ed->encoding != BTRFS_ENCODING_NONE) {
6402 WARN("other encodings not supported\n");
6403 return FALSE;
6404 }
6405
6406 if (ed2->size - ed2->offset != ed2->num_bytes) {
6407 TRACE("last EXTENT_DATA does not run all the way to the end of the extent\n");
6408 return FALSE;
6409 }
6410
6411 if (ed2->size >= MAX_EXTENT_SIZE) {
6412 TRACE("extent size was too large to extend (%llx >= %llx)\n", ed2->size, (UINT64)MAX_EXTENT_SIZE);
6413 return FALSE;
6414 }
6415
6416 c = get_chunk_from_address(Vcb, ed2->address);
6417
6418 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
6419
6420 le = c->space.Flink;
6421 while (le != &c->space) {
6422 s = CONTAINING_RECORD(le, space, list_entry);
6423
6424 if (s->address == ed2->address + ed2->size) {
6425 UINT64 newlen = min(min(s->size, length), MAX_EXTENT_SIZE - ed2->size);
6426
6427 success = extend_data(Vcb, fcb, start_data, newlen, data, changed_sector_list, ext, c, Irp, rollback);
6428
6429 if (success)
6430 *written += newlen;
6431
6432 break;
6433 } else if (s->address > ed2->address + ed2->size)
6434 break;
6435
6436 le = le->Flink;
6437 }
6438
6439 ExReleaseResourceLite(&c->lock);
6440
6441 return success;
6442 }
6443
6444 static NTSTATUS insert_prealloc_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback) {
6445 LIST_ENTRY* le;
6446 chunk* c;
6447 #ifdef __REACTOS__
6448 UINT64 flags;
6449 #else
6450 UINT64 flags, origlength = length;
6451 #endif
6452 NTSTATUS Status;
6453 BOOL page_file = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE;
6454
6455 flags = fcb->Vcb->data_flags;
6456
6457 // FIXME - try and maximize contiguous ranges first. If we can't do that,
6458 // allocate all the free space we find until it's enough.
6459
6460 do {
6461 UINT64 extlen = min(MAX_EXTENT_SIZE, length);
6462
6463 ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
6464
6465 le = fcb->Vcb->chunks.Flink;
6466 while (le != &fcb->Vcb->chunks) {
6467 c = CONTAINING_RECORD(le, chunk, list_entry);
6468
6469 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
6470
6471 if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= extlen) {
6472 if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen)) {
6473 ExReleaseResourceLite(&c->lock);
6474 ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
6475 goto cont;
6476 }
6477 }
6478
6479 ExReleaseResourceLite(&c->lock);
6480
6481 le = le->Flink;
6482 }
6483
6484 ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
6485
6486 ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
6487
6488 if ((c = alloc_chunk(fcb->Vcb, flags))) {
6489 ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
6490
6491 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
6492
6493 if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= extlen) {
6494 if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen)) {
6495 ExReleaseResourceLite(&c->lock);
6496 goto cont;
6497 }
6498 }
6499
6500 ExReleaseResourceLite(&c->lock);
6501 } else
6502 ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
6503
6504 WARN("couldn't find any data chunks with %llx bytes free\n", origlength);
6505 Status = STATUS_DISK_FULL;
6506 goto end;
6507
6508 cont:
6509 length -= extlen;
6510 start += extlen;
6511 } while (length > 0);
6512
6513 Status = STATUS_SUCCESS;
6514
6515 end:
6516 return Status;
6517 }
6518
6519 // static void print_tree(tree* t) {
6520 // LIST_ENTRY* le = t->itemlist.Flink;
6521 // while (le != &t->itemlist) {
6522 // tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
6523 // ERR("%llx,%x,%llx (ignore = %s)\n", td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE");
6524 // le = le->Flink;
6525 // }
6526 // }
6527
6528 NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
6529 LIST_ENTRY* le;
6530 chunk* c;
6531 UINT64 flags, orig_length = length, written = 0;
6532
6533 TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, length, data, changed_sector_list);
6534
6535 // FIXME - split data up if not enough space for just one extent
6536
6537 if (start_data > 0) {
6538 try_extend_data(Vcb, fcb, start_data, length, data, changed_sector_list, Irp, &written, rollback);
6539
6540 if (written == length)
6541 return STATUS_SUCCESS;
6542 else if (written > 0) {
6543 start_data += written;
6544 length -= written;
6545 data = &((UINT8*)data)[written];
6546 }
6547 }
6548
6549 flags = Vcb->data_flags;
6550
6551 while (written < orig_length) {
6552 UINT64 newlen = min(length, MAX_EXTENT_SIZE);
6553 BOOL done = FALSE;
6554
6555 // Rather than necessarily writing the whole extent at once, we deal with it in blocks of 128 MB.
6556 // First, see if we can write the extent part to an existing chunk.
6557
6558 ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE);
6559
6560 le = Vcb->chunks.Flink;
6561 while (le != &Vcb->chunks) {
6562 c = CONTAINING_RECORD(le, chunk, list_entry);
6563
6564 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
6565
6566 if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen) {
6567 if (insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, changed_sector_list, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen)) {
6568 written += newlen;
6569
6570 if (written == orig_length) {
6571 ExReleaseResourceLite(&c->lock);
6572 ExReleaseResourceLite(&Vcb->chunk_lock);
6573 return STATUS_SUCCESS;
6574 } else {
6575 done = TRUE;
6576 start_data += newlen;
6577 length -= newlen;
6578 data = &((UINT8*)data)[newlen];
6579 break;
6580 }
6581 }
6582 }
6583
6584 ExReleaseResourceLite(&c->lock);
6585
6586 le = le->Flink;
6587 }
6588
6589 ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
6590
6591 if (done) continue;
6592
6593 // Otherwise, see if we can put it in a new chunk.
6594
6595 ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
6596
6597 if ((c = alloc_chunk(Vcb, flags))) {
6598 ExReleaseResourceLite(&Vcb->chunk_lock);
6599
6600 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
6601
6602 if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen) {
6603 if (insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, changed_sector_list, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen)) {
6604 written += newlen;
6605
6606 if (written == orig_length) {
6607 ExReleaseResourceLite(&c->lock);
6608 return STATUS_SUCCESS;
6609 } else {
6610 done = TRUE;
6611 start_data += newlen;
6612 length -= newlen;
6613 data = &((UINT8*)data)[newlen];
6614 }
6615 }
6616 }
6617
6618 ExReleaseResourceLite(&c->lock);
6619 } else
6620 ExReleaseResourceLite(&Vcb->chunk_lock);
6621
6622 if (!done) {
6623 FIXME("FIXME - not enough room to write whole extent part, try to write bits and pieces\n"); // FIXME
6624 break;
6625 }
6626 }
6627
6628 WARN("couldn't find any data chunks with %llx bytes free\n", length);
6629
6630 return STATUS_DISK_FULL;
6631 }
6632
6633 static void update_checksum_tree(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) {
6634 LIST_ENTRY* le = Vcb->sector_checksums.Flink;
6635 changed_sector* cs;
6636 traverse_ptr tp, next_tp;
6637 KEY searchkey;
6638 UINT32* data;
6639 NTSTATUS Status;
6640
6641 if (!Vcb->checksum_root) {
6642 ERR("no checksum root\n");
6643 goto exit;
6644 }
6645
6646 while (le != &Vcb->sector_checksums) {
6647 UINT64 startaddr, endaddr;
6648 ULONG len;
6649 UINT32* checksums;
6650 RTL_BITMAP bmp;
6651 ULONG* bmparr;
6652 ULONG runlength, index;
6653
6654 cs = (changed_sector*)le;
6655
6656 searchkey.obj_id = EXTENT_CSUM_ID;
6657 searchkey.obj_type = TYPE_EXTENT_CSUM;
6658 searchkey.offset = cs->ol.key;
6659
6660 // FIXME - create checksum_root if it doesn't exist at all
6661
6662 Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp);
6663 if (!NT_SUCCESS(Status)) { // tree is completely empty
6664 // FIXME - do proper check here that tree is empty
6665 if (!cs->deleted) {
6666 checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * cs->length, ALLOC_TAG);
6667 if (!checksums) {
6668 ERR("out of memory\n");
6669 goto exit;
6670 }
6671
6672 RtlCopyMemory(checksums, cs->checksums, sizeof(UINT32) * cs->length);
6673
6674 if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, cs->ol.key, checksums, sizeof(UINT32) * cs->length, NULL, Irp, rollback)) {
6675 ERR("insert_tree_item failed\n");
6676 ExFreePool(checksums);
6677 goto exit;
6678 }
6679 }
6680 } else {
6681 UINT32 tplen;
6682
6683 // FIXME - check entry is TYPE_EXTENT_CSUM?
6684
6685 if (tp.item->key.offset < cs->ol.key && tp.item->key.offset + (tp.item->size * Vcb->superblock.sector_size / sizeof(UINT32)) >= cs->ol.key)
6686 startaddr = tp.item->key.offset;
6687 else
6688 startaddr = cs->ol.key;
6689
6690 searchkey.obj_id = EXTENT_CSUM_ID;
6691 searchkey.obj_type = TYPE_EXTENT_CSUM;
6692 searchkey.offset = cs->ol.key + (cs->length * Vcb->superblock.sector_size);
6693
6694 Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp);
6695 if (!NT_SUCCESS(Status)) {
6696 ERR("error - find_item returned %08x\n", Status);
6697 goto exit;
6698 }
6699
6700 tplen = tp.item->size / sizeof(UINT32);
6701
6702 if (tp.item->key.offset + (tplen * Vcb->superblock.sector_size) >= cs->ol.key + (cs->length * Vcb->superblock.sector_size))
6703 endaddr = tp.item->key.offset + (tplen * Vcb->superblock.sector_size);
6704 else
6705 endaddr = cs->ol.key + (cs->length * Vcb->superblock.sector_size);
6706
6707 TRACE("cs starts at %llx (%x sectors)\n", cs->ol.key, cs->length);
6708 TRACE("startaddr = %llx\n", startaddr);
6709 TRACE("endaddr = %llx\n", endaddr);
6710
6711 len = (endaddr - startaddr) / Vcb->superblock.sector_size;
6712
6713 checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * len, ALLOC_TAG);
6714 if (!checksums) {
6715 ERR("out of memory\n");
6716 goto exit;
6717 }
6718
6719 bmparr = ExAllocatePoolWithTag(PagedPool, sizeof(ULONG) * ((len/8)+1), ALLOC_TAG);
6720 if (!bmparr) {
6721 ERR("out of memory\n");
6722 ExFreePool(checksums);
6723 goto exit;
6724 }
6725
6726 RtlInitializeBitMap(&bmp, bmparr, len);
6727 RtlSetAllBits(&bmp);
6728
6729 searchkey.obj_id = EXTENT_CSUM_ID;
6730 searchkey.obj_type = TYPE_EXTENT_CSUM;
6731 searchkey.offset = cs->ol.key;
6732
6733 Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp);
6734 if (!NT_SUCCESS(Status)) {
6735 ERR("error - find_item returned %08x\n", Status);
6736 goto exit;
6737 }
6738
6739 // set bit = free space, cleared bit = allocated sector
6740
6741 // ERR("start loop\n");
6742 while (tp.item->key.offset < endaddr) {
6743 // ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
6744 if (tp.item->key.offset >= startaddr) {
6745 if (tp.item->size > 0) {
6746 RtlCopyMemory(&checksums[(tp.item->key.offset - startaddr) / Vcb->superblock.sector_size], tp.item->data, tp.item->size);
6747 RtlClearBits(&bmp, (tp.item->key.offset - startaddr) / Vcb->superblock.sector_size, tp.item->size / sizeof(UINT32));
6748 }
6749
6750 delete_tree_item(Vcb, &tp, rollback);
6751 }
6752
6753 if (find_next_item(Vcb, &tp, &next_tp, FALSE, Irp)) {
6754 tp = next_tp;
6755 } else
6756 break;
6757 }
6758 // ERR("end loop\n");
6759
6760 if (cs->deleted) {
6761 RtlSetBits(&bmp, (cs->ol.key - startaddr) / Vcb->superblock.sector_size, cs->length);
6762 } else {
6763 RtlCopyMemory(&checksums[(cs->ol.key - startaddr) / Vcb->superblock.sector_size], cs->checksums, cs->length * sizeof(UINT32));
6764 RtlClearBits(&bmp, (cs->ol.key - startaddr) / Vcb->superblock.sector_size, cs->length);
6765 }
6766
6767 runlength = RtlFindFirstRunClear(&bmp, &index);
6768
6769 while (runlength != 0) {
6770 do {
6771 ULONG rl;
6772
6773 if (runlength * sizeof(UINT32) > MAX_CSUM_SIZE)
6774 rl = MAX_CSUM_SIZE / sizeof(UINT32);
6775 else
6776 rl = runlength;
6777
6778 data = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * rl, ALLOC_TAG);
6779 if (!data) {
6780 ERR("out of memory\n");
6781 ExFreePool(bmparr);
6782 ExFreePool(checksums);
6783 goto exit;
6784 }
6785
6786 RtlCopyMemory(data, &checksums[index], sizeof(UINT32) * rl);
6787
6788 if (!insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, startaddr + (index * Vcb->superblock.sector_size), data, sizeof(UINT32) * rl, NULL, Irp, rollback)) {
6789 ERR("insert_tree_item failed\n");
6790 ExFreePool(data);
6791 ExFreePool(bmparr);
6792 ExFreePool(checksums);
6793 goto exit;
6794 }
6795
6796 runlength -= rl;
6797 index += rl;
6798 } while (runlength > 0);
6799
6800 runlength = RtlFindNextForwardRunClear(&bmp, index, &index);
6801 }
6802
6803 ExFreePool(bmparr);
6804 ExFreePool(checksums);
6805 }
6806
6807 le = le->Flink;
6808 }
6809
6810 exit:
6811 while (!IsListEmpty(&Vcb->sector_checksums)) {
6812 le = RemoveHeadList(&Vcb->sector_checksums);
6813 cs = (changed_sector*)le;
6814
6815 if (cs->checksums)
6816 ExFreePool(cs->checksums);
6817
6818 ExFreePool(cs);
6819 }
6820 }
6821
6822 void commit_checksum_changes(device_extension* Vcb, LIST_ENTRY* changed_sector_list) {
6823 while (!IsListEmpty(changed_sector_list)) {
6824 LIST_ENTRY* le = RemoveHeadList(changed_sector_list);
6825 InsertTailList(&Vcb->sector_checksums, le);
6826 }
6827 }
6828
6829 NTSTATUS truncate_file(fcb* fcb, UINT64 end, PIRP Irp, LIST_ENTRY* rollback) {
6830 NTSTATUS Status;
6831
6832 // FIXME - convert into inline extent if short enough
6833
6834 Status = excise_extents(fcb->Vcb, fcb, sector_align(end, fcb->Vcb->superblock.sector_size),
6835 sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), Irp, rollback);
6836 if (!NT_SUCCESS(Status)) {
6837 ERR("error - excise_extents failed\n");
6838 return Status;
6839 }
6840
6841 fcb->inode_item.st_size = end;
6842 TRACE("setting st_size to %llx\n", end);
6843
6844 fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
6845 fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
6846 fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
6847 // FIXME - inform cache manager of this
6848
6849 TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart);
6850
6851 return STATUS_SUCCESS;
6852 }
6853
6854 NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIRP Irp, LIST_ENTRY* rollback) {
6855 UINT64 oldalloc, newalloc;
6856 BOOL cur_inline;
6857 NTSTATUS Status;
6858
6859 TRACE("(%p, %p, %x, %u)\n", fcb, fileref, end, prealloc);
6860
6861 if (fcb->ads)
6862 return stream_set_end_of_file_information(fcb->Vcb, end, fcb, fileref, NULL, FALSE, rollback);
6863 else {
6864 extent* ext = NULL;
6865 LIST_ENTRY* le;
6866
6867 le = fcb->extents.Blink;
6868 while (le != &fcb->extents) {
6869 extent* ext2 = CONTAINING_RECORD(le, extent, list_entry);
6870
6871 if (!ext2->ignore) {
6872 ext = ext2;
6873 break;
6874 }
6875
6876 le = le->Blink;
6877 }
6878
6879 oldalloc = 0;
6880 if (ext) {
6881 EXTENT_DATA* ed = ext->data;
6882 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
6883
6884 if (ext->datalen < sizeof(EXTENT_DATA)) {
6885 ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA));
6886 return STATUS_INTERNAL_ERROR;
6887 }
6888
6889 oldalloc = ext->offset + (ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes);
6890 cur_inline = ed->type == EXTENT_TYPE_INLINE;
6891
6892 if (cur_inline && end > fcb->Vcb->options.max_inline) {
6893 LIST_ENTRY changed_sector_list;
6894 BOOL nocsum = fcb->inode_item.flags & BTRFS_INODE_NODATASUM;
6895 UINT64 origlength, length;
6896 UINT8* data;
6897 UINT64 offset = ext->offset;
6898
6899 TRACE("giving inline file proper extents\n");
6900
6901 origlength = ed->decoded_size;
6902
6903 cur_inline = FALSE;
6904
6905 if (!nocsum)
6906 InitializeListHead(&changed_sector_list);
6907
6908 length = sector_align(origlength, fcb->Vcb->superblock.sector_size);
6909
6910 data = ExAllocatePoolWithTag(PagedPool, length, ALLOC_TAG);
6911 if (!data) {
6912 ERR("could not allocate %llx bytes for data\n", length);
6913 return STATUS_INSUFFICIENT_RESOURCES;
6914 }
6915
6916 if (length > origlength)
6917 RtlZeroMemory(data + origlength, length - origlength);
6918
6919 RtlCopyMemory(data, ed->data, origlength);
6920
6921 fcb->inode_item.st_blocks -= origlength;
6922
6923 remove_fcb_extent(fcb, ext, rollback);
6924
6925 if (write_fcb_compressed(fcb)) {
6926 Status = write_compressed(fcb, offset, offset + length, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
6927 if (!NT_SUCCESS(Status)) {
6928 ERR("write_compressed returned %08x\n", Status);
6929 ExFreePool(data);
6930 return Status;
6931 }
6932 } else {
6933 Status = insert_extent(fcb->Vcb, fcb, offset, length, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
6934 if (!NT_SUCCESS(Status)) {
6935 ERR("insert_extent returned %08x\n", Status);
6936 ExFreePool(data);
6937 return Status;
6938 }
6939 }
6940
6941 oldalloc = ext->offset + length;
6942
6943 ExFreePool(data);
6944
6945 if (!nocsum) {
6946 ExAcquireResourceExclusiveLite(&fcb->Vcb->checksum_lock, TRUE);
6947 commit_checksum_changes(fcb->Vcb, &changed_sector_list);
6948 ExReleaseResourceLite(&fcb->Vcb->checksum_lock);
6949 }
6950 }
6951
6952 if (cur_inline) {
6953 ULONG edsize;
6954
6955 if (end > oldalloc) {
6956 edsize = sizeof(EXTENT_DATA) - 1 + end - ext->offset;
6957 ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
6958
6959 if (!ed) {
6960 ERR("out of memory\n");
6961 return STATUS_INSUFFICIENT_RESOURCES;
6962 }
6963
6964 RtlZeroMemory(ed, edsize);
6965 RtlCopyMemory(ed, ext->data, ext->datalen);
6966
6967 ed->decoded_size = end - ext->offset;
6968
6969 remove_fcb_extent(fcb, ext, rollback);
6970
6971 if (!add_extent_to_fcb(fcb, ext->offset, ed, edsize, ext->unique, rollback)) {
6972 ERR("add_extent_to_fcb failed\n");
6973 ExFreePool(ed);
6974 return STATUS_INTERNAL_ERROR;
6975 }
6976
6977 fcb->extents_changed = TRUE;
6978 mark_fcb_dirty(fcb);
6979 }
6980
6981 TRACE("extending inline file (oldalloc = %llx, end = %llx)\n", oldalloc, end);
6982
6983 fcb->inode_item.st_size = end;
6984 TRACE("setting st_size to %llx\n", end);
6985
6986 fcb->inode_item.st_blocks = end;
6987
6988 fcb->Header.AllocationSize.QuadPart = fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
6989 } else {
6990 newalloc = sector_align(end, fcb->Vcb->superblock.sector_size);
6991
6992 if (newalloc > oldalloc) {
6993 if (prealloc) {
6994 // FIXME - try and extend previous extent first
6995
6996 Status = insert_prealloc_extent(fcb, oldalloc, newalloc - oldalloc, rollback);
6997
6998 if (!NT_SUCCESS(Status)) {
6999 ERR("insert_prealloc_extent returned %08x\n", Status);
7000 return Status;
7001 }
7002 }
7003
7004 fcb->extents_changed = TRUE;
7005 mark_fcb_dirty(fcb);
7006 }
7007
7008 fcb->inode_item.st_size = end;
7009 TRACE("setting st_size to %llx\n", end);
7010
7011 TRACE("newalloc = %llx\n", newalloc);
7012
7013 fcb->Header.AllocationSize.QuadPart = newalloc;
7014 fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
7015 }
7016 } else {
7017 if (end > fcb->Vcb->options.max_inline) {
7018 newalloc = sector_align(end, fcb->Vcb->superblock.sector_size);
7019
7020 if (prealloc) {
7021 Status = insert_prealloc_extent(fcb, 0, newalloc, rollback);
7022
7023 if (!NT_SUCCESS(Status)) {
7024 ERR("insert_prealloc_extent returned %08x\n", Status);
7025 return Status;
7026 }
7027 }
7028
7029 fcb->extents_changed = TRUE;
7030 mark_fcb_dirty(fcb);
7031
7032 fcb->inode_item.st_size = end;
7033 TRACE("setting st_size to %llx\n", end);
7034
7035 TRACE("newalloc = %llx\n", newalloc);
7036
7037 fcb->Header.AllocationSize.QuadPart = newalloc;
7038 fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
7039 } else {
7040 EXTENT_DATA* ed;
7041 ULONG edsize;
7042
7043 edsize = sizeof(EXTENT_DATA) - 1 + end;
7044 ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
7045
7046 if (!ed) {
7047 ERR("out of memory\n");
7048 return STATUS_INSUFFICIENT_RESOURCES;
7049 }
7050
7051 ed->generation = fcb->Vcb->superblock.generation;
7052 ed->decoded_size = end;
7053 ed->compression = BTRFS_COMPRESSION_NONE;
7054 ed->encryption = BTRFS_ENCRYPTION_NONE;
7055 ed->encoding = BTRFS_ENCODING_NONE;
7056 ed->type = EXTENT_TYPE_INLINE;
7057
7058 RtlZeroMemory(ed->data, end);
7059
7060 if (!add_extent_to_fcb(fcb, 0, ed, edsize, FALSE, rollback)) {
7061 ERR("add_extent_to_fcb failed\n");
7062 ExFreePool(ed);
7063 return STATUS_INTERNAL_ERROR;
7064 }
7065
7066 fcb->extents_changed = TRUE;
7067 mark_fcb_dirty(fcb);
7068
7069 fcb->inode_item.st_size = end;
7070 TRACE("setting st_size to %llx\n", end);
7071
7072 fcb->inode_item.st_blocks = end;
7073
7074 fcb->Header.AllocationSize.QuadPart = fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
7075 }
7076 }
7077 }
7078
7079 return STATUS_SUCCESS;
7080 }
7081
7082 // #ifdef DEBUG_PARANOID
7083 // static void print_loaded_trees(tree* t, int spaces) {
7084 // char pref[10];
7085 // int i;
7086 // LIST_ENTRY* le;
7087 //
7088 // for (i = 0; i < spaces; i++) {
7089 // pref[i] = ' ';
7090 // }
7091 // pref[spaces] = 0;
7092 //
7093 // if (!t) {
7094 // ERR("%s(not loaded)\n", pref);
7095 // return;
7096 // }
7097 //
7098 // le = t->itemlist.Flink;
7099 // while (le != &t->itemlist) {
7100 // tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
7101 //
7102 // ERR("%s%llx,%x,%llx ignore=%s\n", pref, td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE");
7103 //
7104 // if (t->header.level > 0) {
7105 // print_loaded_trees(td->treeholder.tree, spaces+1);
7106 // }
7107 //
7108 // le = le->Flink;
7109 // }
7110 // }
7111
7112 // static void check_extents_consistent(device_extension* Vcb, fcb* fcb) {
7113 // KEY searchkey;
7114 // traverse_ptr tp, next_tp;
7115 // UINT64 length, oldlength, lastoff, alloc;
7116 // NTSTATUS Status;
7117 // EXTENT_DATA* ed;
7118 // EXTENT_DATA2* ed2;
7119 //
7120 // if (fcb->ads || fcb->inode_item.st_size == 0 || fcb->deleted)
7121 // return;
7122 //
7123 // TRACE("inode = %llx, subvol = %llx\n", fcb->inode, fcb->subvol->id);
7124 //
7125 // searchkey.obj_id = fcb->inode;
7126 // searchkey.obj_type = TYPE_EXTENT_DATA;
7127 // searchkey.offset = 0;
7128 //
7129 // Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
7130 // if (!NT_SUCCESS(Status)) {
7131 // ERR("error - find_item returned %08x\n", Status);
7132 // goto failure;
7133 // }
7134 //
7135 // if (keycmp(&searchkey, &tp.item->key)) {
7136 // ERR("could not find EXTENT_DATA at offset 0\n");
7137 // goto failure;
7138 // }
7139 //
7140 // if (tp.item->size < sizeof(EXTENT_DATA)) {
7141 // 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_DATA));
7142 // goto failure;
7143 // }
7144 //
7145 // ed = (EXTENT_DATA*)tp.item->data;
7146 // ed2 = (EXTENT_DATA2*)&ed->data[0];
7147 //
7148 // length = oldlength = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
7149 // lastoff = tp.item->key.offset;
7150 //
7151 // TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length);
7152 //
7153 // alloc = 0;
7154 // if (ed->type != EXTENT_TYPE_REGULAR || ed2->address != 0) {
7155 // alloc += length;
7156 // }
7157 //
7158 // while (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
7159 // if (next_tp.item->key.obj_id != searchkey.obj_id || next_tp.item->key.obj_type != searchkey.obj_type)
7160 // break;
7161 //
7162 // tp = next_tp;
7163 //
7164 // if (tp.item->size < sizeof(EXTENT_DATA)) {
7165 // 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_DATA));
7166 // goto failure;
7167 // }
7168 //
7169 // ed = (EXTENT_DATA*)tp.item->data;
7170 // ed2 = (EXTENT_DATA2*)&ed->data[0];
7171 //
7172 // length = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
7173 //
7174 // TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length);
7175 //
7176 // if (tp.item->key.offset != lastoff + oldlength) {
7177 // ERR("EXTENT_DATA in %llx,%llx was at %llx, expected %llx\n", fcb->subvol->id, fcb->inode, tp.item->key.offset, lastoff + oldlength);
7178 // goto failure;
7179 // }
7180 //
7181 // if (ed->type != EXTENT_TYPE_REGULAR || ed2->address != 0) {
7182 // alloc += length;
7183 // }
7184 //
7185 // oldlength = length;
7186 // lastoff = tp.item->key.offset;
7187 // }
7188 //
7189 // if (alloc != fcb->inode_item.st_blocks) {
7190 // ERR("allocation size was %llx, expected %llx\n", alloc, fcb->inode_item.st_blocks);
7191 // goto failure;
7192 // }
7193 //
7194 // // if (fcb->inode_item.st_blocks != lastoff + oldlength) {
7195 // // ERR("extents finished at %x, expected %x\n", (UINT32)(lastoff + oldlength), (UINT32)fcb->inode_item.st_blocks);
7196 // // goto failure;
7197 // // }
7198 //
7199 // return;
7200 //
7201 // failure:
7202 // if (fcb->subvol->treeholder.tree)
7203 // print_loaded_trees(fcb->subvol->treeholder.tree, 0);
7204 //
7205 // int3;
7206 // }
7207
7208 // static void check_extent_tree_consistent(device_extension* Vcb) {
7209 // KEY searchkey;
7210 // traverse_ptr tp, next_tp;
7211 // UINT64 lastaddr;
7212 // BOOL b, inconsistency;
7213 //
7214 // searchkey.obj_id = 0;
7215 // searchkey.obj_type = 0;
7216 // searchkey.offset = 0;
7217 //
7218 // if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
7219 // ERR("error - could not find any entries in extent_root\n");
7220 // int3;
7221 // }
7222 //
7223 // lastaddr = 0;
7224 // inconsistency = FALSE;
7225 //
7226 // do {
7227 // if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
7228 // // ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
7229 //
7230 // if (tp.item->key.obj_id < lastaddr) {
7231 // // ERR("inconsistency!\n");
7232 // // int3;
7233 // inconsistency = TRUE;
7234 // }
7235 //
7236 // lastaddr = tp.item->key.obj_id + tp.item->key.offset;
7237 // }
7238 //
7239 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
7240 // if (b) {
7241 // free_traverse_ptr(&tp);
7242 // tp = next_tp;
7243 // }
7244 // } while (b);
7245 //
7246 // free_traverse_ptr(&tp);
7247 //
7248 // if (!inconsistency)
7249 // return;
7250 //
7251 // ERR("Inconsistency detected:\n");
7252 //
7253 // if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
7254 // ERR("error - could not find any entries in extent_root\n");
7255 // int3;
7256 // }
7257 //
7258 // do {
7259 // if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
7260 // ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
7261 //
7262 // if (tp.item->key.obj_id < lastaddr) {
7263 // ERR("inconsistency!\n");
7264 // }
7265 //
7266 // lastaddr = tp.item->key.obj_id + tp.item->key.offset;
7267 // }
7268 //
7269 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
7270 // if (b) {
7271 // free_traverse_ptr(&tp);
7272 // tp = next_tp;
7273 // }
7274 // } while (b);
7275 //
7276 // free_traverse_ptr(&tp);
7277 //
7278 // int3;
7279 // }
7280 // #endif
7281
7282 static NTSTATUS do_write_file_prealloc(fcb* fcb, extent* ext, UINT64 start_data, UINT64 end_data, void* data, UINT64* written,
7283 LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
7284 EXTENT_DATA* ed = ext->data;
7285 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
7286 NTSTATUS Status;
7287 chunk* c;
7288
7289 if (start_data <= ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace all
7290 EXTENT_DATA* ned;
7291 extent* newext;
7292
7293 ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
7294 if (!ned) {
7295 ERR("out of memory\n");
7296 return STATUS_INSUFFICIENT_RESOURCES;
7297 }
7298
7299 newext = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
7300 if (!newext) {
7301 ERR("out of memory\n");
7302 ExFreePool(ned);
7303 return STATUS_INSUFFICIENT_RESOURCES;
7304 }
7305
7306 RtlCopyMemory(ned, ext->data, ext->datalen);
7307
7308 ned->type = EXTENT_TYPE_REGULAR;
7309
7310 Status = do_write_data(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, ed2->num_bytes, changed_sector_list, Irp);
7311 if (!NT_SUCCESS(Status)) {
7312 ERR("do_write_data returned %08x\n", Status);
7313 return Status;
7314 }
7315
7316 *written = ed2->num_bytes;
7317
7318 newext->offset = ext->offset;
7319 newext->data = ned;
7320 newext->datalen = ext->datalen;
7321 newext->unique = ext->unique;
7322 newext->ignore = FALSE;
7323 InsertHeadList(&ext->list_entry, &newext->list_entry);
7324
7325 add_insert_extent_rollback(rollback, fcb, newext);
7326
7327 remove_fcb_extent(fcb, ext, rollback);
7328 } else if (start_data <= ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace beginning
7329 EXTENT_DATA *ned, *nedb;
7330 EXTENT_DATA2* ned2;
7331 extent *newext1, *newext2;
7332
7333 ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
7334 if (!ned) {
7335 ERR("out of memory\n");
7336 return STATUS_INSUFFICIENT_RESOURCES;
7337 }
7338
7339 nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
7340 if (!nedb) {
7341 ERR("out of memory\n");
7342 ExFreePool(ned);
7343 return STATUS_INSUFFICIENT_RESOURCES;
7344 }
7345
7346 newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
7347 if (!newext1) {
7348 ERR("out of memory\n");
7349 ExFreePool(ned);
7350 ExFreePool(nedb);
7351 return STATUS_INSUFFICIENT_RESOURCES;
7352 }
7353
7354 newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
7355 if (!newext2) {
7356 ERR("out of memory\n");
7357 ExFreePool(ned);
7358 ExFreePool(nedb);
7359 ExFreePool(newext1);
7360 return STATUS_INSUFFICIENT_RESOURCES;
7361 }
7362
7363 RtlCopyMemory(ned, ext->data, ext->datalen);
7364 ned->type = EXTENT_TYPE_REGULAR;
7365 ned2 = (EXTENT_DATA2*)ned->data;
7366 ned2->num_bytes = end_data - ext->offset;
7367
7368 RtlCopyMemory(nedb, ext->data, ext->datalen);
7369 ned2 = (EXTENT_DATA2*)nedb->data;
7370 ned2->offset += end_data - ext->offset;
7371 ned2->num_bytes -= end_data - ext->offset;
7372
7373 Status = do_write_data(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, end_data - ext->offset, changed_sector_list, Irp);
7374 if (!NT_SUCCESS(Status)) {
7375 ERR("do_write_data returned %08x\n", Status);
7376 return Status;
7377 }
7378
7379 *written = end_data - ext->offset;
7380
7381 newext1->offset = ext->offset;
7382 newext1->data = ned;
7383 newext1->datalen = ext->datalen;
7384 newext1->unique = FALSE;
7385 newext1->ignore = FALSE;
7386 InsertHeadList(&ext->list_entry, &newext1->list_entry);
7387
7388 add_insert_extent_rollback(rollback, fcb, newext1);
7389
7390 newext2->offset = end_data;
7391 newext2->data = nedb;
7392 newext2->datalen = ext->datalen;
7393 newext2->unique = FALSE;
7394 newext2->ignore = FALSE;
7395 InsertHeadList(&newext1->list_entry, &newext2->list_entry);
7396
7397 add_insert_extent_rollback(rollback, fcb, newext2);
7398
7399 c = get_chunk_from_address(fcb->Vcb, ed2->address);
7400
7401 if (!c)
7402 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
7403 else {
7404 Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
7405 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
7406
7407 if (!NT_SUCCESS(Status)) {
7408 ERR("update_changed_extent_ref returned %08x\n", Status);
7409 return Status;
7410 }
7411 }
7412
7413 remove_fcb_extent(fcb, ext, rollback);
7414 } else if (start_data > ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace end
7415 EXTENT_DATA *ned, *nedb;
7416 EXTENT_DATA2* ned2;
7417 extent *newext1, *newext2;
7418
7419 ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
7420 if (!ned) {
7421 ERR("out of memory\n");
7422 return STATUS_INSUFFICIENT_RESOURCES;
7423 }
7424
7425 nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
7426 if (!nedb) {
7427 ERR("out of memory\n");
7428 ExFreePool(ned);
7429 return STATUS_INSUFFICIENT_RESOURCES;
7430 }
7431
7432 newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
7433 if (!newext1) {
7434 ERR("out of memory\n");
7435 ExFreePool(ned);
7436 ExFreePool(nedb);
7437 return STATUS_INSUFFICIENT_RESOURCES;
7438 }
7439
7440 newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
7441 if (!newext2) {
7442 ERR("out of memory\n");
7443 ExFreePool(ned);
7444 ExFreePool(nedb);
7445 ExFreePool(newext1);
7446 return STATUS_INSUFFICIENT_RESOURCES;
7447 }
7448
7449 RtlCopyMemory(ned, ext->data, ext->datalen);
7450
7451 ned2 = (EXTENT_DATA2*)ned->data;
7452 ned2->num_bytes = start_data - ext->offset;
7453
7454 RtlCopyMemory(nedb, ext->data, ext->datalen);
7455
7456 nedb->type = EXTENT_TYPE_REGULAR;
7457 ned2 = (EXTENT_DATA2*)nedb->data;
7458 ned2->offset += start_data - ext->offset;
7459 ned2->num_bytes = ext->offset + ed2->num_bytes - start_data;
7460
7461 Status = do_write_data(fcb->Vcb, ed2->address + ned2->offset, data, ned2->num_bytes, changed_sector_list, Irp);
7462 if (!NT_SUCCESS(Status)) {
7463 ERR("do_write_data returned %08x\n", Status);
7464 return Status;
7465 }
7466
7467 *written = ned2->num_bytes;
7468
7469 newext1->offset = ext->offset;
7470 newext1->data = ned;
7471 newext1->datalen = ext->datalen;
7472 newext1->unique = FALSE;
7473 newext1->ignore = FALSE;
7474 InsertHeadList(&ext->list_entry, &newext1->list_entry);
7475
7476 add_insert_extent_rollback(rollback, fcb, newext1);
7477
7478 newext2->offset = start_data;
7479 newext2->data = nedb;
7480 newext2->datalen = ext->datalen;
7481 newext2->unique = FALSE;
7482 newext2->ignore = FALSE;
7483 InsertHeadList(&newext1->list_entry, &newext2->list_entry);
7484
7485 add_insert_extent_rollback(rollback, fcb, newext2);
7486
7487 c = get_chunk_from_address(fcb->Vcb, ed2->address);
7488
7489 if (!c)
7490 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
7491 else {
7492 Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
7493 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
7494
7495 if (!NT_SUCCESS(Status)) {
7496 ERR("update_changed_extent_ref returned %08x\n", Status);
7497 return Status;
7498 }
7499 }
7500
7501 remove_fcb_extent(fcb, ext, rollback);
7502 } else if (start_data > ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace middle
7503 EXTENT_DATA *ned, *nedb, *nedc;
7504 EXTENT_DATA2* ned2;
7505 extent *newext1, *newext2, *newext3;
7506
7507 ned = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
7508 if (!ned) {
7509 ERR("out of memory\n");
7510 return STATUS_INSUFFICIENT_RESOURCES;
7511 }
7512
7513 nedb = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
7514 if (!nedb) {
7515 ERR("out of memory\n");
7516 ExFreePool(ned);
7517 return STATUS_INSUFFICIENT_RESOURCES;
7518 }
7519
7520 nedc = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG);
7521 if (!nedb) {
7522 ERR("out of memory\n");
7523 ExFreePool(ned);
7524 ExFreePool(nedb);
7525 return STATUS_INSUFFICIENT_RESOURCES;
7526 }
7527
7528 newext1 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
7529 if (!newext1) {
7530 ERR("out of memory\n");
7531 ExFreePool(ned);
7532 ExFreePool(nedb);
7533 ExFreePool(nedc);
7534 return STATUS_INSUFFICIENT_RESOURCES;
7535 }
7536
7537 newext2 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
7538 if (!newext2) {
7539 ERR("out of memory\n");
7540 ExFreePool(ned);
7541 ExFreePool(nedb);
7542 ExFreePool(nedc);
7543 ExFreePool(newext1);
7544 return STATUS_INSUFFICIENT_RESOURCES;
7545 }
7546
7547 newext3 = ExAllocatePoolWithTag(PagedPool, sizeof(extent), ALLOC_TAG);
7548 if (!newext2) {
7549 ERR("out of memory\n");
7550 ExFreePool(ned);
7551 ExFreePool(nedb);
7552 ExFreePool(nedc);
7553 ExFreePool(newext1);
7554 ExFreePool(newext2);
7555 return STATUS_INSUFFICIENT_RESOURCES;
7556 }
7557
7558 RtlCopyMemory(ned, ext->data, ext->datalen);
7559 RtlCopyMemory(nedb, ext->data, ext->datalen);
7560 RtlCopyMemory(nedc, ext->data, ext->datalen);
7561
7562 ned2 = (EXTENT_DATA2*)ned->data;
7563 ned2->num_bytes = start_data - ext->offset;
7564
7565 nedb->type = EXTENT_TYPE_REGULAR;
7566 ned2 = (EXTENT_DATA2*)nedb->data;
7567 ned2->offset += start_data - ext->offset;
7568 ned2->num_bytes = end_data - start_data;
7569
7570 ned2 = (EXTENT_DATA2*)nedc->data;
7571 ned2->offset += end_data - ext->offset;
7572 ned2->num_bytes -= end_data - ext->offset;
7573
7574 ned2 = (EXTENT_DATA2*)nedb->data;
7575 Status = do_write_data(fcb->Vcb, ed2->address + ned2->offset, data, end_data - start_data, changed_sector_list, Irp);
7576 if (!NT_SUCCESS(Status)) {
7577 ERR("do_write_data returned %08x\n", Status);
7578 return Status;
7579 }
7580
7581 *written = end_data - start_data;
7582
7583 newext1->offset = ext->offset;
7584 newext1->data = ned;
7585 newext1->datalen = ext->datalen;
7586 newext1->unique = FALSE;
7587 newext1->ignore = FALSE;
7588 InsertHeadList(&ext->list_entry, &newext1->list_entry);
7589
7590 add_insert_extent_rollback(rollback, fcb, newext1);
7591
7592 newext2->offset = start_data;
7593 newext2->data = nedb;
7594 newext2->datalen = ext->datalen;
7595 newext2->unique = FALSE;
7596 newext2->ignore = FALSE;
7597 InsertHeadList(&newext1->list_entry, &newext2->list_entry);
7598
7599 add_insert_extent_rollback(rollback, fcb, newext2);
7600
7601 newext3->offset = end_data;
7602 newext3->data = nedc;
7603 newext3->datalen = ext->datalen;
7604 newext3->unique = FALSE;
7605 newext3->ignore = FALSE;
7606 InsertHeadList(&newext2->list_entry, &newext3->list_entry);
7607
7608 add_insert_extent_rollback(rollback, fcb, newext3);
7609
7610 c = get_chunk_from_address(fcb->Vcb, ed2->address);
7611
7612 if (!c)
7613 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
7614 else {
7615 Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 2,
7616 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, ed2->size, Irp);
7617
7618 if (!NT_SUCCESS(Status)) {
7619 ERR("update_changed_extent_ref returned %08x\n", Status);
7620 return Status;
7621 }
7622 }
7623
7624 remove_fcb_extent(fcb, ext, rollback);
7625 }
7626
7627 return STATUS_SUCCESS;
7628 }
7629
7630 NTSTATUS do_write_file(fcb* fcb, UINT64 start, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
7631 NTSTATUS Status;
7632 LIST_ENTRY *le, *le2;
7633 UINT64 written = 0, length = end_data - start;
7634 UINT64 last_cow_start;
7635 #ifdef DEBUG_PARANOID
7636 UINT64 last_off;
7637 #endif
7638
7639 last_cow_start = 0;
7640
7641 le = fcb->extents.Flink;
7642 while (le != &fcb->extents) {
7643 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
7644
7645 le2 = le->Flink;
7646
7647 if (!ext->ignore) {
7648 EXTENT_DATA* ed = ext->data;
7649 EXTENT_DATA2* ed2 = ed->type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ed->data;
7650 UINT64 len;
7651 BOOL nocow;
7652
7653 len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
7654
7655 if (ext->offset + len <= start)
7656 goto nextitem;
7657
7658 if (ext->offset > start + written + length)
7659 break;
7660
7661 nocow = (ext->unique && fcb->inode_item.flags & BTRFS_INODE_NODATACOW) || ed->type == EXTENT_TYPE_PREALLOC;
7662
7663 if (nocow) {
7664 if (max(last_cow_start, start + written) < ext->offset) {
7665 UINT64 start_write = max(last_cow_start, start + written);
7666
7667 Status = excise_extents(fcb->Vcb, fcb, start_write, ext->offset, Irp, rollback);
7668 if (!NT_SUCCESS(Status)) {
7669 ERR("excise_extents returned %08x\n", Status);
7670 return Status;
7671 }
7672
7673 Status = insert_extent(fcb->Vcb, fcb, start_write, ext->offset - start_write, data, changed_sector_list, Irp, rollback);
7674 if (!NT_SUCCESS(Status)) {
7675 ERR("insert_extent returned %08x\n", Status);
7676 return Status;
7677 }
7678
7679 written += ext->offset - start_write;
7680 length -= ext->offset - start_write;
7681
7682 if (length == 0)
7683 break;
7684 }
7685
7686 if (ed->type == EXTENT_TYPE_REGULAR) {
7687 UINT64 writeaddr = ed2->address + ed2->offset + start + written - ext->offset;
7688 UINT64 write_len = min(len, length);
7689
7690 TRACE("doing non-COW write to %llx\n", writeaddr);
7691
7692 Status = write_data_complete(fcb->Vcb, writeaddr, (UINT8*)data + written, write_len, Irp, NULL);
7693 if (!NT_SUCCESS(Status)) {
7694 ERR("write_data_complete returned %08x\n", Status);
7695 return Status;
7696 }
7697
7698 if (changed_sector_list) {
7699 unsigned int i;
7700 changed_sector* sc;
7701
7702 sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
7703 if (!sc) {
7704 ERR("out of memory\n");
7705 return STATUS_INSUFFICIENT_RESOURCES;
7706 }
7707
7708 sc->ol.key = writeaddr;
7709 sc->length = write_len / fcb->Vcb->superblock.sector_size;
7710 sc->deleted = FALSE;
7711
7712 sc->checksums = ExAllocatePoolWithTag(PagedPool, sizeof(UINT32) * sc->length, ALLOC_TAG);
7713 if (!sc->checksums) {
7714 ERR("out of memory\n");
7715 ExFreePool(sc);
7716 return STATUS_INSUFFICIENT_RESOURCES;
7717 }
7718
7719 for (i = 0; i < sc->length; i++) {
7720 sc->checksums[i] = ~calc_crc32c(0xffffffff, (UINT8*)data + written + (i * fcb->Vcb->superblock.sector_size), fcb->Vcb->superblock.sector_size);
7721 }
7722
7723 insert_into_ordered_list(changed_sector_list, &sc->ol);
7724 }
7725
7726 written += write_len;
7727 length -= write_len;
7728
7729 if (length == 0)
7730 break;
7731 } else if (ed->type == EXTENT_TYPE_PREALLOC) {
7732 UINT64 write_len;
7733
7734 Status = do_write_file_prealloc(fcb, ext, start + written, end_data, (UINT8*)data + written, &write_len,
7735 changed_sector_list, Irp, rollback);
7736 if (!NT_SUCCESS(Status)) {
7737 ERR("do_write_file_prealloc returned %08x\n", Status);
7738 return Status;
7739 }
7740
7741 written += write_len;
7742 length -= write_len;
7743
7744 if (length == 0)
7745 break;
7746 }
7747
7748 last_cow_start = ext->offset + len;
7749 }
7750 }
7751
7752 nextitem:
7753 le = le2;
7754 }
7755
7756 if (length > 0) {
7757 UINT64 start_write = max(last_cow_start, start + written);
7758
7759 Status = excise_extents(fcb->Vcb, fcb, start_write, end_data, Irp, rollback);
7760 if (!NT_SUCCESS(Status)) {
7761 ERR("excise_extents returned %08x\n", Status);
7762 return Status;
7763 }
7764
7765 Status = insert_extent(fcb->Vcb, fcb, start_write, end_data - start_write, data, changed_sector_list, Irp, rollback);
7766 if (!NT_SUCCESS(Status)) {
7767 ERR("insert_extent returned %08x\n", Status);
7768 return Status;
7769 }
7770 }
7771
7772 // FIXME - make extending work again (here?)
7773 // FIXME - make maximum extent size 128 MB again (here?)
7774
7775 #ifdef DEBUG_PARANOID
7776 last_off = 0xffffffffffffffff;
7777
7778 le = fcb->extents.Flink;
7779 while (le != &fcb->extents) {
7780 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
7781
7782 if (!ext->ignore) {
7783 if (ext->offset == last_off) {
7784 ERR("offset %llx duplicated\n", ext->offset);
7785 int3;
7786 } else if (ext->offset < last_off && last_off != 0xffffffffffffffff) {
7787 ERR("offsets out of order\n");
7788 int3;
7789 }
7790
7791 last_off = ext->offset;
7792 }
7793
7794 le = le->Flink;
7795 }
7796 #endif
7797
7798 fcb->extents_changed = TRUE;
7799 mark_fcb_dirty(fcb);
7800
7801 return STATUS_SUCCESS;
7802 }
7803
7804 NTSTATUS write_compressed(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, LIST_ENTRY* changed_sector_list, PIRP Irp, LIST_ENTRY* rollback) {
7805 NTSTATUS Status;
7806 UINT64 i;
7807
7808 for (i = 0; i < sector_align(end_data - start_data, COMPRESSED_EXTENT_SIZE) / COMPRESSED_EXTENT_SIZE; i++) {
7809 UINT64 s2, e2;
7810 BOOL compressed;
7811
7812 s2 = start_data + (i * COMPRESSED_EXTENT_SIZE);
7813 e2 = min(s2 + COMPRESSED_EXTENT_SIZE, end_data);
7814
7815 Status = write_compressed_bit(fcb, s2, e2, (UINT8*)data + (i * COMPRESSED_EXTENT_SIZE), &compressed, changed_sector_list, Irp, rollback);
7816
7817 if (!NT_SUCCESS(Status)) {
7818 ERR("write_compressed_bit returned %08x\n", Status);
7819 return Status;
7820 }
7821
7822 // If the first 128 KB of a file is incompressible, we set the nocompress flag so we don't
7823 // bother with the rest of it.
7824 if (s2 == 0 && e2 == COMPRESSED_EXTENT_SIZE && !compressed && !fcb->Vcb->options.compress_force) {
7825 fcb->inode_item.flags |= BTRFS_INODE_NOCOMPRESS;
7826 mark_fcb_dirty(fcb);
7827
7828 // write subsequent data non-compressed
7829 if (e2 < end_data) {
7830 Status = do_write_file(fcb, e2, end_data, (UINT8*)data + e2, changed_sector_list, Irp, rollback);
7831
7832 if (!NT_SUCCESS(Status)) {
7833 ERR("do_write_file returned %08x\n", Status);
7834 return Status;
7835 }
7836 }
7837
7838 return STATUS_SUCCESS;
7839 }
7840 }
7841
7842 return STATUS_SUCCESS;
7843 }
7844
7845 NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOL paging_io, BOOL no_cache,
7846 BOOL wait, BOOL deferred_write, LIST_ENTRY* rollback) {
7847 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
7848 PFILE_OBJECT FileObject = IrpSp->FileObject;
7849 EXTENT_DATA* ed2;
7850 UINT64 newlength, start_data, end_data;
7851 UINT32 bufhead;
7852 BOOL make_inline;
7853 UINT8* data;
7854 LIST_ENTRY changed_sector_list;
7855 INODE_ITEM* origii;
7856 BOOL changed_length = FALSE, nocsum/*, lazy_writer = FALSE, write_eof = FALSE*/;
7857 NTSTATUS Status;
7858 LARGE_INTEGER time;
7859 BTRFS_TIME now;
7860 fcb* fcb;
7861 ccb* ccb;
7862 file_ref* fileref;
7863 BOOL paging_lock = FALSE, fcb_lock = FALSE, tree_lock = FALSE, pagefile;
7864 ULONG filter = 0;
7865
7866 TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb, FileObject, offset.QuadPart, buf, *length, paging_io, no_cache);
7867
7868 if (*length == 0) {
7869 WARN("returning success for zero-length write\n");
7870 return STATUS_SUCCESS;
7871 }
7872
7873 if (!FileObject) {
7874 ERR("error - FileObject was NULL\n");
7875 return STATUS_ACCESS_DENIED;
7876 }
7877
7878 fcb = FileObject->FsContext;
7879 ccb = FileObject->FsContext2;
7880 fileref = ccb ? ccb->fileref : NULL;
7881
7882 if (fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK) {
7883 WARN("tried to write to something other than a file or symlink (inode %llx, type %u, %p, %p)\n", fcb->inode, fcb->type, &fcb->type, fcb);
7884 return STATUS_INVALID_DEVICE_REQUEST;
7885 }
7886
7887 if (offset.LowPart == FILE_WRITE_TO_END_OF_FILE && offset.HighPart == -1) {
7888 offset = fcb->Header.FileSize;
7889 // write_eof = TRUE;
7890 }
7891
7892 TRACE("fcb->Header.Flags = %x\n", fcb->Header.Flags);
7893
7894 if (!no_cache && !CcCanIWrite(FileObject, *length, wait, deferred_write))
7895 return STATUS_PENDING;
7896
7897 if (!wait && no_cache)
7898 return STATUS_PENDING;
7899
7900 if (no_cache && !paging_io && FileObject->SectionObjectPointer->DataSectionObject) {
7901 IO_STATUS_BLOCK iosb;
7902
7903 ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
7904
7905 CcFlushCache(FileObject->SectionObjectPointer, &offset, *length, &iosb);
7906
7907 if (!NT_SUCCESS(iosb.Status)) {
7908 ExReleaseResourceLite(fcb->Header.PagingIoResource);
7909 ERR("CcFlushCache returned %08x\n", iosb.Status);
7910 return iosb.Status;
7911 }
7912
7913 paging_lock = TRUE;
7914
7915 CcPurgeCacheSection(FileObject->SectionObjectPointer, &offset, *length, FALSE);
7916 }
7917
7918 if (paging_io) {
7919 if (!ExAcquireResourceSharedLite(fcb->Header.PagingIoResource, wait)) {
7920 Status = STATUS_PENDING;
7921 goto end;
7922 } else
7923 paging_lock = TRUE;
7924 }
7925
7926 pagefile = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE && paging_io;
7927
7928 if (!pagefile && !ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
7929 if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) {
7930 Status = STATUS_PENDING;
7931 goto end;
7932 } else
7933 tree_lock = TRUE;
7934 }
7935
7936 if (no_cache && !ExIsResourceAcquiredExclusiveLite(fcb->Header.Resource)) {
7937 if (!ExAcquireResourceExclusiveLite(fcb->Header.Resource, wait)) {
7938 Status = STATUS_PENDING;
7939 goto end;
7940 } else
7941 fcb_lock = TRUE;
7942 }
7943
7944 nocsum = fcb->ads ? TRUE : fcb->inode_item.flags & BTRFS_INODE_NODATASUM;
7945
7946 newlength = fcb->ads ? fcb->adsdata.Length : fcb->inode_item.st_size;
7947
7948 if (fcb->deleted)
7949 newlength = 0;
7950
7951 TRACE("newlength = %llx\n", newlength);
7952
7953 // if (KeGetCurrentThread() == fcb->lazy_writer_thread) {
7954 // ERR("lazy writer on the TV\n");
7955 // lazy_writer = TRUE;
7956 // }
7957
7958 if (offset.QuadPart + *length > newlength) {
7959 if (paging_io) {
7960 if (offset.QuadPart >= newlength) {
7961 TRACE("paging IO tried to write beyond end of file (file size = %llx, offset = %llx, length = %x)\n", newlength, offset.QuadPart, *length);
7962 TRACE("filename %S\n", file_desc(FileObject));
7963 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
7964 fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
7965 Status = STATUS_SUCCESS;
7966 goto end;
7967 }
7968
7969 *length = newlength - offset.QuadPart;
7970 } else {
7971 newlength = offset.QuadPart + *length;
7972 changed_length = TRUE;
7973
7974 TRACE("extending length to %llx\n", newlength);
7975 }
7976 }
7977
7978 make_inline = fcb->ads ? FALSE : newlength <= fcb->Vcb->options.max_inline;
7979
7980 if (changed_length) {
7981 if (newlength > fcb->Header.AllocationSize.QuadPart) {
7982 if (!tree_lock) {
7983 // We need to acquire the tree lock if we don't have it already -
7984 // we can't give an inline file proper extents at the same as we're
7985 // doing a flush.
7986 if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) {
7987 Status = STATUS_PENDING;
7988 goto end;
7989 } else
7990 tree_lock = TRUE;
7991 }
7992
7993 Status = extend_file(fcb, fileref, newlength, FALSE, Irp, rollback);
7994 if (!NT_SUCCESS(Status)) {
7995 ERR("extend_file returned %08x\n", Status);
7996 goto end;
7997 }
7998 } else if (!fcb->ads)
7999 fcb->inode_item.st_size = newlength;
8000
8001 fcb->Header.FileSize.QuadPart = newlength;
8002 fcb->Header.ValidDataLength.QuadPart = newlength;
8003
8004 TRACE("AllocationSize = %llx\n", fcb->Header.AllocationSize.QuadPart);
8005 TRACE("FileSize = %llx\n", fcb->Header.FileSize.QuadPart);
8006 TRACE("ValidDataLength = %llx\n", fcb->Header.ValidDataLength.QuadPart);
8007 }
8008
8009 if (!no_cache) {
8010 if (!FileObject->PrivateCacheMap || changed_length) {
8011 CC_FILE_SIZES ccfs;
8012
8013 ccfs.AllocationSize = fcb->Header.AllocationSize;
8014 ccfs.FileSize = fcb->Header.FileSize;
8015 ccfs.ValidDataLength = fcb->Header.ValidDataLength;
8016
8017 if (!FileObject->PrivateCacheMap) {
8018 TRACE("calling CcInitializeCacheMap...\n");
8019 CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, FileObject);
8020
8021 CcSetReadAheadGranularity(FileObject, READ_AHEAD_GRANULARITY);
8022 }
8023
8024 CcSetFileSizes(FileObject, &ccfs);
8025 }
8026
8027 if (IrpSp->MinorFunction & IRP_MN_MDL) {
8028 CcPrepareMdlWrite(FileObject, &offset, *length, &Irp->MdlAddress, &Irp->IoStatus);
8029
8030 Status = Irp->IoStatus.Status;
8031 goto end;
8032 } else {
8033 TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject, offset.QuadPart, *length, wait, buf);
8034 if (!CcCopyWrite(FileObject, &offset, *length, wait, buf)) {
8035 Status = STATUS_PENDING;
8036 goto end;
8037 }
8038 TRACE("CcCopyWrite finished\n");
8039 }
8040
8041 Status = STATUS_SUCCESS;
8042 goto end;
8043 }
8044
8045 if (fcb->ads) {
8046 if (changed_length) {
8047 char* data2;
8048
8049 if (newlength > fcb->adsmaxlen) {
8050 ERR("error - xattr too long (%llu > %u)\n", newlength, fcb->adsmaxlen);
8051 Status = STATUS_DISK_FULL;
8052 goto end;
8053 }
8054
8055 data2 = ExAllocatePoolWithTag(PagedPool, newlength, ALLOC_TAG);
8056 if (!data2) {
8057 ERR("out of memory\n");
8058 Status = STATUS_INSUFFICIENT_RESOURCES;
8059 goto end;
8060 }
8061
8062 if (fcb->adsdata.Buffer) {
8063 RtlCopyMemory(data2, fcb->adsdata.Buffer, fcb->adsdata.Length);
8064 ExFreePool(fcb->adsdata.Buffer);
8065 }
8066
8067 if (newlength > fcb->adsdata.Length)
8068 RtlZeroMemory(&data2[fcb->adsdata.Length], newlength - fcb->adsdata.Length);
8069
8070
8071 fcb->adsdata.Buffer = data2;
8072 fcb->adsdata.Length = fcb->adsdata.MaximumLength = newlength;
8073
8074 fcb->Header.AllocationSize.QuadPart = newlength;
8075 fcb->Header.FileSize.QuadPart = newlength;
8076 fcb->Header.ValidDataLength.QuadPart = newlength;
8077 }
8078
8079 if (*length > 0)
8080 RtlCopyMemory(&fcb->adsdata.Buffer[offset.QuadPart], buf, *length);
8081
8082 fcb->Header.ValidDataLength.QuadPart = newlength;
8083
8084 mark_fcb_dirty(fcb);
8085
8086 if (fileref)
8087 mark_fileref_dirty(fileref);
8088 } else {
8089 BOOL compress = write_fcb_compressed(fcb);
8090
8091 if (make_inline) {
8092 start_data = 0;
8093 end_data = sector_align(newlength, fcb->Vcb->superblock.sector_size);
8094 bufhead = sizeof(EXTENT_DATA) - 1;
8095 } else if (compress) {
8096 start_data = offset.QuadPart & ~(UINT64)(COMPRESSED_EXTENT_SIZE - 1);
8097 end_data = min(sector_align(offset.QuadPart + *length, COMPRESSED_EXTENT_SIZE),
8098 sector_align(newlength, fcb->Vcb->superblock.sector_size));
8099 bufhead = 0;
8100 } else {
8101 start_data = offset.QuadPart & ~(UINT64)(fcb->Vcb->superblock.sector_size - 1);
8102 end_data = sector_align(offset.QuadPart + *length, fcb->Vcb->superblock.sector_size);
8103 bufhead = 0;
8104 }
8105
8106 fcb->Header.ValidDataLength.QuadPart = newlength;
8107 TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart);
8108
8109 data = ExAllocatePoolWithTag(PagedPool, end_data - start_data + bufhead, ALLOC_TAG);
8110 if (!data) {
8111 ERR("out of memory\n");
8112 Status = STATUS_INSUFFICIENT_RESOURCES;
8113 goto end;
8114 }
8115
8116 RtlZeroMemory(data + bufhead, end_data - start_data);
8117
8118 TRACE("start_data = %llx\n", start_data);
8119 TRACE("end_data = %llx\n", end_data);
8120
8121 if (offset.QuadPart > start_data || offset.QuadPart + *length < end_data) {
8122 if (changed_length) {
8123 if (fcb->inode_item.st_size > start_data)
8124 Status = read_file(fcb, data + bufhead, start_data, fcb->inode_item.st_size - start_data, NULL, Irp);
8125 else
8126 Status = STATUS_SUCCESS;
8127 } else
8128 Status = read_file(fcb, data + bufhead, start_data, end_data - start_data, NULL, Irp);
8129
8130 if (!NT_SUCCESS(Status)) {
8131 ERR("read_file returned %08x\n", Status);
8132 ExFreePool(data);
8133 goto end;
8134 }
8135 }
8136
8137 RtlCopyMemory(data + bufhead + offset.QuadPart - start_data, buf, *length);
8138
8139 if (!nocsum)
8140 InitializeListHead(&changed_sector_list);
8141
8142 if (make_inline) {
8143 Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback);
8144 if (!NT_SUCCESS(Status)) {
8145 ERR("error - excise_extents returned %08x\n", Status);
8146 ExFreePool(data);
8147 goto end;
8148 }
8149
8150 ed2 = (EXTENT_DATA*)data;
8151 ed2->generation = fcb->Vcb->superblock.generation;
8152 ed2->decoded_size = newlength;
8153 ed2->compression = BTRFS_COMPRESSION_NONE;
8154 ed2->encryption = BTRFS_ENCRYPTION_NONE;
8155 ed2->encoding = BTRFS_ENCODING_NONE;
8156 ed2->type = EXTENT_TYPE_INLINE;
8157
8158 if (!add_extent_to_fcb(fcb, 0, ed2, sizeof(EXTENT_DATA) - 1 + newlength, FALSE, rollback)) {
8159 ERR("add_extent_to_fcb failed\n");
8160 ExFreePool(data);
8161 Status = STATUS_INTERNAL_ERROR;
8162 goto end;
8163 }
8164
8165 fcb->inode_item.st_blocks += newlength;
8166 } else if (compress) {
8167 Status = write_compressed(fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
8168
8169 if (!NT_SUCCESS(Status)) {
8170 ERR("write_compressed returned %08x\n", Status);
8171 ExFreePool(data);
8172 goto end;
8173 }
8174
8175 ExFreePool(data);
8176 } else {
8177 Status = do_write_file(fcb, start_data, end_data, data, nocsum ? NULL : &changed_sector_list, Irp, rollback);
8178
8179 if (!NT_SUCCESS(Status)) {
8180 ERR("do_write_file returned %08x\n", Status);
8181 ExFreePool(data);
8182 goto end;
8183 }
8184
8185 ExFreePool(data);
8186 }
8187 }
8188
8189 if (!pagefile) {
8190 KeQuerySystemTime(&time);
8191 win_time_to_unix(time, &now);
8192
8193 // ERR("no_cache = %s, FileObject->PrivateCacheMap = %p\n", no_cache ? "TRUE" : "FALSE", FileObject->PrivateCacheMap);
8194 //
8195 // if (!no_cache) {
8196 // if (!FileObject->PrivateCacheMap) {
8197 // CC_FILE_SIZES ccfs;
8198 //
8199 // ccfs.AllocationSize = fcb->Header.AllocationSize;
8200 // ccfs.FileSize = fcb->Header.FileSize;
8201 // ccfs.ValidDataLength = fcb->Header.ValidDataLength;
8202 //
8203 // TRACE("calling CcInitializeCacheMap...\n");
8204 // CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
8205 //
8206 // changed_length = FALSE;
8207 // }
8208 // }
8209
8210 if (fcb->ads) {
8211 if (fileref && fileref->parent)
8212 origii = &fileref->parent->fcb->inode_item;
8213 else {
8214 ERR("no parent fcb found for stream\n");
8215 Status = STATUS_INTERNAL_ERROR;
8216 goto end;
8217 }
8218 } else
8219 origii = &fcb->inode_item;
8220
8221 origii->transid = Vcb->superblock.generation;
8222 origii->sequence++;
8223 origii->st_ctime = now;
8224
8225 if (!fcb->ads) {
8226 if (changed_length) {
8227 TRACE("setting st_size to %llx\n", newlength);
8228 origii->st_size = newlength;
8229 filter |= FILE_NOTIFY_CHANGE_SIZE;
8230 }
8231
8232 origii->st_mtime = now;
8233 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
8234 }
8235
8236 mark_fcb_dirty(fcb->ads ? fileref->parent->fcb : fcb);
8237 }
8238
8239 if (!nocsum) {
8240 ExAcquireResourceExclusiveLite(&Vcb->checksum_lock, TRUE);
8241 commit_checksum_changes(Vcb, &changed_sector_list);
8242 ExReleaseResourceLite(&Vcb->checksum_lock);
8243 }
8244
8245 if (changed_length) {
8246 CC_FILE_SIZES ccfs;
8247
8248 ccfs.AllocationSize = fcb->Header.AllocationSize;
8249 ccfs.FileSize = fcb->Header.FileSize;
8250 ccfs.ValidDataLength = fcb->Header.ValidDataLength;
8251
8252 CcSetFileSizes(FileObject, &ccfs);
8253 }
8254
8255 // FIXME - make sure this still called if STATUS_PENDING and async
8256 // if (!no_cache) {
8257 // if (!CcCopyWrite(FileObject, &offset, *length, TRUE, buf)) {
8258 // ERR("CcCopyWrite failed.\n");
8259 // }
8260 // }
8261
8262 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
8263 fcb->subvol->root_item.ctime = now;
8264
8265 Status = STATUS_SUCCESS;
8266
8267 if (filter != 0)
8268 send_notification_fcb(fcb->ads ? fileref->parent : fileref, filter, FILE_ACTION_MODIFIED);
8269
8270 end:
8271 if (NT_SUCCESS(Status) && FileObject->Flags & FO_SYNCHRONOUS_IO && !paging_io) {
8272 TRACE("CurrentByteOffset was: %llx\n", FileObject->CurrentByteOffset.QuadPart);
8273 FileObject->CurrentByteOffset.QuadPart = offset.QuadPart + (NT_SUCCESS(Status) ? *length : 0);
8274 TRACE("CurrentByteOffset now: %llx\n", FileObject->CurrentByteOffset.QuadPart);
8275 }
8276
8277 if (fcb_lock)
8278 ExReleaseResourceLite(fcb->Header.Resource);
8279
8280 if (tree_lock)
8281 ExReleaseResourceLite(&Vcb->tree_lock);
8282
8283 if (paging_lock)
8284 ExReleaseResourceLite(fcb->Header.PagingIoResource);
8285
8286 return Status;
8287 }
8288
8289 NTSTATUS write_file(device_extension* Vcb, PIRP Irp, BOOL wait, BOOL deferred_write) {
8290 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
8291 void* buf;
8292 NTSTATUS Status;
8293 LARGE_INTEGER offset = IrpSp->Parameters.Write.ByteOffset;
8294 PFILE_OBJECT FileObject = IrpSp->FileObject;
8295 fcb* fcb = FileObject ? FileObject->FsContext : NULL;
8296 // BOOL locked = FALSE;
8297 // LARGE_INTEGER freq, time1, time2;
8298 LIST_ENTRY rollback;
8299
8300 InitializeListHead(&rollback);
8301
8302 // time1 = KeQueryPerformanceCounter(&freq);
8303
8304 TRACE("write\n");
8305
8306 Irp->IoStatus.Information = 0;
8307
8308 TRACE("offset = %llx\n", offset.QuadPart);
8309 TRACE("length = %x\n", IrpSp->Parameters.Write.Length);
8310
8311 if (!Irp->AssociatedIrp.SystemBuffer) {
8312 buf = map_user_buffer(Irp);
8313
8314 if (Irp->MdlAddress && !buf) {
8315 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
8316 Status = STATUS_INSUFFICIENT_RESOURCES;
8317 goto exit;
8318 }
8319 } else
8320 buf = Irp->AssociatedIrp.SystemBuffer;
8321
8322 TRACE("buf = %p\n", buf);
8323
8324 // if (Irp->Flags & IRP_NOCACHE) {
8325 // if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) {
8326 // Status = STATUS_PENDING;
8327 // goto exit;
8328 // }
8329 // locked = TRUE;
8330 // }
8331
8332 if (fcb && !(Irp->Flags & IRP_PAGING_IO) && !FsRtlCheckLockForWriteAccess(&fcb->lock, Irp)) {
8333 WARN("tried to write to locked region\n");
8334 Status = STATUS_FILE_LOCK_CONFLICT;
8335 goto exit;
8336 }
8337
8338 // ERR("Irp->Flags = %x\n", Irp->Flags);
8339 Status = write_file2(Vcb, Irp, offset, buf, &IrpSp->Parameters.Write.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE,
8340 wait, deferred_write, &rollback);
8341
8342 if (Status == STATUS_PENDING)
8343 goto exit;
8344 else if (!NT_SUCCESS(Status)) {
8345 ERR("write_file2 returned %08x\n", Status);
8346 goto exit;
8347 }
8348
8349 // if (locked)
8350 // Status = consider_write(Vcb);
8351
8352 if (NT_SUCCESS(Status)) {
8353 Irp->IoStatus.Information = IrpSp->Parameters.Write.Length;
8354
8355 #ifdef DEBUG_PARANOID
8356 // if (locked)
8357 // check_extents_consistent(Vcb, FileObject->FsContext); // TESTING
8358
8359 // check_extent_tree_consistent(Vcb);
8360 #endif
8361 }
8362
8363 exit:
8364 // if (locked) {
8365 if (NT_SUCCESS(Status))
8366 clear_rollback(&rollback);
8367 else
8368 do_rollback(Vcb, &rollback);
8369 //
8370 // ExReleaseResourceLite(&Vcb->tree_lock);
8371 // }
8372
8373 // time2 = KeQueryPerformanceCounter(NULL);
8374
8375 // ERR("time = %u (freq = %u)\n", (UINT32)(time2.QuadPart - time1.QuadPart), (UINT32)freq.QuadPart);
8376
8377 return Status;
8378 }
8379
8380 NTSTATUS STDCALL drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
8381 NTSTATUS Status;
8382 BOOL top_level;
8383 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
8384 device_extension* Vcb = DeviceObject->DeviceExtension;
8385 PFILE_OBJECT FileObject = IrpSp->FileObject;
8386 fcb* fcb = FileObject ? FileObject->FsContext : NULL;
8387 ccb* ccb = FileObject ? FileObject->FsContext2 : NULL;
8388
8389 FsRtlEnterFileSystem();
8390
8391 top_level = is_top_level(Irp);
8392
8393 if (Vcb && Vcb->type == VCB_TYPE_PARTITION0) {
8394 Status = part0_passthrough(DeviceObject, Irp);
8395 goto exit;
8396 }
8397
8398 if (!fcb) {
8399 ERR("fcb was NULL\n");
8400 Status = STATUS_INVALID_PARAMETER;
8401 goto end;
8402 }
8403
8404 if (!ccb) {
8405 ERR("ccb was NULL\n");
8406 Status = STATUS_INVALID_PARAMETER;
8407 goto end;
8408 }
8409
8410 if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) {
8411 Status = STATUS_ACCESS_DENIED;
8412 goto end;
8413 }
8414
8415 if (Vcb->readonly) {
8416 Status = STATUS_MEDIA_WRITE_PROTECTED;
8417 goto end;
8418 }
8419
8420 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
8421 WARN("insufficient permissions\n");
8422 Status = STATUS_ACCESS_DENIED;
8423 goto end;
8424 }
8425
8426 // ERR("recursive = %s\n", Irp != IoGetTopLevelIrp() ? "TRUE" : "FALSE");
8427
8428 _SEH2_TRY {
8429 if (IrpSp->MinorFunction & IRP_MN_COMPLETE) {
8430 CcMdlWriteComplete(IrpSp->FileObject, &IrpSp->Parameters.Write.ByteOffset, Irp->MdlAddress);
8431
8432 Irp->MdlAddress = NULL;
8433 Status = STATUS_SUCCESS;
8434 } else {
8435 Status = write_file(Vcb, Irp, IoIsOperationSynchronous(Irp), FALSE);
8436 }
8437 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
8438 Status = _SEH2_GetExceptionCode();
8439 } _SEH2_END;
8440
8441 end:
8442 Irp->IoStatus.Status = Status;
8443
8444 TRACE("wrote %u bytes\n", Irp->IoStatus.Information);
8445
8446 if (Status != STATUS_PENDING)
8447 IoCompleteRequest(Irp, IO_NO_INCREMENT);
8448 else {
8449 IoMarkIrpPending(Irp);
8450
8451 if (!add_thread_job(Vcb, Irp))
8452 do_write_job(Vcb, Irp);
8453 }
8454
8455 exit:
8456 if (top_level)
8457 IoSetTopLevelIrp(NULL);
8458
8459 FsRtlExitFileSystem();
8460
8461 TRACE("returning %08x\n", Status);
8462
8463 return Status;
8464 }