xref: /reactos/drivers/filesystems/btrfs/write.c (revision 07608028)
1 /* Copyright (c) Mark Harmstone 2016-17
2  *
3  * This file is part of WinBtrfs.
4  *
5  * WinBtrfs is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public Licence as published by
7  * the Free Software Foundation, either version 3 of the Licence, or
8  * (at your option) any later version.
9  *
10  * WinBtrfs is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public Licence for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public Licence
16  * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
17 
18 #include "btrfs_drv.h"
19 
20 typedef struct {
21     UINT64 start;
22     UINT64 end;
23     UINT8* data;
24     PMDL mdl;
25     UINT64 irp_offset;
26 } write_stripe;
27 
28 _Function_class_(IO_COMPLETION_ROUTINE)
29 #ifdef __REACTOS__
30 static NTSTATUS NTAPI write_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr);
31 #else
32 static NTSTATUS write_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr);
33 #endif
34 
35 static void remove_fcb_extent(fcb* fcb, extent* ext, LIST_ENTRY* rollback);
36 
37 extern tPsUpdateDiskCounters fPsUpdateDiskCounters;
38 extern tCcCopyWriteEx fCcCopyWriteEx;
39 extern tFsRtlUpdateDiskCounters fFsRtlUpdateDiskCounters;
40 extern BOOL diskacc;
41 
42 BOOL find_data_address_in_chunk(device_extension* Vcb, chunk* c, UINT64 length, UINT64* address) {
43     LIST_ENTRY* le;
44     space* s;
45 
46     TRACE("(%p, %llx, %llx, %p)\n", Vcb, c->offset, length, address);
47 
48     if (length > c->chunk_item->size - c->used)
49         return FALSE;
50 
51     if (!c->cache_loaded) {
52         NTSTATUS Status = load_cache_chunk(Vcb, c, NULL);
53 
54         if (!NT_SUCCESS(Status)) {
55             ERR("load_cache_chunk returned %08x\n", Status);
56             return FALSE;
57         }
58     }
59 
60     if (IsListEmpty(&c->space_size))
61         return FALSE;
62 
63     le = c->space_size.Flink;
64     while (le != &c->space_size) {
65         s = CONTAINING_RECORD(le, space, list_entry_size);
66 
67         if (s->size == length) {
68             *address = s->address;
69             return TRUE;
70         } else if (s->size < length) {
71             if (le == c->space_size.Flink)
72                 return FALSE;
73 
74             s = CONTAINING_RECORD(le->Blink, space, list_entry_size);
75 
76             *address = s->address;
77             return TRUE;
78         }
79 
80         le = le->Flink;
81     }
82 
83     s = CONTAINING_RECORD(c->space_size.Blink, space, list_entry_size);
84 
85     if (s->size > length) {
86         *address = s->address;
87         return TRUE;
88     }
89 
90     return FALSE;
91 }
92 
93 chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address) {
94     LIST_ENTRY* le2;
95 
96     ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE);
97 
98     le2 = Vcb->chunks.Flink;
99     while (le2 != &Vcb->chunks) {
100         chunk* c = CONTAINING_RECORD(le2, chunk, list_entry);
101 
102         if (address >= c->offset && address < c->offset + c->chunk_item->size) {
103             ExReleaseResourceLite(&Vcb->chunk_lock);
104             return c;
105         }
106 
107         le2 = le2->Flink;
108     }
109 
110     ExReleaseResourceLite(&Vcb->chunk_lock);
111 
112     return NULL;
113 }
114 
115 typedef struct {
116     space* dh;
117     device* device;
118 } stripe;
119 
120 static UINT64 find_new_chunk_address(device_extension* Vcb, UINT64 size) {
121     UINT64 lastaddr;
122     LIST_ENTRY* le;
123 
124     lastaddr = 0xc00000;
125 
126     le = Vcb->chunks.Flink;
127     while (le != &Vcb->chunks) {
128         chunk* c = CONTAINING_RECORD(le, chunk, list_entry);
129 
130         if (c->offset >= lastaddr + size)
131             return lastaddr;
132 
133         lastaddr = c->offset + c->chunk_item->size;
134 
135         le = le->Flink;
136     }
137 
138     return lastaddr;
139 }
140 
141 static BOOL find_new_dup_stripes(device_extension* Vcb, stripe* stripes, UINT64 max_stripe_size, BOOL full_size) {
142     UINT64 devusage = 0xffffffffffffffff;
143     space *devdh1 = NULL, *devdh2 = NULL;
144     LIST_ENTRY* le;
145     device* dev2 = NULL;
146 
147     le = Vcb->devices.Flink;
148 
149     while (le != &Vcb->devices) {
150         device* dev = CONTAINING_RECORD(le, device, list_entry);
151 
152         if (!dev->readonly && !dev->reloc && dev->devobj) {
153             UINT64 usage = (dev->devitem.bytes_used * 4096) / dev->devitem.num_bytes;
154 
155             // favour devices which have been used the least
156             if (usage < devusage) {
157                 if (!IsListEmpty(&dev->space)) {
158                     LIST_ENTRY* le2;
159                     space *dh1 = NULL, *dh2 = NULL;
160 
161                     le2 = dev->space.Flink;
162                     while (le2 != &dev->space) {
163                         space* dh = CONTAINING_RECORD(le2, space, list_entry);
164 
165                         if (dh->size >= max_stripe_size && (!dh1 || !dh2 || dh->size < dh1->size)) {
166                             dh2 = dh1;
167                             dh1 = dh;
168                         }
169 
170                         le2 = le2->Flink;
171                     }
172 
173                     if (dh1 && (dh2 || dh1->size >= 2 * max_stripe_size)) {
174                         dev2 = dev;
175                         devusage = usage;
176                         devdh1 = dh1;
177                         devdh2 = dh2 ? dh2 : dh1;
178                     }
179                 }
180             }
181         }
182 
183         le = le->Flink;
184     }
185 
186     if (!devdh1) {
187         UINT64 size = 0;
188 
189         // Can't find hole of at least max_stripe_size; look for the largest one we can find
190 
191         if (full_size)
192             return FALSE;
193 
194         le = Vcb->devices.Flink;
195         while (le != &Vcb->devices) {
196             device* dev = CONTAINING_RECORD(le, device, list_entry);
197 
198             if (!dev->readonly && !dev->reloc) {
199                 if (!IsListEmpty(&dev->space)) {
200                     LIST_ENTRY* le2;
201                     space *dh1 = NULL, *dh2 = NULL;
202 
203                     le2 = dev->space.Flink;
204                     while (le2 != &dev->space) {
205                         space* dh = CONTAINING_RECORD(le2, space, list_entry);
206 
207                         if (!dh1 || !dh2 || dh->size < dh1->size) {
208                             dh2 = dh1;
209                             dh1 = dh;
210                         }
211 
212                         le2 = le2->Flink;
213                     }
214 
215                     if (dh1) {
216                         UINT64 devsize;
217 
218                         if (dh2)
219                             devsize = max(dh1->size / 2, min(dh1->size, dh2->size));
220                         else
221                             devsize = dh1->size / 2;
222 
223                         if (devsize > size) {
224                             dev2 = dev;
225                             devdh1 = dh1;
226 
227                             if (dh2 && min(dh1->size, dh2->size) > dh1->size / 2)
228                                 devdh2 = dh2;
229                             else
230                                 devdh2 = dh1;
231 
232                             size = devsize;
233                         }
234                     }
235                 }
236             }
237 
238             le = le->Flink;
239         }
240 
241         if (!devdh1)
242             return FALSE;
243     }
244 
245     stripes[0].device = stripes[1].device = dev2;
246     stripes[0].dh = devdh1;
247     stripes[1].dh = devdh2;
248 
249     return TRUE;
250 }
251 
252 static BOOL find_new_stripe(device_extension* Vcb, stripe* stripes, UINT16 i, UINT64 max_stripe_size, BOOL allow_missing, BOOL full_size) {
253     UINT64 k, devusage = 0xffffffffffffffff;
254     space* devdh = NULL;
255     LIST_ENTRY* le;
256     device* dev2 = NULL;
257 
258     le = Vcb->devices.Flink;
259     while (le != &Vcb->devices) {
260         device* dev = CONTAINING_RECORD(le, device, list_entry);
261         UINT64 usage;
262         BOOL skip = FALSE;
263 
264         if (dev->readonly || dev->reloc || (!dev->devobj && !allow_missing)) {
265             le = le->Flink;
266             continue;
267         }
268 
269         // skip this device if it already has a stripe
270         if (i > 0) {
271             for (k = 0; k < i; k++) {
272                 if (stripes[k].device == dev) {
273                     skip = TRUE;
274                     break;
275                 }
276             }
277         }
278 
279         if (!skip) {
280             usage = (dev->devitem.bytes_used * 4096) / dev->devitem.num_bytes;
281 
282             // favour devices which have been used the least
283             if (usage < devusage) {
284                 if (!IsListEmpty(&dev->space)) {
285                     LIST_ENTRY* le2;
286 
287                     le2 = dev->space.Flink;
288                     while (le2 != &dev->space) {
289                         space* dh = CONTAINING_RECORD(le2, space, list_entry);
290 
291                         if ((dev2 != dev && dh->size >= max_stripe_size) ||
292                             (dev2 == dev && dh->size >= max_stripe_size && dh->size < devdh->size)
293                         ) {
294                             devdh = dh;
295                             dev2 = dev;
296                             devusage = usage;
297                         }
298 
299                         le2 = le2->Flink;
300                     }
301                 }
302             }
303         }
304 
305         le = le->Flink;
306     }
307 
308     if (!devdh) {
309         // Can't find hole of at least max_stripe_size; look for the largest one we can find
310 
311         if (full_size)
312             return FALSE;
313 
314         le = Vcb->devices.Flink;
315         while (le != &Vcb->devices) {
316             device* dev = CONTAINING_RECORD(le, device, list_entry);
317             BOOL skip = FALSE;
318 
319             if (dev->readonly || dev->reloc || (!dev->devobj && !allow_missing)) {
320                 le = le->Flink;
321                 continue;
322             }
323 
324             // skip this device if it already has a stripe
325             if (i > 0) {
326                 for (k = 0; k < i; k++) {
327                     if (stripes[k].device == dev) {
328                         skip = TRUE;
329                         break;
330                     }
331                 }
332             }
333 
334             if (!skip) {
335                 if (!IsListEmpty(&dev->space)) {
336                     LIST_ENTRY* le2;
337 
338                     le2 = dev->space.Flink;
339                     while (le2 != &dev->space) {
340                         space* dh = CONTAINING_RECORD(le2, space, list_entry);
341 
342                         if (!devdh || devdh->size < dh->size) {
343                             devdh = dh;
344                             dev2 = dev;
345                         }
346 
347                         le2 = le2->Flink;
348                     }
349                 }
350             }
351 
352             le = le->Flink;
353         }
354 
355         if (!devdh)
356             return FALSE;
357     }
358 
359     stripes[i].dh = devdh;
360     stripes[i].device = dev2;
361 
362     return TRUE;
363 }
364 
365 NTSTATUS alloc_chunk(device_extension* Vcb, UINT64 flags, chunk** pc, BOOL full_size) {
366     NTSTATUS Status;
367     UINT64 max_stripe_size, max_chunk_size, stripe_size, stripe_length, factor;
368     UINT64 total_size = 0, logaddr;
369     UINT16 i, type, num_stripes, sub_stripes, max_stripes, min_stripes, allowed_missing;
370     stripe* stripes = NULL;
371     UINT16 cisize;
372     CHUNK_ITEM_STRIPE* cis;
373     chunk* c = NULL;
374     space* s = NULL;
375     LIST_ENTRY* le;
376 
377     le = Vcb->devices.Flink;
378     while (le != &Vcb->devices) {
379         device* dev = CONTAINING_RECORD(le, device, list_entry);
380         total_size += dev->devitem.num_bytes;
381 
382         le = le->Flink;
383     }
384 
385     TRACE("total_size = %llx\n", total_size);
386 
387     // We purposely check for DATA first - mixed blocks have the same size
388     // as DATA ones.
389     if (flags & BLOCK_FLAG_DATA) {
390         max_stripe_size = 0x40000000; // 1 GB
391         max_chunk_size = 10 * max_stripe_size;
392     } else if (flags & BLOCK_FLAG_METADATA) {
393         if (total_size > 0xC80000000) // 50 GB
394             max_stripe_size = 0x40000000; // 1 GB
395         else
396             max_stripe_size = 0x10000000; // 256 MB
397 
398         max_chunk_size = max_stripe_size;
399     } else if (flags & BLOCK_FLAG_SYSTEM) {
400         max_stripe_size = 0x2000000; // 32 MB
401         max_chunk_size = 2 * max_stripe_size;
402     } else {
403         ERR("unknown chunk type\n");
404         return STATUS_INTERNAL_ERROR;
405     }
406 
407     if (flags & BLOCK_FLAG_DUPLICATE) {
408         min_stripes = 2;
409         max_stripes = 2;
410         sub_stripes = 0;
411         type = BLOCK_FLAG_DUPLICATE;
412         allowed_missing = 0;
413     } else if (flags & BLOCK_FLAG_RAID0) {
414         min_stripes = 2;
415         max_stripes = (UINT16)min(0xffff, Vcb->superblock.num_devices);
416         sub_stripes = 0;
417         type = BLOCK_FLAG_RAID0;
418         allowed_missing = 0;
419     } else if (flags & BLOCK_FLAG_RAID1) {
420         min_stripes = 2;
421         max_stripes = 2;
422         sub_stripes = 1;
423         type = BLOCK_FLAG_RAID1;
424         allowed_missing = 1;
425     } else if (flags & BLOCK_FLAG_RAID10) {
426         min_stripes = 4;
427         max_stripes = (UINT16)min(0xffff, Vcb->superblock.num_devices);
428         sub_stripes = 2;
429         type = BLOCK_FLAG_RAID10;
430         allowed_missing = 1;
431     } else if (flags & BLOCK_FLAG_RAID5) {
432         min_stripes = 3;
433         max_stripes = (UINT16)min(0xffff, Vcb->superblock.num_devices);
434         sub_stripes = 1;
435         type = BLOCK_FLAG_RAID5;
436         allowed_missing = 1;
437     } else if (flags & BLOCK_FLAG_RAID6) {
438         min_stripes = 4;
439         max_stripes = 257;
440         sub_stripes = 1;
441         type = BLOCK_FLAG_RAID6;
442         allowed_missing = 2;
443     } else { // SINGLE
444         min_stripes = 1;
445         max_stripes = 1;
446         sub_stripes = 1;
447         type = 0;
448         allowed_missing = 0;
449     }
450 
451     if (max_chunk_size > total_size / 10) {  // cap at 10%
452         max_chunk_size = total_size / 10;
453         max_stripe_size = max_chunk_size / min_stripes;
454     }
455 
456     TRACE("would allocate a new chunk of %llx bytes and stripe %llx\n", max_chunk_size, max_stripe_size);
457 
458     stripes = ExAllocatePoolWithTag(PagedPool, sizeof(stripe) * max_stripes, ALLOC_TAG);
459     if (!stripes) {
460         ERR("out of memory\n");
461         Status = STATUS_INSUFFICIENT_RESOURCES;
462         goto end;
463     }
464 
465     num_stripes = 0;
466 
467     if (type == BLOCK_FLAG_DUPLICATE) {
468         if (!find_new_dup_stripes(Vcb, stripes, max_stripe_size, full_size)) {
469             Status = STATUS_DISK_FULL;
470             goto end;
471         }
472         else
473             num_stripes = max_stripes;
474     } else {
475         for (i = 0; i < max_stripes; i++) {
476             if (!find_new_stripe(Vcb, stripes, i, max_stripe_size, FALSE, full_size))
477                 break;
478             else
479                 num_stripes++;
480         }
481     }
482 
483     if (num_stripes < min_stripes && Vcb->options.allow_degraded && allowed_missing > 0) {
484         UINT16 added_missing = 0;
485 
486         for (i = num_stripes; i < max_stripes; i++) {
487             if (!find_new_stripe(Vcb, stripes, i, max_stripe_size, TRUE, full_size))
488                 break;
489             else {
490                 added_missing++;
491                 if (added_missing >= allowed_missing)
492                     break;
493             }
494         }
495 
496         num_stripes += added_missing;
497     }
498 
499     // for RAID10, round down to an even number of stripes
500     if (type == BLOCK_FLAG_RAID10 && (num_stripes % sub_stripes) != 0) {
501         num_stripes -= num_stripes % sub_stripes;
502     }
503 
504     if (num_stripes < min_stripes) {
505         WARN("found %u stripes, needed at least %u\n", num_stripes, min_stripes);
506         Status = STATUS_DISK_FULL;
507         goto end;
508     }
509 
510     c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG);
511     if (!c) {
512         ERR("out of memory\n");
513         Status = STATUS_INSUFFICIENT_RESOURCES;
514         goto end;
515     }
516 
517     c->devices = NULL;
518 
519     cisize = sizeof(CHUNK_ITEM) + (num_stripes * sizeof(CHUNK_ITEM_STRIPE));
520     c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, cisize, ALLOC_TAG);
521     if (!c->chunk_item) {
522         ERR("out of memory\n");
523         Status = STATUS_INSUFFICIENT_RESOURCES;
524         goto end;
525     }
526 
527     stripe_length = 0x10000; // FIXME? BTRFS_STRIPE_LEN in kernel
528 
529     if (type == BLOCK_FLAG_DUPLICATE && stripes[1].dh == stripes[0].dh)
530         stripe_size = min(stripes[0].dh->size / 2, max_stripe_size);
531     else {
532         stripe_size = max_stripe_size;
533         for (i = 0; i < num_stripes; i++) {
534             if (stripes[i].dh->size < stripe_size)
535                 stripe_size = stripes[i].dh->size;
536         }
537     }
538 
539     if (type == 0 || type == BLOCK_FLAG_DUPLICATE || type == BLOCK_FLAG_RAID1)
540         factor = 1;
541     else if (type == BLOCK_FLAG_RAID0)
542         factor = num_stripes;
543     else if (type == BLOCK_FLAG_RAID10)
544         factor = num_stripes / sub_stripes;
545     else if (type == BLOCK_FLAG_RAID5)
546         factor = num_stripes - 1;
547     else if (type == BLOCK_FLAG_RAID6)
548         factor = num_stripes - 2;
549 
550     if (stripe_size * factor > max_chunk_size)
551         stripe_size = max_chunk_size / factor;
552 
553     if (stripe_size % stripe_length > 0)
554         stripe_size -= stripe_size % stripe_length;
555 
556     if (stripe_size == 0) {
557         ERR("not enough free space found (stripe_size == 0)\n");
558         Status = STATUS_DISK_FULL;
559         goto end;
560     }
561 
562     c->chunk_item->size = stripe_size * factor;
563     c->chunk_item->root_id = Vcb->extent_root->id;
564     c->chunk_item->stripe_length = stripe_length;
565     c->chunk_item->type = flags;
566     c->chunk_item->opt_io_alignment = (UINT32)c->chunk_item->stripe_length;
567     c->chunk_item->opt_io_width = (UINT32)c->chunk_item->stripe_length;
568     c->chunk_item->sector_size = stripes[0].device->devitem.minimal_io_size;
569     c->chunk_item->num_stripes = num_stripes;
570     c->chunk_item->sub_stripes = sub_stripes;
571 
572     c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * num_stripes, ALLOC_TAG);
573     if (!c->devices) {
574         ERR("out of memory\n");
575         Status = STATUS_INSUFFICIENT_RESOURCES;
576         goto end;
577     }
578 
579     cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
580     for (i = 0; i < num_stripes; i++) {
581         cis[i].dev_id = stripes[i].device->devitem.dev_id;
582 
583         if (type == BLOCK_FLAG_DUPLICATE && i == 1 && stripes[i].dh == stripes[0].dh)
584             cis[i].offset = stripes[0].dh->address + stripe_size;
585         else
586             cis[i].offset = stripes[i].dh->address;
587 
588         cis[i].dev_uuid = stripes[i].device->devitem.device_uuid;
589 
590         c->devices[i] = stripes[i].device;
591     }
592 
593     logaddr = find_new_chunk_address(Vcb, c->chunk_item->size);
594 
595     Vcb->superblock.chunk_root_generation = Vcb->superblock.generation;
596 
597     c->size = cisize;
598     c->offset = logaddr;
599     c->used = c->oldused = 0;
600     c->cache = c->old_cache = NULL;
601     c->readonly = FALSE;
602     c->reloc = FALSE;
603     c->last_alloc_set = FALSE;
604     c->last_stripe = 0;
605     c->cache_loaded = TRUE;
606     c->changed = FALSE;
607     c->space_changed = FALSE;
608     c->balance_num = 0;
609 
610     InitializeListHead(&c->space);
611     InitializeListHead(&c->space_size);
612     InitializeListHead(&c->deleting);
613     InitializeListHead(&c->changed_extents);
614 
615     InitializeListHead(&c->range_locks);
616     ExInitializeResourceLite(&c->range_locks_lock);
617     KeInitializeEvent(&c->range_locks_event, NotificationEvent, FALSE);
618 
619     InitializeListHead(&c->partial_stripes);
620     ExInitializeResourceLite(&c->partial_stripes_lock);
621 
622     ExInitializeResourceLite(&c->lock);
623     ExInitializeResourceLite(&c->changed_extents_lock);
624 
625     s = ExAllocatePoolWithTag(NonPagedPool, sizeof(space), ALLOC_TAG);
626     if (!s) {
627         ERR("out of memory\n");
628         Status = STATUS_INSUFFICIENT_RESOURCES;
629         goto end;
630     }
631 
632     s->address = c->offset;
633     s->size = c->chunk_item->size;
634     InsertTailList(&c->space, &s->list_entry);
635     InsertTailList(&c->space_size, &s->list_entry_size);
636 
637     protect_superblocks(c);
638 
639     for (i = 0; i < num_stripes; i++) {
640         stripes[i].device->devitem.bytes_used += stripe_size;
641 
642         space_list_subtract2(&stripes[i].device->space, NULL, cis[i].offset, stripe_size, NULL, NULL);
643     }
644 
645     Status = STATUS_SUCCESS;
646 
647     if (flags & BLOCK_FLAG_RAID5 || flags & BLOCK_FLAG_RAID6)
648         Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_RAID56;
649 
650 end:
651     if (stripes)
652         ExFreePool(stripes);
653 
654     if (!NT_SUCCESS(Status)) {
655         if (c) {
656             if (c->devices)
657                 ExFreePool(c->devices);
658 
659             if (c->chunk_item)
660                 ExFreePool(c->chunk_item);
661 
662             ExFreePool(c);
663         }
664 
665         if (s) ExFreePool(s);
666     } else {
667         BOOL done = FALSE;
668 
669         le = Vcb->chunks.Flink;
670         while (le != &Vcb->chunks) {
671             chunk* c2 = CONTAINING_RECORD(le, chunk, list_entry);
672 
673             if (c2->offset > c->offset) {
674                 InsertHeadList(le->Blink, &c->list_entry);
675                 done = TRUE;
676                 break;
677             }
678 
679             le = le->Flink;
680         }
681 
682         if (!done)
683             InsertTailList(&Vcb->chunks, &c->list_entry);
684 
685         c->created = TRUE;
686         c->changed = TRUE;
687         c->space_changed = TRUE;
688         c->list_entry_balance.Flink = NULL;
689 
690         *pc = c;
691     }
692 
693     return Status;
694 }
695 
696 static NTSTATUS prepare_raid0_write(_Pre_satisfies_(_Curr_->chunk_item->num_stripes>0) _In_ chunk* c, _In_ UINT64 address, _In_reads_bytes_(length) void* data,
697                                     _In_ UINT32 length, _In_ write_stripe* stripes, _In_ PIRP Irp, _In_ UINT64 irp_offset, _In_ write_data_context* wtc) {
698     UINT64 startoff, endoff;
699     UINT16 startoffstripe, endoffstripe, stripenum;
700     UINT64 pos, *stripeoff;
701     UINT32 i;
702     BOOL file_write = Irp && Irp->MdlAddress && (Irp->MdlAddress->ByteOffset == 0);
703     PMDL master_mdl;
704     PFN_NUMBER* pfns;
705 
706     stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes, ALLOC_TAG);
707     if (!stripeoff) {
708         ERR("out of memory\n");
709         return STATUS_INSUFFICIENT_RESOURCES;
710     }
711 
712     get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &startoff, &startoffstripe);
713     get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &endoff, &endoffstripe);
714 
715     if (file_write) {
716         master_mdl = Irp->MdlAddress;
717 
718         pfns = (PFN_NUMBER*)(Irp->MdlAddress + 1);
719         pfns = &pfns[irp_offset >> PAGE_SHIFT];
720     } else if (((ULONG_PTR)data % PAGE_SIZE) != 0) {
721         wtc->scratch = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
722         if (!wtc->scratch) {
723             ERR("out of memory\n");
724             return STATUS_INSUFFICIENT_RESOURCES;
725         }
726 
727         RtlCopyMemory(wtc->scratch, data, length);
728 
729         master_mdl = IoAllocateMdl(wtc->scratch, length, FALSE, FALSE, NULL);
730         if (!master_mdl) {
731             ERR("out of memory\n");
732             return STATUS_INSUFFICIENT_RESOURCES;
733         }
734 
735         MmBuildMdlForNonPagedPool(master_mdl);
736 
737         wtc->mdl = master_mdl;
738 
739         pfns = (PFN_NUMBER*)(master_mdl + 1);
740     } else {
741         NTSTATUS Status = STATUS_SUCCESS;
742 
743         master_mdl = IoAllocateMdl(data, length, FALSE, FALSE, NULL);
744         if (!master_mdl) {
745             ERR("out of memory\n");
746             return STATUS_INSUFFICIENT_RESOURCES;
747         }
748 
749         _SEH2_TRY {
750             MmProbeAndLockPages(master_mdl, KernelMode, IoReadAccess);
751         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
752             Status = _SEH2_GetExceptionCode();
753         } _SEH2_END;
754 
755         if (!NT_SUCCESS(Status)) {
756             ERR("MmProbeAndLockPages threw exception %08x\n", Status);
757             IoFreeMdl(master_mdl);
758             return Status;
759         }
760 
761         wtc->mdl = master_mdl;
762 
763         pfns = (PFN_NUMBER*)(master_mdl + 1);
764     }
765 
766     for (i = 0; i < c->chunk_item->num_stripes; i++) {
767         if (startoffstripe > i)
768             stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
769         else if (startoffstripe == i)
770             stripes[i].start = startoff;
771         else
772             stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length);
773 
774         if (endoffstripe > i)
775             stripes[i].end = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
776         else if (endoffstripe == i)
777             stripes[i].end = endoff + 1;
778         else
779             stripes[i].end = endoff - (endoff % c->chunk_item->stripe_length);
780 
781         if (stripes[i].start != stripes[i].end) {
782             stripes[i].mdl = IoAllocateMdl(NULL, (ULONG)(stripes[i].end - stripes[i].start), FALSE, FALSE, NULL);
783             if (!stripes[i].mdl) {
784                 ERR("IoAllocateMdl failed\n");
785                 ExFreePool(stripeoff);
786                 return STATUS_INSUFFICIENT_RESOURCES;
787             }
788         }
789     }
790 
791     pos = 0;
792     RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes);
793 
794     stripenum = startoffstripe;
795 
796     while (pos < length) {
797         PFN_NUMBER* stripe_pfns = (PFN_NUMBER*)(stripes[stripenum].mdl + 1);
798 
799         if (pos == 0) {
800             UINT32 writelen = (UINT32)min(stripes[stripenum].end - stripes[stripenum].start,
801                                           c->chunk_item->stripe_length - (stripes[stripenum].start % c->chunk_item->stripe_length));
802 
803             RtlCopyMemory(stripe_pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
804 
805             stripeoff[stripenum] += writelen;
806             pos += writelen;
807         } else if (length - pos < c->chunk_item->stripe_length) {
808             RtlCopyMemory(&stripe_pfns[stripeoff[stripenum] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (ULONG)((length - pos) * sizeof(PFN_NUMBER) >> PAGE_SHIFT));
809             break;
810         } else {
811             RtlCopyMemory(&stripe_pfns[stripeoff[stripenum] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT));
812 
813             stripeoff[stripenum] += c->chunk_item->stripe_length;
814             pos += c->chunk_item->stripe_length;
815         }
816 
817         stripenum = (stripenum + 1) % c->chunk_item->num_stripes;
818     }
819 
820     ExFreePool(stripeoff);
821 
822     return STATUS_SUCCESS;
823 }
824 
825 static NTSTATUS prepare_raid10_write(_Pre_satisfies_(_Curr_->chunk_item->sub_stripes>0&&_Curr_->chunk_item->num_stripes>=_Curr_->chunk_item->sub_stripes) _In_ chunk* c,
826                                      _In_ UINT64 address, _In_reads_bytes_(length) void* data, _In_ UINT32 length, _In_ write_stripe* stripes,
827                                      _In_ PIRP Irp, _In_ UINT64 irp_offset, _In_ write_data_context* wtc) {
828     UINT64 startoff, endoff;
829     UINT16 startoffstripe, endoffstripe, stripenum;
830     UINT64 pos, *stripeoff;
831     UINT32 i;
832     BOOL file_write = Irp && Irp->MdlAddress && (Irp->MdlAddress->ByteOffset == 0);
833     PMDL master_mdl;
834     PFN_NUMBER* pfns;
835 
836     get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes / c->chunk_item->sub_stripes, &startoff, &startoffstripe);
837     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);
838 
839     stripenum = startoffstripe;
840     startoffstripe *= c->chunk_item->sub_stripes;
841     endoffstripe *= c->chunk_item->sub_stripes;
842 
843     if (file_write) {
844         master_mdl = Irp->MdlAddress;
845 
846         pfns = (PFN_NUMBER*)(Irp->MdlAddress + 1);
847         pfns = &pfns[irp_offset >> PAGE_SHIFT];
848     } else if (((ULONG_PTR)data % PAGE_SIZE) != 0) {
849         wtc->scratch = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
850         if (!wtc->scratch) {
851             ERR("out of memory\n");
852             return STATUS_INSUFFICIENT_RESOURCES;
853         }
854 
855         RtlCopyMemory(wtc->scratch, data, length);
856 
857         master_mdl = IoAllocateMdl(wtc->scratch, length, FALSE, FALSE, NULL);
858         if (!master_mdl) {
859             ERR("out of memory\n");
860             return STATUS_INSUFFICIENT_RESOURCES;
861         }
862 
863         MmBuildMdlForNonPagedPool(master_mdl);
864 
865         wtc->mdl = master_mdl;
866 
867         pfns = (PFN_NUMBER*)(master_mdl + 1);
868     } else {
869         NTSTATUS Status = STATUS_SUCCESS;
870 
871         master_mdl = IoAllocateMdl(data, length, FALSE, FALSE, NULL);
872         if (!master_mdl) {
873             ERR("out of memory\n");
874             return STATUS_INSUFFICIENT_RESOURCES;
875         }
876 
877         _SEH2_TRY {
878             MmProbeAndLockPages(master_mdl, KernelMode, IoReadAccess);
879         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
880             Status = _SEH2_GetExceptionCode();
881         } _SEH2_END;
882 
883         if (!NT_SUCCESS(Status)) {
884             ERR("MmProbeAndLockPages threw exception %08x\n", Status);
885             IoFreeMdl(master_mdl);
886             return Status;
887         }
888 
889         wtc->mdl = master_mdl;
890 
891         pfns = (PFN_NUMBER*)(master_mdl + 1);
892     }
893 
894     for (i = 0; i < c->chunk_item->num_stripes; i += c->chunk_item->sub_stripes) {
895         UINT16 j;
896 
897         if (startoffstripe > i)
898             stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
899         else if (startoffstripe == i)
900             stripes[i].start = startoff;
901         else
902             stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length);
903 
904         if (endoffstripe > i)
905             stripes[i].end = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
906         else if (endoffstripe == i)
907             stripes[i].end = endoff + 1;
908         else
909             stripes[i].end = endoff - (endoff % c->chunk_item->stripe_length);
910 
911         stripes[i].mdl = IoAllocateMdl(NULL, (ULONG)(stripes[i].end - stripes[i].start), FALSE, FALSE, NULL);
912         if (!stripes[i].mdl) {
913             ERR("IoAllocateMdl failed\n");
914             return STATUS_INSUFFICIENT_RESOURCES;
915         }
916 
917         for (j = 1; j < c->chunk_item->sub_stripes; j++) {
918             stripes[i+j].start = stripes[i].start;
919             stripes[i+j].end = stripes[i].end;
920             stripes[i+j].data = stripes[i].data;
921             stripes[i+j].mdl = stripes[i].mdl;
922         }
923     }
924 
925     pos = 0;
926 
927     stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes / c->chunk_item->sub_stripes, ALLOC_TAG);
928     if (!stripeoff) {
929         ERR("out of memory\n");
930         return STATUS_INSUFFICIENT_RESOURCES;
931     }
932 
933     RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes / c->chunk_item->sub_stripes);
934 
935     while (pos < length) {
936         PFN_NUMBER* stripe_pfns = (PFN_NUMBER*)(stripes[stripenum * c->chunk_item->sub_stripes].mdl + 1);
937 
938         if (pos == 0) {
939             UINT32 writelen = (UINT32)min(stripes[stripenum * c->chunk_item->sub_stripes].end - stripes[stripenum * c->chunk_item->sub_stripes].start,
940                                           c->chunk_item->stripe_length - (stripes[stripenum * c->chunk_item->sub_stripes].start % c->chunk_item->stripe_length));
941 
942             RtlCopyMemory(stripe_pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
943 
944             stripeoff[stripenum] += writelen;
945             pos += writelen;
946         } else if (length - pos < c->chunk_item->stripe_length) {
947             RtlCopyMemory(&stripe_pfns[stripeoff[stripenum] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (ULONG)((length - pos) * sizeof(PFN_NUMBER) >> PAGE_SHIFT));
948             break;
949         } else {
950             RtlCopyMemory(&stripe_pfns[stripeoff[stripenum] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT));
951 
952             stripeoff[stripenum] += c->chunk_item->stripe_length;
953             pos += c->chunk_item->stripe_length;
954         }
955 
956         stripenum = (stripenum + 1) % (c->chunk_item->num_stripes / c->chunk_item->sub_stripes);
957     }
958 
959     ExFreePool(stripeoff);
960 
961     return STATUS_SUCCESS;
962 }
963 
964 static NTSTATUS add_partial_stripe(device_extension* Vcb, chunk *c, UINT64 address, UINT32 length, void* data) {
965     NTSTATUS Status;
966     LIST_ENTRY* le;
967     partial_stripe* ps;
968     UINT64 stripe_addr;
969     UINT16 num_data_stripes;
970     ULONG bmplen;
971 
972     num_data_stripes = c->chunk_item->num_stripes - (c->chunk_item->type & BLOCK_FLAG_RAID5 ? 1 : 2);
973     stripe_addr = address - ((address - c->offset) % (num_data_stripes * c->chunk_item->stripe_length));
974 
975     ExAcquireResourceExclusiveLite(&c->partial_stripes_lock, TRUE);
976 
977     le = c->partial_stripes.Flink;
978     while (le != &c->partial_stripes) {
979         ps = CONTAINING_RECORD(le, partial_stripe, list_entry);
980 
981         if (ps->address == stripe_addr) {
982             // update existing entry
983 
984             RtlCopyMemory(ps->data + address - stripe_addr, data, length);
985             RtlClearBits(&ps->bmp, (ULONG)((address - stripe_addr) / Vcb->superblock.sector_size), length / Vcb->superblock.sector_size);
986 
987             // if now filled, flush
988             if (RtlAreBitsClear(&ps->bmp, 0, (ULONG)((num_data_stripes * c->chunk_item->stripe_length) / Vcb->superblock.sector_size))) {
989                 Status = flush_partial_stripe(Vcb, c, ps);
990                 if (!NT_SUCCESS(Status)) {
991                     ERR("flush_partial_stripe returned %08x\n", Status);
992                     goto end;
993                 }
994 
995                 RemoveEntryList(&ps->list_entry);
996 
997                 if (ps->bmparr)
998                     ExFreePool(ps->bmparr);
999 
1000                 ExFreePool(ps);
1001             }
1002 
1003             Status = STATUS_SUCCESS;
1004             goto end;
1005         } else if (ps->address > stripe_addr)
1006             break;
1007 
1008         le = le->Flink;
1009     }
1010 
1011     // add new entry
1012 
1013     ps = ExAllocatePoolWithTag(NonPagedPool, offsetof(partial_stripe, data[0]) + (ULONG)(num_data_stripes * c->chunk_item->stripe_length), ALLOC_TAG);
1014     if (!ps) {
1015         ERR("out of memory\n");
1016         Status = STATUS_INSUFFICIENT_RESOURCES;
1017         goto end;
1018     }
1019 
1020     bmplen = (ULONG)sector_align(((num_data_stripes * c->chunk_item->stripe_length) / (8 * Vcb->superblock.sector_size) + 1), sizeof(ULONG));
1021 
1022     ps->address = stripe_addr;
1023     ps->bmparr = ExAllocatePoolWithTag(NonPagedPool, bmplen, ALLOC_TAG);
1024     if (!ps->bmparr) {
1025         ERR("out of memory\n");
1026         ExFreePool(ps);
1027         Status = STATUS_INSUFFICIENT_RESOURCES;
1028         goto end;
1029     }
1030 
1031     RtlInitializeBitMap(&ps->bmp, ps->bmparr, (ULONG)((num_data_stripes * c->chunk_item->stripe_length) / Vcb->superblock.sector_size));
1032     RtlSetAllBits(&ps->bmp);
1033 
1034     RtlCopyMemory(ps->data + address - stripe_addr, data, length);
1035     RtlClearBits(&ps->bmp, (ULONG)((address - stripe_addr) / Vcb->superblock.sector_size), length / Vcb->superblock.sector_size);
1036 
1037     InsertHeadList(le->Blink, &ps->list_entry);
1038 
1039     Status = STATUS_SUCCESS;
1040 
1041 end:
1042     ExReleaseResourceLite(&c->partial_stripes_lock);
1043 
1044     return Status;
1045 }
1046 
1047 typedef struct {
1048     PMDL mdl;
1049     PFN_NUMBER* pfns;
1050 } log_stripe;
1051 
1052 static NTSTATUS prepare_raid5_write(device_extension* Vcb, chunk* c, UINT64 address, void* data, UINT32 length, write_stripe* stripes, PIRP Irp,
1053                                     UINT64 irp_offset, ULONG priority, write_data_context* wtc) {
1054     UINT64 startoff, endoff, parity_start, parity_end;
1055     UINT16 startoffstripe, endoffstripe, parity, num_data_stripes = c->chunk_item->num_stripes - 1;
1056     UINT64 pos, parity_pos, *stripeoff = NULL;
1057     UINT32 i;
1058     BOOL file_write = Irp && Irp->MdlAddress && (Irp->MdlAddress->ByteOffset == 0);
1059     PMDL master_mdl;
1060     NTSTATUS Status;
1061     PFN_NUMBER *pfns, *parity_pfns;
1062     log_stripe* log_stripes = NULL;
1063 
1064     if ((address + length - c->offset) % (num_data_stripes * c->chunk_item->stripe_length) > 0) {
1065         UINT64 delta = (address + length - c->offset) % (num_data_stripes * c->chunk_item->stripe_length);
1066 
1067         delta = min(irp_offset + length, delta);
1068         Status = add_partial_stripe(Vcb, c, address + length - delta, (UINT32)delta, (UINT8*)data + irp_offset + length - delta);
1069         if (!NT_SUCCESS(Status)) {
1070             ERR("add_partial_stripe returned %08x\n", Status);
1071             goto exit;
1072         }
1073 
1074         length -= (UINT32)delta;
1075     }
1076 
1077     if (length > 0 && (address - c->offset) % (num_data_stripes * c->chunk_item->stripe_length) > 0) {
1078         UINT64 delta = (num_data_stripes * c->chunk_item->stripe_length) - ((address - c->offset) % (num_data_stripes * c->chunk_item->stripe_length));
1079 
1080         Status = add_partial_stripe(Vcb, c, address, (UINT32)delta, (UINT8*)data + irp_offset);
1081         if (!NT_SUCCESS(Status)) {
1082             ERR("add_partial_stripe returned %08x\n", Status);
1083             goto exit;
1084         }
1085 
1086         address += delta;
1087         length -= (UINT32)delta;
1088         irp_offset += delta;
1089     }
1090 
1091     if (length == 0) {
1092         Status = STATUS_SUCCESS;
1093         goto exit;
1094     }
1095 
1096     get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, num_data_stripes, &startoff, &startoffstripe);
1097     get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, num_data_stripes, &endoff, &endoffstripe);
1098 
1099     pos = 0;
1100     while (pos < length) {
1101         parity = (((address - c->offset + pos) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes;
1102 
1103         if (pos == 0) {
1104             UINT16 stripe = (parity + startoffstripe + 1) % c->chunk_item->num_stripes;
1105             ULONG skip, writelen;
1106 
1107             i = startoffstripe;
1108             while (stripe != parity) {
1109                 if (i == startoffstripe) {
1110                     writelen = (ULONG)min(length, c->chunk_item->stripe_length - (startoff % c->chunk_item->stripe_length));
1111 
1112                     stripes[stripe].start = startoff;
1113                     stripes[stripe].end = startoff + writelen;
1114 
1115                     pos += writelen;
1116 
1117                     if (pos == length)
1118                         break;
1119                 } else {
1120                     writelen = (ULONG)min(length - pos, c->chunk_item->stripe_length);
1121 
1122                     stripes[stripe].start = startoff - (startoff % c->chunk_item->stripe_length);
1123                     stripes[stripe].end = stripes[stripe].start + writelen;
1124 
1125                     pos += writelen;
1126 
1127                     if (pos == length)
1128                         break;
1129                 }
1130 
1131                 i++;
1132                 stripe = (stripe + 1) % c->chunk_item->num_stripes;
1133             }
1134 
1135             if (pos == length)
1136                 break;
1137 
1138             for (i = 0; i < startoffstripe; i++) {
1139                 stripe = (parity + i + 1) % c->chunk_item->num_stripes;
1140 
1141                 stripes[stripe].start = stripes[stripe].end = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
1142             }
1143 
1144             stripes[parity].start = stripes[parity].end = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
1145 
1146             if (length - pos > c->chunk_item->num_stripes * num_data_stripes * c->chunk_item->stripe_length) {
1147                 skip = (ULONG)(((length - pos) / (c->chunk_item->num_stripes * num_data_stripes * c->chunk_item->stripe_length)) - 1);
1148 
1149                 for (i = 0; i < c->chunk_item->num_stripes; i++) {
1150                     stripes[i].end += skip * c->chunk_item->num_stripes * c->chunk_item->stripe_length;
1151                 }
1152 
1153                 pos += skip * num_data_stripes * c->chunk_item->num_stripes * c->chunk_item->stripe_length;
1154             }
1155         } else if (length - pos >= c->chunk_item->stripe_length * num_data_stripes) {
1156             for (i = 0; i < c->chunk_item->num_stripes; i++) {
1157                 stripes[i].end += c->chunk_item->stripe_length;
1158             }
1159 
1160             pos += c->chunk_item->stripe_length * num_data_stripes;
1161         } else {
1162             UINT16 stripe = (parity + 1) % c->chunk_item->num_stripes;
1163 
1164             i = 0;
1165             while (stripe != parity) {
1166                 if (endoffstripe == i) {
1167                     stripes[stripe].end = endoff + 1;
1168                     break;
1169                 } else if (endoffstripe > i)
1170                     stripes[stripe].end = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
1171 
1172                 i++;
1173                 stripe = (stripe + 1) % c->chunk_item->num_stripes;
1174             }
1175 
1176             break;
1177         }
1178     }
1179 
1180     parity_start = 0xffffffffffffffff;
1181     parity_end = 0;
1182 
1183     for (i = 0; i < c->chunk_item->num_stripes; i++) {
1184         if (stripes[i].start != 0 || stripes[i].end != 0) {
1185             parity_start = min(stripes[i].start, parity_start);
1186             parity_end = max(stripes[i].end, parity_end);
1187         }
1188     }
1189 
1190     if (parity_end == parity_start) {
1191         Status = STATUS_SUCCESS;
1192         goto exit;
1193     }
1194 
1195     parity = (((address - c->offset) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes;
1196     stripes[parity].start = parity_start;
1197 
1198     parity = (((address - c->offset + length - 1) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes;
1199     stripes[parity].end = parity_end;
1200 
1201     log_stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(log_stripe) * num_data_stripes, ALLOC_TAG);
1202     if (!log_stripes) {
1203         ERR("out of memory\n");
1204         Status = STATUS_INSUFFICIENT_RESOURCES;
1205         goto exit;
1206     }
1207 
1208     RtlZeroMemory(log_stripes, sizeof(log_stripe) * num_data_stripes);
1209 
1210     for (i = 0; i < num_data_stripes; i++) {
1211         log_stripes[i].mdl = IoAllocateMdl(NULL, (ULONG)(parity_end - parity_start), FALSE, FALSE, NULL);
1212         if (!log_stripes[i].mdl) {
1213             ERR("out of memory\n");
1214             Status = STATUS_INSUFFICIENT_RESOURCES;
1215             goto exit;
1216         }
1217 
1218         log_stripes[i].mdl->MdlFlags |= MDL_PARTIAL;
1219         log_stripes[i].pfns = (PFN_NUMBER*)(log_stripes[i].mdl + 1);
1220     }
1221 
1222     wtc->parity1 = ExAllocatePoolWithTag(NonPagedPool, (ULONG)(parity_end - parity_start), ALLOC_TAG);
1223     if (!wtc->parity1) {
1224         ERR("out of memory\n");
1225         Status = STATUS_INSUFFICIENT_RESOURCES;
1226         goto exit;
1227     }
1228 
1229     wtc->parity1_mdl = IoAllocateMdl(wtc->parity1, (ULONG)(parity_end - parity_start), FALSE, FALSE, NULL);
1230     if (!wtc->parity1_mdl) {
1231         ERR("out of memory\n");
1232         Status = STATUS_INSUFFICIENT_RESOURCES;
1233         goto exit;
1234     }
1235 
1236     MmBuildMdlForNonPagedPool(wtc->parity1_mdl);
1237 
1238     if (file_write)
1239         master_mdl = Irp->MdlAddress;
1240     else if (((ULONG_PTR)data % PAGE_SIZE) != 0) {
1241         wtc->scratch = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
1242         if (!wtc->scratch) {
1243             ERR("out of memory\n");
1244             Status = STATUS_INSUFFICIENT_RESOURCES;
1245             goto exit;
1246         }
1247 
1248         RtlCopyMemory(wtc->scratch, (UINT8*)data + irp_offset, length);
1249 
1250         master_mdl = IoAllocateMdl(wtc->scratch, length, FALSE, FALSE, NULL);
1251         if (!master_mdl) {
1252             ERR("out of memory\n");
1253             Status = STATUS_INSUFFICIENT_RESOURCES;
1254             goto exit;
1255         }
1256 
1257         MmBuildMdlForNonPagedPool(master_mdl);
1258 
1259         wtc->mdl = master_mdl;
1260     } else {
1261         master_mdl = IoAllocateMdl((UINT8*)data + irp_offset, length, FALSE, FALSE, NULL);
1262         if (!master_mdl) {
1263             ERR("out of memory\n");
1264             Status = STATUS_INSUFFICIENT_RESOURCES;
1265             goto exit;
1266         }
1267 
1268         Status = STATUS_SUCCESS;
1269 
1270         _SEH2_TRY {
1271             MmProbeAndLockPages(master_mdl, KernelMode, IoReadAccess);
1272         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
1273             Status = _SEH2_GetExceptionCode();
1274         } _SEH2_END;
1275 
1276         if (!NT_SUCCESS(Status)) {
1277             ERR("MmProbeAndLockPages threw exception %08x\n", Status);
1278             IoFreeMdl(master_mdl);
1279             return Status;
1280         }
1281 
1282         wtc->mdl = master_mdl;
1283     }
1284 
1285     pfns = (PFN_NUMBER*)(master_mdl + 1);
1286     parity_pfns = (PFN_NUMBER*)(wtc->parity1_mdl + 1);
1287 
1288     if (file_write)
1289         pfns = &pfns[irp_offset >> PAGE_SHIFT];
1290 
1291     for (i = 0; i < c->chunk_item->num_stripes; i++) {
1292         if (stripes[i].start != stripes[i].end) {
1293             stripes[i].mdl = IoAllocateMdl((UINT8*)MmGetMdlVirtualAddress(master_mdl) + irp_offset, (ULONG)(stripes[i].end - stripes[i].start), FALSE, FALSE, NULL);
1294             if (!stripes[i].mdl) {
1295                 ERR("IoAllocateMdl failed\n");
1296                 Status = STATUS_INSUFFICIENT_RESOURCES;
1297                 goto exit;
1298             }
1299         }
1300     }
1301 
1302     stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes, ALLOC_TAG);
1303     if (!stripeoff) {
1304         ERR("out of memory\n");
1305         Status = STATUS_INSUFFICIENT_RESOURCES;
1306         goto exit;
1307     }
1308 
1309     RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes);
1310 
1311     pos = 0;
1312     parity_pos = 0;
1313 
1314     while (pos < length) {
1315         PFN_NUMBER* stripe_pfns;
1316 
1317         parity = (((address - c->offset + pos) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes;
1318 
1319         if (pos == 0) {
1320             UINT16 stripe = (parity + startoffstripe + 1) % c->chunk_item->num_stripes;
1321             UINT32 writelen = (UINT32)min(length - pos, min(stripes[stripe].end - stripes[stripe].start,
1322                                                             c->chunk_item->stripe_length - (stripes[stripe].start % c->chunk_item->stripe_length)));
1323             UINT32 maxwritelen = writelen;
1324 
1325             stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
1326 
1327             RtlCopyMemory(stripe_pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1328 
1329             RtlCopyMemory(log_stripes[startoffstripe].pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1330             log_stripes[startoffstripe].pfns += writelen >> PAGE_SHIFT;
1331 
1332             stripeoff[stripe] = writelen;
1333             pos += writelen;
1334 
1335             stripe = (stripe + 1) % c->chunk_item->num_stripes;
1336             i = startoffstripe + 1;
1337 
1338             while (stripe != parity) {
1339                 stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
1340                 writelen = (UINT32)min(length - pos, min(stripes[stripe].end - stripes[stripe].start, c->chunk_item->stripe_length));
1341 
1342                 if (writelen == 0)
1343                     break;
1344 
1345                 if (writelen > maxwritelen)
1346                     maxwritelen = writelen;
1347 
1348                 RtlCopyMemory(stripe_pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1349 
1350                 RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1351                 log_stripes[i].pfns += writelen >> PAGE_SHIFT;
1352 
1353                 stripeoff[stripe] = writelen;
1354                 pos += writelen;
1355 
1356                 stripe = (stripe + 1) % c->chunk_item->num_stripes;
1357                 i++;
1358             }
1359 
1360             stripe_pfns = (PFN_NUMBER*)(stripes[parity].mdl + 1);
1361 
1362             RtlCopyMemory(stripe_pfns, parity_pfns, maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1363             stripeoff[parity] = maxwritelen;
1364             parity_pos = maxwritelen;
1365         } else if (length - pos >= c->chunk_item->stripe_length * num_data_stripes) {
1366             UINT16 stripe = (parity + 1) % c->chunk_item->num_stripes;
1367 
1368             i = 0;
1369             while (stripe != parity) {
1370                 stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
1371 
1372                 RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT));
1373 
1374                 RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT));
1375                 log_stripes[i].pfns += c->chunk_item->stripe_length >> PAGE_SHIFT;
1376 
1377                 stripeoff[stripe] += c->chunk_item->stripe_length;
1378                 pos += c->chunk_item->stripe_length;
1379 
1380                 stripe = (stripe + 1) % c->chunk_item->num_stripes;
1381                 i++;
1382             }
1383 
1384             stripe_pfns = (PFN_NUMBER*)(stripes[parity].mdl + 1);
1385 
1386             RtlCopyMemory(&stripe_pfns[stripeoff[parity] >> PAGE_SHIFT], &parity_pfns[parity_pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT));
1387             stripeoff[parity] += c->chunk_item->stripe_length;
1388             parity_pos += c->chunk_item->stripe_length;
1389         } else {
1390             UINT16 stripe = (parity + 1) % c->chunk_item->num_stripes;
1391             UINT32 writelen, maxwritelen = 0;
1392 
1393             i = 0;
1394             while (pos < length) {
1395                 stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
1396                 writelen = (UINT32)min(length - pos, min(stripes[stripe].end - stripes[stripe].start, c->chunk_item->stripe_length));
1397 
1398                 if (writelen == 0)
1399                     break;
1400 
1401                 if (writelen > maxwritelen)
1402                     maxwritelen = writelen;
1403 
1404                 RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1405 
1406                 RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1407                 log_stripes[i].pfns += writelen >> PAGE_SHIFT;
1408 
1409                 stripeoff[stripe] += writelen;
1410                 pos += writelen;
1411 
1412                 stripe = (stripe + 1) % c->chunk_item->num_stripes;
1413                 i++;
1414             }
1415 
1416             stripe_pfns = (PFN_NUMBER*)(stripes[parity].mdl + 1);
1417 
1418             RtlCopyMemory(&stripe_pfns[stripeoff[parity] >> PAGE_SHIFT], &parity_pfns[parity_pos >> PAGE_SHIFT], maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1419         }
1420     }
1421 
1422     for (i = 0; i < num_data_stripes; i++) {
1423         UINT8* ss = MmGetSystemAddressForMdlSafe(log_stripes[i].mdl, priority);
1424 
1425         if (i == 0)
1426             RtlCopyMemory(wtc->parity1, ss, (UINT32)(parity_end - parity_start));
1427         else
1428             do_xor(wtc->parity1, ss, (UINT32)(parity_end - parity_start));
1429     }
1430 
1431     Status = STATUS_SUCCESS;
1432 
1433 exit:
1434     if (log_stripes) {
1435         for (i = 0; i < num_data_stripes; i++) {
1436             if (log_stripes[i].mdl)
1437                 IoFreeMdl(log_stripes[i].mdl);
1438         }
1439 
1440         ExFreePool(log_stripes);
1441     }
1442 
1443     if (stripeoff)
1444         ExFreePool(stripeoff);
1445 
1446     return Status;
1447 }
1448 
1449 static NTSTATUS prepare_raid6_write(device_extension* Vcb, chunk* c, UINT64 address, void* data, UINT32 length, write_stripe* stripes, PIRP Irp,
1450                                     UINT64 irp_offset, ULONG priority, write_data_context* wtc) {
1451     UINT64 startoff, endoff, parity_start, parity_end;
1452     UINT16 startoffstripe, endoffstripe, parity1, num_data_stripes = c->chunk_item->num_stripes - 2;
1453     UINT64 pos, parity_pos, *stripeoff = NULL;
1454     UINT32 i;
1455     BOOL file_write = Irp && Irp->MdlAddress && (Irp->MdlAddress->ByteOffset == 0);
1456     PMDL master_mdl;
1457     NTSTATUS Status;
1458     PFN_NUMBER *pfns, *parity1_pfns, *parity2_pfns;
1459     log_stripe* log_stripes = NULL;
1460 
1461     if ((address + length - c->offset) % (num_data_stripes * c->chunk_item->stripe_length) > 0) {
1462         UINT64 delta = (address + length - c->offset) % (num_data_stripes * c->chunk_item->stripe_length);
1463 
1464         delta = min(irp_offset + length, delta);
1465         Status = add_partial_stripe(Vcb, c, address + length - delta, (UINT32)delta, (UINT8*)data + irp_offset + length - delta);
1466         if (!NT_SUCCESS(Status)) {
1467             ERR("add_partial_stripe returned %08x\n", Status);
1468             goto exit;
1469         }
1470 
1471         length -= (UINT32)delta;
1472     }
1473 
1474     if (length > 0 && (address - c->offset) % (num_data_stripes * c->chunk_item->stripe_length) > 0) {
1475         UINT64 delta = (num_data_stripes * c->chunk_item->stripe_length) - ((address - c->offset) % (num_data_stripes * c->chunk_item->stripe_length));
1476 
1477         Status = add_partial_stripe(Vcb, c, address, (UINT32)delta, (UINT8*)data + irp_offset);
1478         if (!NT_SUCCESS(Status)) {
1479             ERR("add_partial_stripe returned %08x\n", Status);
1480             goto exit;
1481         }
1482 
1483         address += delta;
1484         length -= (UINT32)delta;
1485         irp_offset += delta;
1486     }
1487 
1488     if (length == 0) {
1489         Status = STATUS_SUCCESS;
1490         goto exit;
1491     }
1492 
1493     get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, num_data_stripes, &startoff, &startoffstripe);
1494     get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, num_data_stripes, &endoff, &endoffstripe);
1495 
1496     pos = 0;
1497     while (pos < length) {
1498         parity1 = (((address - c->offset + pos) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes;
1499 
1500         if (pos == 0) {
1501             UINT16 stripe = (parity1 + startoffstripe + 2) % c->chunk_item->num_stripes;
1502             UINT16 parity2 = (parity1 + 1) % c->chunk_item->num_stripes;
1503             ULONG skip, writelen;
1504 
1505             i = startoffstripe;
1506             while (stripe != parity1) {
1507                 if (i == startoffstripe) {
1508                     writelen = (ULONG)min(length, c->chunk_item->stripe_length - (startoff % c->chunk_item->stripe_length));
1509 
1510                     stripes[stripe].start = startoff;
1511                     stripes[stripe].end = startoff + writelen;
1512 
1513                     pos += writelen;
1514 
1515                     if (pos == length)
1516                         break;
1517                 } else {
1518                     writelen = (ULONG)min(length - pos, c->chunk_item->stripe_length);
1519 
1520                     stripes[stripe].start = startoff - (startoff % c->chunk_item->stripe_length);
1521                     stripes[stripe].end = stripes[stripe].start + writelen;
1522 
1523                     pos += writelen;
1524 
1525                     if (pos == length)
1526                         break;
1527                 }
1528 
1529                 i++;
1530                 stripe = (stripe + 1) % c->chunk_item->num_stripes;
1531             }
1532 
1533             if (pos == length)
1534                 break;
1535 
1536             for (i = 0; i < startoffstripe; i++) {
1537                 stripe = (parity1 + i + 2) % c->chunk_item->num_stripes;
1538 
1539                 stripes[stripe].start = stripes[stripe].end = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
1540             }
1541 
1542             stripes[parity1].start = stripes[parity1].end = stripes[parity2].start = stripes[parity2].end =
1543                 startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
1544 
1545             if (length - pos > c->chunk_item->num_stripes * num_data_stripes * c->chunk_item->stripe_length) {
1546                 skip = (ULONG)(((length - pos) / (c->chunk_item->num_stripes * num_data_stripes * c->chunk_item->stripe_length)) - 1);
1547 
1548                 for (i = 0; i < c->chunk_item->num_stripes; i++) {
1549                     stripes[i].end += skip * c->chunk_item->num_stripes * c->chunk_item->stripe_length;
1550                 }
1551 
1552                 pos += skip * num_data_stripes * c->chunk_item->num_stripes * c->chunk_item->stripe_length;
1553             }
1554         } else if (length - pos >= c->chunk_item->stripe_length * num_data_stripes) {
1555             for (i = 0; i < c->chunk_item->num_stripes; i++) {
1556                 stripes[i].end += c->chunk_item->stripe_length;
1557             }
1558 
1559             pos += c->chunk_item->stripe_length * num_data_stripes;
1560         } else {
1561             UINT16 stripe = (parity1 + 2) % c->chunk_item->num_stripes;
1562 
1563             i = 0;
1564             while (stripe != parity1) {
1565                 if (endoffstripe == i) {
1566                     stripes[stripe].end = endoff + 1;
1567                     break;
1568                 } else if (endoffstripe > i)
1569                     stripes[stripe].end = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length;
1570 
1571                 i++;
1572                 stripe = (stripe + 1) % c->chunk_item->num_stripes;
1573             }
1574 
1575             break;
1576         }
1577     }
1578 
1579     parity_start = 0xffffffffffffffff;
1580     parity_end = 0;
1581 
1582     for (i = 0; i < c->chunk_item->num_stripes; i++) {
1583         if (stripes[i].start != 0 || stripes[i].end != 0) {
1584             parity_start = min(stripes[i].start, parity_start);
1585             parity_end = max(stripes[i].end, parity_end);
1586         }
1587     }
1588 
1589     if (parity_end == parity_start) {
1590         Status = STATUS_SUCCESS;
1591         goto exit;
1592     }
1593 
1594     parity1 = (((address - c->offset) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes;
1595     stripes[parity1].start = stripes[(parity1 + 1) % c->chunk_item->num_stripes].start = parity_start;
1596 
1597     parity1 = (((address - c->offset + length - 1) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes;
1598     stripes[parity1].end = stripes[(parity1 + 1) % c->chunk_item->num_stripes].end = parity_end;
1599 
1600     log_stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(log_stripe) * num_data_stripes, ALLOC_TAG);
1601     if (!log_stripes) {
1602         ERR("out of memory\n");
1603         Status = STATUS_INSUFFICIENT_RESOURCES;
1604         goto exit;
1605     }
1606 
1607     RtlZeroMemory(log_stripes, sizeof(log_stripe) * num_data_stripes);
1608 
1609     for (i = 0; i < num_data_stripes; i++) {
1610         log_stripes[i].mdl = IoAllocateMdl(NULL, (ULONG)(parity_end - parity_start), FALSE, FALSE, NULL);
1611         if (!log_stripes[i].mdl) {
1612             ERR("out of memory\n");
1613             Status = STATUS_INSUFFICIENT_RESOURCES;
1614             goto exit;
1615         }
1616 
1617         log_stripes[i].mdl->MdlFlags |= MDL_PARTIAL;
1618         log_stripes[i].pfns = (PFN_NUMBER*)(log_stripes[i].mdl + 1);
1619     }
1620 
1621     wtc->parity1 = ExAllocatePoolWithTag(NonPagedPool, (ULONG)(parity_end - parity_start), ALLOC_TAG);
1622     if (!wtc->parity1) {
1623         ERR("out of memory\n");
1624         Status = STATUS_INSUFFICIENT_RESOURCES;
1625         goto exit;
1626     }
1627 
1628     wtc->parity2 = ExAllocatePoolWithTag(NonPagedPool, (ULONG)(parity_end - parity_start), ALLOC_TAG);
1629     if (!wtc->parity2) {
1630         ERR("out of memory\n");
1631         Status = STATUS_INSUFFICIENT_RESOURCES;
1632         goto exit;
1633     }
1634 
1635     wtc->parity1_mdl = IoAllocateMdl(wtc->parity1, (ULONG)(parity_end - parity_start), FALSE, FALSE, NULL);
1636     if (!wtc->parity1_mdl) {
1637         ERR("out of memory\n");
1638         Status = STATUS_INSUFFICIENT_RESOURCES;
1639         goto exit;
1640     }
1641 
1642     MmBuildMdlForNonPagedPool(wtc->parity1_mdl);
1643 
1644     wtc->parity2_mdl = IoAllocateMdl(wtc->parity2, (ULONG)(parity_end - parity_start), FALSE, FALSE, NULL);
1645     if (!wtc->parity2_mdl) {
1646         ERR("out of memory\n");
1647         Status = STATUS_INSUFFICIENT_RESOURCES;
1648         goto exit;
1649     }
1650 
1651     MmBuildMdlForNonPagedPool(wtc->parity2_mdl);
1652 
1653     if (file_write)
1654         master_mdl = Irp->MdlAddress;
1655     else if (((ULONG_PTR)data % PAGE_SIZE) != 0) {
1656         wtc->scratch = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
1657         if (!wtc->scratch) {
1658             ERR("out of memory\n");
1659             Status = STATUS_INSUFFICIENT_RESOURCES;
1660             goto exit;
1661         }
1662 
1663         RtlCopyMemory(wtc->scratch, (UINT8*)data + irp_offset, length);
1664 
1665         master_mdl = IoAllocateMdl(wtc->scratch, length, FALSE, FALSE, NULL);
1666         if (!master_mdl) {
1667             ERR("out of memory\n");
1668             Status = STATUS_INSUFFICIENT_RESOURCES;
1669             goto exit;
1670         }
1671 
1672         MmBuildMdlForNonPagedPool(master_mdl);
1673 
1674         wtc->mdl = master_mdl;
1675     } else {
1676         master_mdl = IoAllocateMdl((UINT8*)data + irp_offset, length, FALSE, FALSE, NULL);
1677         if (!master_mdl) {
1678             ERR("out of memory\n");
1679             Status = STATUS_INSUFFICIENT_RESOURCES;
1680             goto exit;
1681         }
1682 
1683         Status = STATUS_SUCCESS;
1684 
1685         _SEH2_TRY {
1686             MmProbeAndLockPages(master_mdl, KernelMode, IoReadAccess);
1687         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
1688             Status = _SEH2_GetExceptionCode();
1689         } _SEH2_END;
1690 
1691         if (!NT_SUCCESS(Status)) {
1692             ERR("MmProbeAndLockPages threw exception %08x\n", Status);
1693             IoFreeMdl(master_mdl);
1694             goto exit;
1695         }
1696 
1697         wtc->mdl = master_mdl;
1698     }
1699 
1700     pfns = (PFN_NUMBER*)(master_mdl + 1);
1701     parity1_pfns = (PFN_NUMBER*)(wtc->parity1_mdl + 1);
1702     parity2_pfns = (PFN_NUMBER*)(wtc->parity2_mdl + 1);
1703 
1704     if (file_write)
1705         pfns = &pfns[irp_offset >> PAGE_SHIFT];
1706 
1707     for (i = 0; i < c->chunk_item->num_stripes; i++) {
1708         if (stripes[i].start != stripes[i].end) {
1709             stripes[i].mdl = IoAllocateMdl((UINT8*)MmGetMdlVirtualAddress(master_mdl) + irp_offset, (ULONG)(stripes[i].end - stripes[i].start), FALSE, FALSE, NULL);
1710             if (!stripes[i].mdl) {
1711                 ERR("IoAllocateMdl failed\n");
1712                 Status = STATUS_INSUFFICIENT_RESOURCES;
1713                 goto exit;
1714             }
1715         }
1716     }
1717 
1718     stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes, ALLOC_TAG);
1719     if (!stripeoff) {
1720         ERR("out of memory\n");
1721         Status = STATUS_INSUFFICIENT_RESOURCES;
1722         goto exit;
1723     }
1724 
1725     RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes);
1726 
1727     pos = 0;
1728     parity_pos = 0;
1729 
1730     while (pos < length) {
1731         PFN_NUMBER* stripe_pfns;
1732 
1733         parity1 = (((address - c->offset + pos) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes;
1734 
1735         if (pos == 0) {
1736             UINT16 stripe = (parity1 + startoffstripe + 2) % c->chunk_item->num_stripes, parity2;
1737             UINT32 writelen = (UINT32)min(length - pos, min(stripes[stripe].end - stripes[stripe].start,
1738                                                             c->chunk_item->stripe_length - (stripes[stripe].start % c->chunk_item->stripe_length)));
1739             UINT32 maxwritelen = writelen;
1740 
1741             stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
1742 
1743             RtlCopyMemory(stripe_pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1744 
1745             RtlCopyMemory(log_stripes[startoffstripe].pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1746             log_stripes[startoffstripe].pfns += writelen >> PAGE_SHIFT;
1747 
1748             stripeoff[stripe] = writelen;
1749             pos += writelen;
1750 
1751             stripe = (stripe + 1) % c->chunk_item->num_stripes;
1752             i = startoffstripe + 1;
1753 
1754             while (stripe != parity1) {
1755                 stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
1756                 writelen = (UINT32)min(length - pos, min(stripes[stripe].end - stripes[stripe].start, c->chunk_item->stripe_length));
1757 
1758                 if (writelen == 0)
1759                     break;
1760 
1761                 if (writelen > maxwritelen)
1762                     maxwritelen = writelen;
1763 
1764                 RtlCopyMemory(stripe_pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1765 
1766                 RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1767                 log_stripes[i].pfns += writelen >> PAGE_SHIFT;
1768 
1769                 stripeoff[stripe] = writelen;
1770                 pos += writelen;
1771 
1772                 stripe = (stripe + 1) % c->chunk_item->num_stripes;
1773                 i++;
1774             }
1775 
1776             stripe_pfns = (PFN_NUMBER*)(stripes[parity1].mdl + 1);
1777             RtlCopyMemory(stripe_pfns, parity1_pfns, maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1778             stripeoff[parity1] = maxwritelen;
1779 
1780             parity2 = (parity1 + 1) % c->chunk_item->num_stripes;
1781 
1782             stripe_pfns = (PFN_NUMBER*)(stripes[parity2].mdl + 1);
1783             RtlCopyMemory(stripe_pfns, parity2_pfns, maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1784             stripeoff[parity2] = maxwritelen;
1785 
1786             parity_pos = maxwritelen;
1787         } else if (length - pos >= c->chunk_item->stripe_length * num_data_stripes) {
1788             UINT16 stripe = (parity1 + 2) % c->chunk_item->num_stripes, parity2;
1789 
1790             i = 0;
1791             while (stripe != parity1) {
1792                 stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
1793 
1794                 RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT));
1795 
1796                 RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT));
1797                 log_stripes[i].pfns += c->chunk_item->stripe_length >> PAGE_SHIFT;
1798 
1799                 stripeoff[stripe] += c->chunk_item->stripe_length;
1800                 pos += c->chunk_item->stripe_length;
1801 
1802                 stripe = (stripe + 1) % c->chunk_item->num_stripes;
1803                 i++;
1804             }
1805 
1806             stripe_pfns = (PFN_NUMBER*)(stripes[parity1].mdl + 1);
1807             RtlCopyMemory(&stripe_pfns[stripeoff[parity1] >> PAGE_SHIFT], &parity1_pfns[parity_pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT));
1808             stripeoff[parity1] += c->chunk_item->stripe_length;
1809 
1810             parity2 = (parity1 + 1) % c->chunk_item->num_stripes;
1811 
1812             stripe_pfns = (PFN_NUMBER*)(stripes[parity2].mdl + 1);
1813             RtlCopyMemory(&stripe_pfns[stripeoff[parity2] >> PAGE_SHIFT], &parity2_pfns[parity_pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT));
1814             stripeoff[parity2] += c->chunk_item->stripe_length;
1815 
1816             parity_pos += c->chunk_item->stripe_length;
1817         } else {
1818             UINT16 stripe = (parity1 + 2) % c->chunk_item->num_stripes, parity2;
1819             UINT32 writelen, maxwritelen = 0;
1820 
1821             i = 0;
1822             while (pos < length) {
1823                 stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1);
1824                 writelen = (UINT32)min(length - pos, min(stripes[stripe].end - stripes[stripe].start, c->chunk_item->stripe_length));
1825 
1826                 if (writelen == 0)
1827                     break;
1828 
1829                 if (writelen > maxwritelen)
1830                     maxwritelen = writelen;
1831 
1832                 RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1833 
1834                 RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1835                 log_stripes[i].pfns += writelen >> PAGE_SHIFT;
1836 
1837                 stripeoff[stripe] += writelen;
1838                 pos += writelen;
1839 
1840                 stripe = (stripe + 1) % c->chunk_item->num_stripes;
1841                 i++;
1842             }
1843 
1844             stripe_pfns = (PFN_NUMBER*)(stripes[parity1].mdl + 1);
1845             RtlCopyMemory(&stripe_pfns[stripeoff[parity1] >> PAGE_SHIFT], &parity1_pfns[parity_pos >> PAGE_SHIFT], maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1846 
1847             parity2 = (parity1 + 1) % c->chunk_item->num_stripes;
1848 
1849             stripe_pfns = (PFN_NUMBER*)(stripes[parity2].mdl + 1);
1850             RtlCopyMemory(&stripe_pfns[stripeoff[parity2] >> PAGE_SHIFT], &parity2_pfns[parity_pos >> PAGE_SHIFT], maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT);
1851         }
1852     }
1853 
1854     for (i = 0; i < num_data_stripes; i++) {
1855         UINT8* ss = MmGetSystemAddressForMdlSafe(log_stripes[c->chunk_item->num_stripes - 3 - i].mdl, priority);
1856 
1857         if (i == 0) {
1858             RtlCopyMemory(wtc->parity1, ss, (ULONG)(parity_end - parity_start));
1859             RtlCopyMemory(wtc->parity2, ss, (ULONG)(parity_end - parity_start));
1860         } else {
1861             do_xor(wtc->parity1, ss, (UINT32)(parity_end - parity_start));
1862 
1863             galois_double(wtc->parity2, (UINT32)(parity_end - parity_start));
1864             do_xor(wtc->parity2, ss, (UINT32)(parity_end - parity_start));
1865         }
1866     }
1867 
1868     Status = STATUS_SUCCESS;
1869 
1870 exit:
1871     if (log_stripes) {
1872         for (i = 0; i < num_data_stripes; i++) {
1873             if (log_stripes[i].mdl)
1874                 IoFreeMdl(log_stripes[i].mdl);
1875         }
1876 
1877         ExFreePool(log_stripes);
1878     }
1879 
1880     if (stripeoff)
1881         ExFreePool(stripeoff);
1882 
1883     return Status;
1884 }
1885 
1886 NTSTATUS write_data(_In_ device_extension* Vcb, _In_ UINT64 address, _In_reads_bytes_(length) void* data, _In_ UINT32 length, _In_ write_data_context* wtc,
1887                     _In_opt_ PIRP Irp, _In_opt_ chunk* c, _In_ BOOL file_write, _In_ UINT64 irp_offset, _In_ ULONG priority) {
1888     NTSTATUS Status;
1889     UINT32 i;
1890     CHUNK_ITEM_STRIPE* cis;
1891     write_stripe* stripes = NULL;
1892     UINT64 total_writing = 0;
1893     ULONG allowed_missing, missing;
1894 
1895     TRACE("(%p, %llx, %p, %x)\n", Vcb, address, data, length);
1896 
1897     if (!c) {
1898         c = get_chunk_from_address(Vcb, address);
1899         if (!c) {
1900             ERR("could not get chunk for address %llx\n", address);
1901             return STATUS_INTERNAL_ERROR;
1902         }
1903     }
1904 
1905     stripes = ExAllocatePoolWithTag(PagedPool, sizeof(write_stripe) * c->chunk_item->num_stripes, ALLOC_TAG);
1906     if (!stripes) {
1907         ERR("out of memory\n");
1908         return STATUS_INSUFFICIENT_RESOURCES;
1909     }
1910 
1911     RtlZeroMemory(stripes, sizeof(write_stripe) * c->chunk_item->num_stripes);
1912 
1913     cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
1914 
1915     if (c->chunk_item->type & BLOCK_FLAG_RAID0) {
1916         Status = prepare_raid0_write(c, address, data, length, stripes, file_write ? Irp : NULL, irp_offset, wtc);
1917         if (!NT_SUCCESS(Status)) {
1918             ERR("prepare_raid0_write returned %08x\n", Status);
1919             goto prepare_failed;
1920         }
1921 
1922         allowed_missing = 0;
1923     } else if (c->chunk_item->type & BLOCK_FLAG_RAID10) {
1924         Status = prepare_raid10_write(c, address, data, length, stripes, file_write ? Irp : NULL, irp_offset, wtc);
1925         if (!NT_SUCCESS(Status)) {
1926             ERR("prepare_raid10_write returned %08x\n", Status);
1927             goto prepare_failed;
1928         }
1929 
1930         allowed_missing = 1;
1931     } else if (c->chunk_item->type & BLOCK_FLAG_RAID5) {
1932         Status = prepare_raid5_write(Vcb, c, address, data, length, stripes, file_write ? Irp : NULL, irp_offset, priority, wtc);
1933         if (!NT_SUCCESS(Status)) {
1934             ERR("prepare_raid5_write returned %08x\n", Status);
1935             goto prepare_failed;
1936         }
1937 
1938         allowed_missing = 1;
1939     } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) {
1940         Status = prepare_raid6_write(Vcb, c, address, data, length, stripes, file_write ? Irp : NULL, irp_offset, priority, wtc);
1941         if (!NT_SUCCESS(Status)) {
1942             ERR("prepare_raid6_write returned %08x\n", Status);
1943             goto prepare_failed;
1944         }
1945 
1946         allowed_missing = 2;
1947     } else {  // write same data to every location - SINGLE, DUP, RAID1
1948         for (i = 0; i < c->chunk_item->num_stripes; i++) {
1949             stripes[i].start = address - c->offset;
1950             stripes[i].end = stripes[i].start + length;
1951             stripes[i].data = data;
1952             stripes[i].irp_offset = irp_offset;
1953 
1954             if (c->devices[i]->devobj) {
1955                 if (file_write) {
1956                     UINT8* va;
1957                     ULONG writelen = (ULONG)(stripes[i].end - stripes[i].start);
1958 
1959                     va = (UINT8*)MmGetMdlVirtualAddress(Irp->MdlAddress) + stripes[i].irp_offset;
1960 
1961                     stripes[i].mdl = IoAllocateMdl(va, writelen, FALSE, FALSE, NULL);
1962                     if (!stripes[i].mdl) {
1963                         ERR("IoAllocateMdl failed\n");
1964                         Status = STATUS_INSUFFICIENT_RESOURCES;
1965                         goto prepare_failed;
1966                     }
1967 
1968                     IoBuildPartialMdl(Irp->MdlAddress, stripes[i].mdl, va, writelen);
1969                 } else {
1970                     stripes[i].mdl = IoAllocateMdl(stripes[i].data, (ULONG)(stripes[i].end - stripes[i].start), FALSE, FALSE, NULL);
1971                     if (!stripes[i].mdl) {
1972                         ERR("IoAllocateMdl failed\n");
1973                         Status = STATUS_INSUFFICIENT_RESOURCES;
1974                         goto prepare_failed;
1975                     }
1976 
1977                     Status = STATUS_SUCCESS;
1978 
1979                     _SEH2_TRY {
1980                         MmProbeAndLockPages(stripes[i].mdl, KernelMode, IoReadAccess);
1981                     } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
1982                         Status = _SEH2_GetExceptionCode();
1983                     } _SEH2_END;
1984 
1985                     if (!NT_SUCCESS(Status)) {
1986                         ERR("MmProbeAndLockPages threw exception %08x\n", Status);
1987                         IoFreeMdl(stripes[i].mdl);
1988                         stripes[i].mdl = NULL;
1989                         goto prepare_failed;
1990                     }
1991                 }
1992             }
1993         }
1994 
1995         allowed_missing = c->chunk_item->num_stripes - 1;
1996     }
1997 
1998     missing = 0;
1999     for (i = 0; i < c->chunk_item->num_stripes; i++) {
2000         if (!c->devices[i]->devobj)
2001             missing++;
2002     }
2003 
2004     if (missing > allowed_missing) {
2005         ERR("cannot write as %u missing devices (maximum %u)\n", missing, allowed_missing);
2006         Status = STATUS_DEVICE_NOT_READY;
2007         goto prepare_failed;
2008     }
2009 
2010     for (i = 0; i < c->chunk_item->num_stripes; i++) {
2011         write_data_stripe* stripe;
2012         PIO_STACK_LOCATION IrpSp;
2013 
2014         stripe = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_stripe), ALLOC_TAG);
2015         if (!stripe) {
2016             ERR("out of memory\n");
2017             Status = STATUS_INSUFFICIENT_RESOURCES;
2018             goto end;
2019         }
2020 
2021         if (stripes[i].start == stripes[i].end || !c->devices[i]->devobj) {
2022             stripe->status = WriteDataStatus_Ignore;
2023             stripe->Irp = NULL;
2024             stripe->buf = stripes[i].data;
2025             stripe->mdl = NULL;
2026         } else {
2027             stripe->context = (struct _write_data_context*)wtc;
2028             stripe->buf = stripes[i].data;
2029             stripe->device = c->devices[i];
2030             RtlZeroMemory(&stripe->iosb, sizeof(IO_STATUS_BLOCK));
2031             stripe->status = WriteDataStatus_Pending;
2032             stripe->mdl = stripes[i].mdl;
2033 
2034             if (!Irp) {
2035                 stripe->Irp = IoAllocateIrp(stripe->device->devobj->StackSize, FALSE);
2036 
2037                 if (!stripe->Irp) {
2038                     ERR("IoAllocateIrp failed\n");
2039                     ExFreePool(stripe);
2040                     Status = STATUS_INSUFFICIENT_RESOURCES;
2041                     goto end;
2042                 }
2043             } else {
2044                 stripe->Irp = IoMakeAssociatedIrp(Irp, stripe->device->devobj->StackSize);
2045 
2046                 if (!stripe->Irp) {
2047                     ERR("IoMakeAssociatedIrp failed\n");
2048                     ExFreePool(stripe);
2049                     Status = STATUS_INSUFFICIENT_RESOURCES;
2050                     goto end;
2051                 }
2052             }
2053 
2054             IrpSp = IoGetNextIrpStackLocation(stripe->Irp);
2055             IrpSp->MajorFunction = IRP_MJ_WRITE;
2056 
2057             if (stripe->device->devobj->Flags & DO_BUFFERED_IO) {
2058                 stripe->Irp->AssociatedIrp.SystemBuffer = MmGetSystemAddressForMdlSafe(stripes[i].mdl, priority);
2059 
2060                 stripe->Irp->Flags = IRP_BUFFERED_IO;
2061             } else if (stripe->device->devobj->Flags & DO_DIRECT_IO)
2062                 stripe->Irp->MdlAddress = stripe->mdl;
2063             else
2064                 stripe->Irp->UserBuffer = MmGetSystemAddressForMdlSafe(stripes[i].mdl, priority);
2065 
2066 #ifdef DEBUG_PARANOID
2067             if (stripes[i].end < stripes[i].start) {
2068                 ERR("trying to write stripe with negative length (%llx < %llx)\n", stripes[i].end, stripes[i].start);
2069                 int3;
2070             }
2071 #endif
2072 
2073             IrpSp->Parameters.Write.Length = (ULONG)(stripes[i].end - stripes[i].start);
2074             IrpSp->Parameters.Write.ByteOffset.QuadPart = stripes[i].start + cis[i].offset;
2075 
2076             total_writing += IrpSp->Parameters.Write.Length;
2077 
2078             stripe->Irp->UserIosb = &stripe->iosb;
2079             wtc->stripes_left++;
2080 
2081             IoSetCompletionRoutine(stripe->Irp, write_data_completion, stripe, TRUE, TRUE, TRUE);
2082         }
2083 
2084         InsertTailList(&wtc->stripes, &stripe->list_entry);
2085     }
2086 
2087     if (diskacc)
2088         fFsRtlUpdateDiskCounters(0, total_writing);
2089 
2090     Status = STATUS_SUCCESS;
2091 
2092 end:
2093 
2094     if (stripes) ExFreePool(stripes);
2095 
2096     if (!NT_SUCCESS(Status))
2097         free_write_data_stripes(wtc);
2098 
2099     return Status;
2100 
2101 prepare_failed:
2102     for (i = 0; i < c->chunk_item->num_stripes; i++) {
2103         if (stripes[i].mdl && (i == 0 || stripes[i].mdl != stripes[i-1].mdl)) {
2104             if (stripes[i].mdl->MdlFlags & MDL_PAGES_LOCKED)
2105                 MmUnlockPages(stripes[i].mdl);
2106 
2107             IoFreeMdl(stripes[i].mdl);
2108         }
2109     }
2110 
2111     if (wtc->parity1_mdl) {
2112         if (wtc->parity1_mdl->MdlFlags & MDL_PAGES_LOCKED)
2113             MmUnlockPages(wtc->parity1_mdl);
2114 
2115         IoFreeMdl(wtc->parity1_mdl);
2116         wtc->parity1_mdl = NULL;
2117     }
2118 
2119     if (wtc->parity2_mdl) {
2120         if (wtc->parity2_mdl->MdlFlags & MDL_PAGES_LOCKED)
2121             MmUnlockPages(wtc->parity2_mdl);
2122 
2123         IoFreeMdl(wtc->parity2_mdl);
2124         wtc->parity2_mdl = NULL;
2125     }
2126 
2127     if (wtc->mdl) {
2128         if (wtc->mdl->MdlFlags & MDL_PAGES_LOCKED)
2129             MmUnlockPages(wtc->mdl);
2130 
2131         IoFreeMdl(wtc->mdl);
2132         wtc->mdl = NULL;
2133     }
2134 
2135     if (wtc->parity1) {
2136         ExFreePool(wtc->parity1);
2137         wtc->parity1 = NULL;
2138     }
2139 
2140     if (wtc->parity2) {
2141         ExFreePool(wtc->parity2);
2142         wtc->parity2 = NULL;
2143     }
2144 
2145     if (wtc->scratch) {
2146         ExFreePool(wtc->scratch);
2147         wtc->scratch = NULL;
2148     }
2149 
2150     ExFreePool(stripes);
2151     return Status;
2152 }
2153 
2154 void get_raid56_lock_range(chunk* c, UINT64 address, UINT64 length, UINT64* lockaddr, UINT64* locklen) {
2155     UINT64 startoff, endoff;
2156     UINT16 startoffstripe, endoffstripe, datastripes;
2157 
2158     datastripes = c->chunk_item->num_stripes - (c->chunk_item->type & BLOCK_FLAG_RAID5 ? 1 : 2);
2159 
2160     get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, datastripes, &startoff, &startoffstripe);
2161     get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, datastripes, &endoff, &endoffstripe);
2162 
2163     startoff -= startoff % c->chunk_item->stripe_length;
2164     endoff = sector_align(endoff, c->chunk_item->stripe_length);
2165 
2166     *lockaddr = c->offset + (startoff * datastripes);
2167     *locklen = (endoff - startoff) * datastripes;
2168 }
2169 
2170 NTSTATUS write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp, chunk* c, BOOL file_write, UINT64 irp_offset, ULONG priority) {
2171     write_data_context wtc;
2172     NTSTATUS Status;
2173     UINT64 lockaddr, locklen;
2174 
2175     KeInitializeEvent(&wtc.Event, NotificationEvent, FALSE);
2176     InitializeListHead(&wtc.stripes);
2177     wtc.stripes_left = 0;
2178     wtc.parity1 = wtc.parity2 = wtc.scratch = NULL;
2179     wtc.mdl = wtc.parity1_mdl = wtc.parity2_mdl = NULL;
2180 
2181     if (!c) {
2182         c = get_chunk_from_address(Vcb, address);
2183         if (!c) {
2184             ERR("could not get chunk for address %llx\n", address);
2185             return STATUS_INTERNAL_ERROR;
2186         }
2187     }
2188 
2189     if (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6) {
2190         get_raid56_lock_range(c, address, length, &lockaddr, &locklen);
2191         chunk_lock_range(Vcb, c, lockaddr, locklen);
2192     }
2193 
2194     _SEH2_TRY {
2195         Status = write_data(Vcb, address, data, length, &wtc, Irp, c, file_write, irp_offset, priority);
2196     } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
2197         Status = _SEH2_GetExceptionCode();
2198     } _SEH2_END;
2199 
2200     if (!NT_SUCCESS(Status)) {
2201         ERR("write_data returned %08x\n", Status);
2202 
2203         if (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6)
2204             chunk_unlock_range(Vcb, c, lockaddr, locklen);
2205 
2206         free_write_data_stripes(&wtc);
2207         return Status;
2208     }
2209 
2210     if (wtc.stripes.Flink != &wtc.stripes) {
2211         // launch writes and wait
2212         LIST_ENTRY* le = wtc.stripes.Flink;
2213         BOOL no_wait = TRUE;
2214 
2215         while (le != &wtc.stripes) {
2216             write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
2217 
2218             if (stripe->status != WriteDataStatus_Ignore) {
2219                 IoCallDriver(stripe->device->devobj, stripe->Irp);
2220                 no_wait = FALSE;
2221             }
2222 
2223             le = le->Flink;
2224         }
2225 
2226         if (!no_wait)
2227             KeWaitForSingleObject(&wtc.Event, Executive, KernelMode, FALSE, NULL);
2228 
2229         le = wtc.stripes.Flink;
2230         while (le != &wtc.stripes) {
2231             write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
2232 
2233             if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) {
2234                 Status = stripe->iosb.Status;
2235 
2236                 log_device_error(Vcb, stripe->device, BTRFS_DEV_STAT_WRITE_ERRORS);
2237                 break;
2238             }
2239 
2240             le = le->Flink;
2241         }
2242 
2243         free_write_data_stripes(&wtc);
2244     }
2245 
2246     if (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6)
2247         chunk_unlock_range(Vcb, c, lockaddr, locklen);
2248 
2249     return Status;
2250 }
2251 
2252 _Function_class_(IO_COMPLETION_ROUTINE)
2253 #ifdef __REACTOS__
2254 static NTSTATUS NTAPI write_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
2255 #else
2256 static NTSTATUS write_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) {
2257 #endif
2258     write_data_stripe* stripe = conptr;
2259     write_data_context* context = (write_data_context*)stripe->context;
2260     LIST_ENTRY* le;
2261 
2262     UNUSED(DeviceObject);
2263 
2264     // FIXME - we need a lock here
2265 
2266     if (stripe->status == WriteDataStatus_Cancelling) {
2267         stripe->status = WriteDataStatus_Cancelled;
2268         goto end;
2269     }
2270 
2271     stripe->iosb = Irp->IoStatus;
2272 
2273     if (NT_SUCCESS(Irp->IoStatus.Status)) {
2274         stripe->status = WriteDataStatus_Success;
2275     } else {
2276         le = context->stripes.Flink;
2277 
2278         stripe->status = WriteDataStatus_Error;
2279 
2280         while (le != &context->stripes) {
2281             write_data_stripe* s2 = CONTAINING_RECORD(le, write_data_stripe, list_entry);
2282 
2283             if (s2->status == WriteDataStatus_Pending) {
2284                 s2->status = WriteDataStatus_Cancelling;
2285                 IoCancelIrp(s2->Irp);
2286             }
2287 
2288             le = le->Flink;
2289         }
2290     }
2291 
2292 end:
2293     if (InterlockedDecrement(&context->stripes_left) == 0)
2294         KeSetEvent(&context->Event, 0, FALSE);
2295 
2296     return STATUS_MORE_PROCESSING_REQUIRED;
2297 }
2298 
2299 void free_write_data_stripes(write_data_context* wtc) {
2300     LIST_ENTRY* le;
2301     PMDL last_mdl = NULL;
2302 
2303     if (wtc->parity1_mdl) {
2304         if (wtc->parity1_mdl->MdlFlags & MDL_PAGES_LOCKED)
2305             MmUnlockPages(wtc->parity1_mdl);
2306 
2307         IoFreeMdl(wtc->parity1_mdl);
2308     }
2309 
2310     if (wtc->parity2_mdl) {
2311         if (wtc->parity2_mdl->MdlFlags & MDL_PAGES_LOCKED)
2312             MmUnlockPages(wtc->parity2_mdl);
2313 
2314         IoFreeMdl(wtc->parity2_mdl);
2315     }
2316 
2317     if (wtc->mdl) {
2318         if (wtc->mdl->MdlFlags & MDL_PAGES_LOCKED)
2319             MmUnlockPages(wtc->mdl);
2320 
2321         IoFreeMdl(wtc->mdl);
2322     }
2323 
2324     if (wtc->parity1)
2325         ExFreePool(wtc->parity1);
2326 
2327     if (wtc->parity2)
2328         ExFreePool(wtc->parity2);
2329 
2330     if (wtc->scratch)
2331         ExFreePool(wtc->scratch);
2332 
2333     le = wtc->stripes.Flink;
2334     while (le != &wtc->stripes) {
2335         write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry);
2336 
2337         if (stripe->mdl && stripe->mdl != last_mdl) {
2338             if (stripe->mdl->MdlFlags & MDL_PAGES_LOCKED)
2339                 MmUnlockPages(stripe->mdl);
2340 
2341             IoFreeMdl(stripe->mdl);
2342         }
2343 
2344         last_mdl = stripe->mdl;
2345 
2346         if (stripe->Irp)
2347             IoFreeIrp(stripe->Irp);
2348 
2349         le = le->Flink;
2350     }
2351 
2352     while (!IsListEmpty(&wtc->stripes)) {
2353         write_data_stripe* stripe = CONTAINING_RECORD(RemoveHeadList(&wtc->stripes), write_data_stripe, list_entry);
2354 
2355         ExFreePool(stripe);
2356     }
2357 }
2358 
2359 void add_extent(_In_ fcb* fcb, _In_ LIST_ENTRY* prevextle, _In_ __drv_aliasesMem extent* newext) {
2360     LIST_ENTRY* le = prevextle->Flink;
2361 
2362     while (le != &fcb->extents) {
2363         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
2364 
2365         if (ext->offset >= newext->offset) {
2366             InsertHeadList(ext->list_entry.Blink, &newext->list_entry);
2367             return;
2368         }
2369 
2370         le = le->Flink;
2371     }
2372 
2373     InsertTailList(&fcb->extents, &newext->list_entry);
2374 }
2375 
2376 NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, PIRP Irp, LIST_ENTRY* rollback) {
2377     NTSTATUS Status;
2378     LIST_ENTRY* le;
2379 
2380     le = fcb->extents.Flink;
2381 
2382     while (le != &fcb->extents) {
2383         LIST_ENTRY* le2 = le->Flink;
2384         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
2385         EXTENT_DATA* ed = &ext->extent_data;
2386         EXTENT_DATA2* ed2 = NULL;
2387         UINT64 len;
2388 
2389         if (!ext->ignore) {
2390             if (ed->type != EXTENT_TYPE_INLINE)
2391                 ed2 = (EXTENT_DATA2*)ed->data;
2392 
2393             len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
2394 
2395             if (ext->offset < end_data && ext->offset + len > start_data) {
2396                 if (ed->type == EXTENT_TYPE_INLINE) {
2397                     if (start_data <= ext->offset && end_data >= ext->offset + len) { // remove all
2398                         remove_fcb_extent(fcb, ext, rollback);
2399 
2400                         fcb->inode_item.st_blocks -= len;
2401                         fcb->inode_item_changed = TRUE;
2402                     } else {
2403                         ERR("trying to split inline extent\n");
2404 #ifdef DEBUG_PARANOID
2405                         int3;
2406 #endif
2407                         return STATUS_INTERNAL_ERROR;
2408                     }
2409                 } else if (ed->type != EXTENT_TYPE_INLINE) {
2410                     if (start_data <= ext->offset && end_data >= ext->offset + len) { // remove all
2411                         if (ed2->size != 0) {
2412                             chunk* c;
2413 
2414                             fcb->inode_item.st_blocks -= len;
2415                             fcb->inode_item_changed = TRUE;
2416 
2417                             c = get_chunk_from_address(Vcb, ed2->address);
2418 
2419                             if (!c) {
2420                                 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
2421                             } else {
2422                                 Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, -1,
2423                                                                    fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp);
2424                                 if (!NT_SUCCESS(Status)) {
2425                                     ERR("update_changed_extent_ref returned %08x\n", Status);
2426                                     goto end;
2427                                 }
2428                             }
2429                         }
2430 
2431                         remove_fcb_extent(fcb, ext, rollback);
2432                     } else if (start_data <= ext->offset && end_data < ext->offset + len) { // remove beginning
2433                         EXTENT_DATA2* ned2;
2434                         extent* newext;
2435 
2436                         if (ed2->size != 0) {
2437                             fcb->inode_item.st_blocks -= end_data - ext->offset;
2438                             fcb->inode_item_changed = TRUE;
2439                         }
2440 
2441                         newext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
2442                         if (!newext) {
2443                             ERR("out of memory\n");
2444                             Status = STATUS_INSUFFICIENT_RESOURCES;
2445                             goto end;
2446                         }
2447 
2448                         ned2 = (EXTENT_DATA2*)newext->extent_data.data;
2449 
2450                         newext->extent_data.generation = Vcb->superblock.generation;
2451                         newext->extent_data.decoded_size = ed->decoded_size;
2452                         newext->extent_data.compression = ed->compression;
2453                         newext->extent_data.encryption = ed->encryption;
2454                         newext->extent_data.encoding = ed->encoding;
2455                         newext->extent_data.type = ed->type;
2456                         ned2->address = ed2->address;
2457                         ned2->size = ed2->size;
2458                         ned2->offset = ed2->offset + (end_data - ext->offset);
2459                         ned2->num_bytes = ed2->num_bytes - (end_data - ext->offset);
2460 
2461                         newext->offset = end_data;
2462                         newext->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
2463                         newext->unique = ext->unique;
2464                         newext->ignore = FALSE;
2465                         newext->inserted = TRUE;
2466 
2467                         if (ext->csum) {
2468                             if (ed->compression == BTRFS_COMPRESSION_NONE) {
2469                                 newext->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ned2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2470                                 if (!newext->csum) {
2471                                     ERR("out of memory\n");
2472                                     Status = STATUS_INSUFFICIENT_RESOURCES;
2473                                     ExFreePool(newext);
2474                                     goto end;
2475                                 }
2476 
2477                                 RtlCopyMemory(newext->csum, &ext->csum[(end_data - ext->offset) / Vcb->superblock.sector_size],
2478                                               (ULONG)(ned2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size));
2479                             } else {
2480                                 newext->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2481                                 if (!newext->csum) {
2482                                     ERR("out of memory\n");
2483                                     Status = STATUS_INSUFFICIENT_RESOURCES;
2484                                     ExFreePool(newext);
2485                                     goto end;
2486                                 }
2487 
2488                                 RtlCopyMemory(newext->csum, ext->csum, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size));
2489                             }
2490                         } else
2491                             newext->csum = NULL;
2492 
2493                         add_extent(fcb, &ext->list_entry, newext);
2494 
2495                         remove_fcb_extent(fcb, ext, rollback);
2496                     } else if (start_data > ext->offset && end_data >= ext->offset + len) { // remove end
2497                         EXTENT_DATA2* ned2;
2498                         extent* newext;
2499 
2500                         if (ed2->size != 0) {
2501                             fcb->inode_item.st_blocks -= ext->offset + len - start_data;
2502                             fcb->inode_item_changed = TRUE;
2503                         }
2504 
2505                         newext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
2506                         if (!newext) {
2507                             ERR("out of memory\n");
2508                             Status = STATUS_INSUFFICIENT_RESOURCES;
2509                             goto end;
2510                         }
2511 
2512                         ned2 = (EXTENT_DATA2*)newext->extent_data.data;
2513 
2514                         newext->extent_data.generation = Vcb->superblock.generation;
2515                         newext->extent_data.decoded_size = ed->decoded_size;
2516                         newext->extent_data.compression = ed->compression;
2517                         newext->extent_data.encryption = ed->encryption;
2518                         newext->extent_data.encoding = ed->encoding;
2519                         newext->extent_data.type = ed->type;
2520                         ned2->address = ed2->address;
2521                         ned2->size = ed2->size;
2522                         ned2->offset = ed2->offset;
2523                         ned2->num_bytes = start_data - ext->offset;
2524 
2525                         newext->offset = ext->offset;
2526                         newext->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
2527                         newext->unique = ext->unique;
2528                         newext->ignore = FALSE;
2529                         newext->inserted = TRUE;
2530 
2531                         if (ext->csum) {
2532                             if (ed->compression == BTRFS_COMPRESSION_NONE) {
2533                                 newext->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ned2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2534                                 if (!newext->csum) {
2535                                     ERR("out of memory\n");
2536                                     Status = STATUS_INSUFFICIENT_RESOURCES;
2537                                     ExFreePool(newext);
2538                                     goto end;
2539                                 }
2540 
2541                                 RtlCopyMemory(newext->csum, ext->csum, (ULONG)(ned2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size));
2542                             } else {
2543                                 newext->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2544                                 if (!newext->csum) {
2545                                     ERR("out of memory\n");
2546                                     Status = STATUS_INSUFFICIENT_RESOURCES;
2547                                     ExFreePool(newext);
2548                                     goto end;
2549                                 }
2550 
2551                                 RtlCopyMemory(newext->csum, ext->csum, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size));
2552                             }
2553                         } else
2554                             newext->csum = NULL;
2555 
2556                         InsertHeadList(&ext->list_entry, &newext->list_entry);
2557 
2558                         remove_fcb_extent(fcb, ext, rollback);
2559                     } else if (start_data > ext->offset && end_data < ext->offset + len) { // remove middle
2560                         EXTENT_DATA2 *neda2, *nedb2;
2561                         extent *newext1, *newext2;
2562 
2563                         if (ed2->size != 0) {
2564                             chunk* c;
2565 
2566                             fcb->inode_item.st_blocks -= end_data - start_data;
2567                             fcb->inode_item_changed = TRUE;
2568 
2569                             c = get_chunk_from_address(Vcb, ed2->address);
2570 
2571                             if (!c) {
2572                                 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
2573                             } else {
2574                                 Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
2575                                                                    fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp);
2576                                 if (!NT_SUCCESS(Status)) {
2577                                     ERR("update_changed_extent_ref returned %08x\n", Status);
2578                                     goto end;
2579                                 }
2580                             }
2581                         }
2582 
2583                         newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
2584                         if (!newext1) {
2585                             ERR("out of memory\n");
2586                             Status = STATUS_INSUFFICIENT_RESOURCES;
2587                             goto end;
2588                         }
2589 
2590                         newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
2591                         if (!newext2) {
2592                             ERR("out of memory\n");
2593                             Status = STATUS_INSUFFICIENT_RESOURCES;
2594                             ExFreePool(newext1);
2595                             goto end;
2596                         }
2597 
2598                         neda2 = (EXTENT_DATA2*)newext1->extent_data.data;
2599 
2600                         newext1->extent_data.generation = Vcb->superblock.generation;
2601                         newext1->extent_data.decoded_size = ed->decoded_size;
2602                         newext1->extent_data.compression = ed->compression;
2603                         newext1->extent_data.encryption = ed->encryption;
2604                         newext1->extent_data.encoding = ed->encoding;
2605                         newext1->extent_data.type = ed->type;
2606                         neda2->address = ed2->address;
2607                         neda2->size = ed2->size;
2608                         neda2->offset = ed2->offset;
2609                         neda2->num_bytes = start_data - ext->offset;
2610 
2611                         nedb2 = (EXTENT_DATA2*)newext2->extent_data.data;
2612 
2613                         newext2->extent_data.generation = Vcb->superblock.generation;
2614                         newext2->extent_data.decoded_size = ed->decoded_size;
2615                         newext2->extent_data.compression = ed->compression;
2616                         newext2->extent_data.encryption = ed->encryption;
2617                         newext2->extent_data.encoding = ed->encoding;
2618                         newext2->extent_data.type = ed->type;
2619                         nedb2->address = ed2->address;
2620                         nedb2->size = ed2->size;
2621                         nedb2->offset = ed2->offset + (end_data - ext->offset);
2622                         nedb2->num_bytes = ext->offset + len - end_data;
2623 
2624                         newext1->offset = ext->offset;
2625                         newext1->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
2626                         newext1->unique = ext->unique;
2627                         newext1->ignore = FALSE;
2628                         newext1->inserted = TRUE;
2629 
2630                         newext2->offset = end_data;
2631                         newext2->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
2632                         newext2->unique = ext->unique;
2633                         newext2->ignore = FALSE;
2634                         newext2->inserted = TRUE;
2635 
2636                         if (ext->csum) {
2637                             if (ed->compression == BTRFS_COMPRESSION_NONE) {
2638                                 newext1->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(neda2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2639                                 if (!newext1->csum) {
2640                                     ERR("out of memory\n");
2641                                     Status = STATUS_INSUFFICIENT_RESOURCES;
2642                                     ExFreePool(newext1);
2643                                     ExFreePool(newext2);
2644                                     goto end;
2645                                 }
2646 
2647                                 newext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(nedb2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2648                                 if (!newext2->csum) {
2649                                     ERR("out of memory\n");
2650                                     Status = STATUS_INSUFFICIENT_RESOURCES;
2651                                     ExFreePool(newext1->csum);
2652                                     ExFreePool(newext1);
2653                                     ExFreePool(newext2);
2654                                     goto end;
2655                                 }
2656 
2657                                 RtlCopyMemory(newext1->csum, ext->csum, (ULONG)(neda2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size));
2658                                 RtlCopyMemory(newext2->csum, &ext->csum[(end_data - ext->offset) / Vcb->superblock.sector_size],
2659                                               (ULONG)(nedb2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size));
2660                             } else {
2661                                 newext1->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2662                                 if (!newext1->csum) {
2663                                     ERR("out of memory\n");
2664                                     Status = STATUS_INSUFFICIENT_RESOURCES;
2665                                     ExFreePool(newext1);
2666                                     ExFreePool(newext2);
2667                                     goto end;
2668                                 }
2669 
2670                                 newext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2671                                 if (!newext2->csum) {
2672                                     ERR("out of memory\n");
2673                                     Status = STATUS_INSUFFICIENT_RESOURCES;
2674                                     ExFreePool(newext1->csum);
2675                                     ExFreePool(newext1);
2676                                     ExFreePool(newext2);
2677                                     goto end;
2678                                 }
2679 
2680                                 RtlCopyMemory(newext1->csum, ext->csum, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size));
2681                                 RtlCopyMemory(newext2->csum, ext->csum, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size));
2682                             }
2683                         } else {
2684                             newext1->csum = NULL;
2685                             newext2->csum = NULL;
2686                         }
2687 
2688                         InsertHeadList(&ext->list_entry, &newext1->list_entry);
2689                         add_extent(fcb, &newext1->list_entry, newext2);
2690 
2691                         remove_fcb_extent(fcb, ext, rollback);
2692                     }
2693                 }
2694             }
2695         }
2696 
2697         le = le2;
2698     }
2699 
2700     Status = STATUS_SUCCESS;
2701 
2702 end:
2703     fcb->extents_changed = TRUE;
2704     mark_fcb_dirty(fcb);
2705 
2706     return Status;
2707 }
2708 
2709 void add_insert_extent_rollback(LIST_ENTRY* rollback, fcb* fcb, extent* ext) {
2710     rollback_extent* re;
2711 
2712     re = ExAllocatePoolWithTag(NonPagedPool, sizeof(rollback_extent), ALLOC_TAG);
2713     if (!re) {
2714         ERR("out of memory\n");
2715         return;
2716     }
2717 
2718     re->fcb = fcb;
2719     re->ext = ext;
2720 
2721     add_rollback(rollback, ROLLBACK_INSERT_EXTENT, re);
2722 }
2723 
2724 #ifdef _MSC_VER
2725 #pragma warning(push)
2726 #pragma warning(suppress: 28194)
2727 #endif
2728 NTSTATUS add_extent_to_fcb(_In_ fcb* fcb, _In_ UINT64 offset, _In_reads_bytes_(edsize) EXTENT_DATA* ed, _In_ UINT16 edsize,
2729                            _In_ BOOL unique, _In_opt_ _When_(return >= 0, __drv_aliasesMem) UINT32* csum, _In_ LIST_ENTRY* rollback) {
2730     extent* ext;
2731     LIST_ENTRY* le;
2732 
2733     ext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + edsize, ALLOC_TAG);
2734     if (!ext) {
2735         ERR("out of memory\n");
2736         return STATUS_INSUFFICIENT_RESOURCES;
2737     }
2738 
2739     ext->offset = offset;
2740     ext->datalen = edsize;
2741     ext->unique = unique;
2742     ext->ignore = FALSE;
2743     ext->inserted = TRUE;
2744     ext->csum = csum;
2745 
2746     RtlCopyMemory(&ext->extent_data, ed, edsize);
2747 
2748     le = fcb->extents.Flink;
2749     while (le != &fcb->extents) {
2750         extent* oldext = CONTAINING_RECORD(le, extent, list_entry);
2751 
2752         if (oldext->offset >= offset) {
2753             InsertHeadList(le->Blink, &ext->list_entry);
2754             goto end;
2755         }
2756 
2757         le = le->Flink;
2758     }
2759 
2760     InsertTailList(&fcb->extents, &ext->list_entry);
2761 
2762 end:
2763     add_insert_extent_rollback(rollback, fcb, ext);
2764 
2765     return STATUS_SUCCESS;
2766 }
2767 #ifdef _MSC_VER
2768 #pragma warning(pop)
2769 #endif
2770 
2771 static void remove_fcb_extent(fcb* fcb, extent* ext, LIST_ENTRY* rollback) {
2772     if (!ext->ignore) {
2773         rollback_extent* re;
2774 
2775         ext->ignore = TRUE;
2776 
2777         re = ExAllocatePoolWithTag(NonPagedPool, sizeof(rollback_extent), ALLOC_TAG);
2778         if (!re) {
2779             ERR("out of memory\n");
2780             return;
2781         }
2782 
2783         re->fcb = fcb;
2784         re->ext = ext;
2785 
2786         add_rollback(rollback, ROLLBACK_DELETE_EXTENT, re);
2787     }
2788 }
2789 
2790 NTSTATUS calc_csum(_In_ device_extension* Vcb, _In_reads_bytes_(sectors*Vcb->superblock.sector_size) UINT8* data,
2791                    _In_ UINT32 sectors, _Out_writes_bytes_(sectors*sizeof(UINT32)) UINT32* csum) {
2792     NTSTATUS Status;
2793     calc_job* cj;
2794 
2795     // From experimenting, it seems that 40 sectors is roughly the crossover
2796     // point where offloading the crc32 calculation becomes worth it.
2797 
2798     if (sectors < 40 || KeQueryActiveProcessorCount(NULL) < 2) {
2799         ULONG j;
2800 
2801         for (j = 0; j < sectors; j++) {
2802             csum[j] = ~calc_crc32c(0xffffffff, data + (j * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
2803         }
2804 
2805         return STATUS_SUCCESS;
2806     }
2807 
2808     Status = add_calc_job(Vcb, data, sectors, csum, &cj);
2809     if (!NT_SUCCESS(Status)) {
2810         ERR("add_calc_job returned %08x\n", Status);
2811         return Status;
2812     }
2813 
2814     KeWaitForSingleObject(&cj->event, Executive, KernelMode, FALSE, NULL);
2815     free_calc_job(cj);
2816 
2817     return STATUS_SUCCESS;
2818 }
2819 
2820 _Requires_lock_held_(c->lock)
2821 _When_(return != 0, _Releases_lock_(c->lock))
2822 BOOL insert_extent_chunk(_In_ device_extension* Vcb, _In_ fcb* fcb, _In_ chunk* c, _In_ UINT64 start_data, _In_ UINT64 length, _In_ BOOL prealloc, _In_opt_ void* data,
2823                          _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback, _In_ UINT8 compression, _In_ UINT64 decoded_size, _In_ BOOL file_write, _In_ UINT64 irp_offset) {
2824     UINT64 address;
2825     NTSTATUS Status;
2826     EXTENT_DATA* ed;
2827     EXTENT_DATA2* ed2;
2828     UINT16 edsize = (UINT16)(offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2));
2829     UINT32* csum = NULL;
2830 
2831     TRACE("(%p, (%llx, %llx), %llx, %llx, %llx, %u, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, c->offset, start_data, length, prealloc, data, rollback);
2832 
2833     if (!find_data_address_in_chunk(Vcb, c, length, &address))
2834         return FALSE;
2835 
2836     // add extent data to inode
2837     ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
2838     if (!ed) {
2839         ERR("out of memory\n");
2840         return FALSE;
2841     }
2842 
2843     ed->generation = Vcb->superblock.generation;
2844     ed->decoded_size = decoded_size;
2845     ed->compression = compression;
2846     ed->encryption = BTRFS_ENCRYPTION_NONE;
2847     ed->encoding = BTRFS_ENCODING_NONE;
2848     ed->type = prealloc ? EXTENT_TYPE_PREALLOC : EXTENT_TYPE_REGULAR;
2849 
2850     ed2 = (EXTENT_DATA2*)ed->data;
2851     ed2->address = address;
2852     ed2->size = length;
2853     ed2->offset = 0;
2854     ed2->num_bytes = decoded_size;
2855 
2856     if (!prealloc && data && !(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
2857         ULONG sl = (ULONG)(length / Vcb->superblock.sector_size);
2858 
2859         csum = ExAllocatePoolWithTag(PagedPool, sl * sizeof(UINT32), ALLOC_TAG);
2860         if (!csum) {
2861             ERR("out of memory\n");
2862             ExFreePool(ed);
2863             return FALSE;
2864         }
2865 
2866         Status = calc_csum(Vcb, data, sl, csum);
2867         if (!NT_SUCCESS(Status)) {
2868             ERR("calc_csum returned %08x\n", Status);
2869             ExFreePool(csum);
2870             ExFreePool(ed);
2871             return FALSE;
2872         }
2873     }
2874 
2875     Status = add_extent_to_fcb(fcb, start_data, ed, edsize, TRUE, csum, rollback);
2876     if (!NT_SUCCESS(Status)) {
2877         ERR("add_extent_to_fcb returned %08x\n", Status);
2878         if (csum) ExFreePool(csum);
2879         ExFreePool(ed);
2880         return FALSE;
2881     }
2882 
2883     ExFreePool(ed);
2884 
2885     c->used += length;
2886     space_list_subtract(c, FALSE, address, length, rollback);
2887 
2888     fcb->inode_item.st_blocks += decoded_size;
2889 
2890     fcb->extents_changed = TRUE;
2891     fcb->inode_item_changed = TRUE;
2892     mark_fcb_dirty(fcb);
2893 
2894     ExAcquireResourceExclusiveLite(&c->changed_extents_lock, TRUE);
2895 
2896     add_changed_extent_ref(c, address, length, fcb->subvol->id, fcb->inode, start_data, 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM);
2897 
2898     ExReleaseResourceLite(&c->changed_extents_lock);
2899 
2900     release_chunk_lock(c, Vcb);
2901 
2902     if (data) {
2903         Status = write_data_complete(Vcb, address, data, (UINT32)length, Irp, NULL, file_write, irp_offset,
2904                                      fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE ? HighPagePriority : NormalPagePriority);
2905         if (!NT_SUCCESS(Status))
2906             ERR("write_data_complete returned %08x\n", Status);
2907     }
2908 
2909     return TRUE;
2910 }
2911 
2912 static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data,
2913                             PIRP Irp, UINT64* written, BOOL file_write, UINT64 irp_offset, LIST_ENTRY* rollback) {
2914     BOOL success = FALSE;
2915     EXTENT_DATA* ed;
2916     EXTENT_DATA2* ed2;
2917     chunk* c;
2918     LIST_ENTRY* le;
2919     extent* ext = NULL;
2920 
2921     le = fcb->extents.Flink;
2922 
2923     while (le != &fcb->extents) {
2924         extent* nextext = CONTAINING_RECORD(le, extent, list_entry);
2925 
2926         if (!nextext->ignore) {
2927             if (nextext->offset == start_data) {
2928                 ext = nextext;
2929                 break;
2930             } else if (nextext->offset > start_data)
2931                 break;
2932 
2933             ext = nextext;
2934         }
2935 
2936         le = le->Flink;
2937     }
2938 
2939     if (!ext)
2940         return FALSE;
2941 
2942     ed = &ext->extent_data;
2943 
2944     if (ed->type != EXTENT_TYPE_REGULAR && ed->type != EXTENT_TYPE_PREALLOC) {
2945         TRACE("not extending extent which is not regular or prealloc\n");
2946         return FALSE;
2947     }
2948 
2949     ed2 = (EXTENT_DATA2*)ed->data;
2950 
2951     if (ext->offset + ed2->num_bytes != start_data) {
2952         TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", ext->offset, ed2->num_bytes, start_data);
2953         return FALSE;
2954     }
2955 
2956     c = get_chunk_from_address(Vcb, ed2->address);
2957 
2958     if (c->reloc || c->readonly || c->chunk_item->type != Vcb->data_flags)
2959         return FALSE;
2960 
2961     acquire_chunk_lock(c, Vcb);
2962 
2963     if (length > c->chunk_item->size - c->used) {
2964         release_chunk_lock(c, Vcb);
2965         return FALSE;
2966     }
2967 
2968     if (!c->cache_loaded) {
2969         NTSTATUS Status = load_cache_chunk(Vcb, c, NULL);
2970 
2971         if (!NT_SUCCESS(Status)) {
2972             ERR("load_cache_chunk returned %08x\n", Status);
2973             release_chunk_lock(c, Vcb);
2974             return FALSE;
2975         }
2976     }
2977 
2978     le = c->space.Flink;
2979     while (le != &c->space) {
2980         space* s = CONTAINING_RECORD(le, space, list_entry);
2981 
2982         if (s->address == ed2->address + ed2->size) {
2983             UINT64 newlen = min(min(s->size, length), MAX_EXTENT_SIZE);
2984 
2985             success = insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen, file_write, irp_offset);
2986 
2987             if (success)
2988                 *written += newlen;
2989             else
2990                 release_chunk_lock(c, Vcb);
2991 
2992             return success;
2993         } else if (s->address > ed2->address + ed2->size)
2994             break;
2995 
2996         le = le->Flink;
2997     }
2998 
2999     release_chunk_lock(c, Vcb);
3000 
3001     return FALSE;
3002 }
3003 
3004 static NTSTATUS insert_chunk_fragmented(fcb* fcb, UINT64 start, UINT64 length, UINT8* data, BOOL prealloc, LIST_ENTRY* rollback) {
3005     LIST_ENTRY* le;
3006     UINT64 flags = fcb->Vcb->data_flags;
3007     BOOL page_file = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE;
3008     NTSTATUS Status;
3009     chunk* c;
3010 
3011     ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
3012 
3013     // first create as many chunks as we can
3014     do {
3015         Status = alloc_chunk(fcb->Vcb, flags, &c, FALSE);
3016     } while (NT_SUCCESS(Status));
3017 
3018     if (Status != STATUS_DISK_FULL) {
3019         ERR("alloc_chunk returned %08x\n", Status);
3020         ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
3021         return Status;
3022     }
3023 
3024     le = fcb->Vcb->chunks.Flink;
3025     while (le != &fcb->Vcb->chunks) {
3026         c = CONTAINING_RECORD(le, chunk, list_entry);
3027 
3028         if (!c->readonly && !c->reloc) {
3029             acquire_chunk_lock(c, fcb->Vcb);
3030 
3031             if (c->chunk_item->type == flags) {
3032                 while (!IsListEmpty(&c->space_size) && length > 0) {
3033                     space* s = CONTAINING_RECORD(c->space_size.Flink, space, list_entry_size);
3034                     UINT64 extlen = min(length, s->size);
3035 
3036                     if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, prealloc && !page_file, data, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen, FALSE, 0)) {
3037                         start += extlen;
3038                         length -= extlen;
3039                         if (data) data += extlen;
3040 
3041                         acquire_chunk_lock(c, fcb->Vcb);
3042                     }
3043                 }
3044             }
3045 
3046             release_chunk_lock(c, fcb->Vcb);
3047 
3048             if (length == 0)
3049                 break;
3050         }
3051 
3052         le = le->Flink;
3053     }
3054 
3055     ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
3056 
3057     return length == 0 ? STATUS_SUCCESS : STATUS_DISK_FULL;
3058 }
3059 
3060 static NTSTATUS insert_prealloc_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback) {
3061     LIST_ENTRY* le;
3062     chunk* c;
3063     UINT64 flags;
3064     NTSTATUS Status;
3065     BOOL page_file = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE;
3066 
3067     flags = fcb->Vcb->data_flags;
3068 
3069     do {
3070         UINT64 extlen = min(MAX_EXTENT_SIZE, length);
3071 
3072         ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
3073 
3074         le = fcb->Vcb->chunks.Flink;
3075         while (le != &fcb->Vcb->chunks) {
3076             c = CONTAINING_RECORD(le, chunk, list_entry);
3077 
3078             if (!c->readonly && !c->reloc) {
3079                 acquire_chunk_lock(c, fcb->Vcb);
3080 
3081                 if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= extlen) {
3082                     if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen, FALSE, 0)) {
3083                         ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
3084                         goto cont;
3085                     }
3086                 }
3087 
3088                 release_chunk_lock(c, fcb->Vcb);
3089             }
3090 
3091             le = le->Flink;
3092         }
3093 
3094         ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
3095 
3096         ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
3097 
3098         Status = alloc_chunk(fcb->Vcb, flags, &c, FALSE);
3099 
3100         ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
3101 
3102         if (!NT_SUCCESS(Status)) {
3103             ERR("alloc_chunk returned %08x\n", Status);
3104             goto end;
3105         }
3106 
3107         acquire_chunk_lock(c, fcb->Vcb);
3108 
3109         if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= extlen) {
3110             if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen, FALSE, 0))
3111                 goto cont;
3112         }
3113 
3114         release_chunk_lock(c, fcb->Vcb);
3115 
3116         Status = insert_chunk_fragmented(fcb, start, length, NULL, TRUE, rollback);
3117         if (!NT_SUCCESS(Status))
3118             ERR("insert_chunk_fragmented returned %08x\n", Status);
3119 
3120         goto end;
3121 
3122 cont:
3123         length -= extlen;
3124         start += extlen;
3125     } while (length > 0);
3126 
3127     Status = STATUS_SUCCESS;
3128 
3129 end:
3130     return Status;
3131 }
3132 
3133 static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data,
3134                               PIRP Irp, BOOL file_write, UINT64 irp_offset, LIST_ENTRY* rollback) {
3135     NTSTATUS Status;
3136     LIST_ENTRY* le;
3137     chunk* c;
3138     UINT64 flags, orig_length = length, written = 0;
3139 
3140     TRACE("(%p, (%llx, %llx), %llx, %llx, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, length, data);
3141 
3142     if (start_data > 0) {
3143         try_extend_data(Vcb, fcb, start_data, length, data, Irp, &written, file_write, irp_offset, rollback);
3144 
3145         if (written == length)
3146             return STATUS_SUCCESS;
3147         else if (written > 0) {
3148             start_data += written;
3149             irp_offset += written;
3150             length -= written;
3151             data = &((UINT8*)data)[written];
3152         }
3153     }
3154 
3155     flags = Vcb->data_flags;
3156 
3157     while (written < orig_length) {
3158         UINT64 newlen = min(length, MAX_EXTENT_SIZE);
3159         BOOL done = FALSE;
3160 
3161         // Rather than necessarily writing the whole extent at once, we deal with it in blocks of 128 MB.
3162         // First, see if we can write the extent part to an existing chunk.
3163 
3164         ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE);
3165 
3166         le = Vcb->chunks.Flink;
3167         while (le != &Vcb->chunks) {
3168             c = CONTAINING_RECORD(le, chunk, list_entry);
3169 
3170             if (!c->readonly && !c->reloc) {
3171                 acquire_chunk_lock(c, Vcb);
3172 
3173                 if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen &&
3174                     insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen, file_write, irp_offset)) {
3175                     written += newlen;
3176 
3177                     if (written == orig_length) {
3178                         ExReleaseResourceLite(&Vcb->chunk_lock);
3179                         return STATUS_SUCCESS;
3180                     } else {
3181                         done = TRUE;
3182                         start_data += newlen;
3183                         irp_offset += newlen;
3184                         length -= newlen;
3185                         data = &((UINT8*)data)[newlen];
3186                         break;
3187                     }
3188                 } else
3189                     release_chunk_lock(c, Vcb);
3190             }
3191 
3192             le = le->Flink;
3193         }
3194 
3195         ExReleaseResourceLite(&Vcb->chunk_lock);
3196 
3197         if (done) continue;
3198 
3199         // Otherwise, see if we can put it in a new chunk.
3200 
3201         ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, TRUE);
3202 
3203         Status = alloc_chunk(Vcb, flags, &c, FALSE);
3204 
3205         ExReleaseResourceLite(&Vcb->chunk_lock);
3206 
3207         if (!NT_SUCCESS(Status)) {
3208             ERR("alloc_chunk returned %08x\n", Status);
3209             return Status;
3210         }
3211 
3212         if (c) {
3213             acquire_chunk_lock(c, Vcb);
3214 
3215             if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen &&
3216                 insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen, file_write, irp_offset)) {
3217                 written += newlen;
3218 
3219                 if (written == orig_length)
3220                     return STATUS_SUCCESS;
3221                 else {
3222                     done = TRUE;
3223                     start_data += newlen;
3224                     irp_offset += newlen;
3225                     length -= newlen;
3226                     data = &((UINT8*)data)[newlen];
3227                 }
3228             } else
3229                 release_chunk_lock(c, Vcb);
3230         }
3231 
3232         if (!done) {
3233             Status = insert_chunk_fragmented(fcb, start_data, length, data, FALSE, rollback);
3234             if (!NT_SUCCESS(Status))
3235                 ERR("insert_chunk_fragmented returned %08x\n", Status);
3236 
3237             return Status;
3238         }
3239     }
3240 
3241     return STATUS_DISK_FULL;
3242 }
3243 
3244 NTSTATUS truncate_file(fcb* fcb, UINT64 end, PIRP Irp, LIST_ENTRY* rollback) {
3245     NTSTATUS Status;
3246 
3247     // FIXME - convert into inline extent if short enough
3248 
3249     if (end > 0 && fcb_is_inline(fcb)) {
3250         UINT8* buf;
3251         BOOL make_inline = end <= fcb->Vcb->options.max_inline;
3252 
3253         buf = ExAllocatePoolWithTag(PagedPool, (ULONG)(make_inline ? (offsetof(EXTENT_DATA, data[0]) + end) : sector_align(end, fcb->Vcb->superblock.sector_size)), ALLOC_TAG);
3254         if (!buf) {
3255             ERR("out of memory\n");
3256             return STATUS_INSUFFICIENT_RESOURCES;
3257         }
3258 
3259         Status = read_file(fcb, make_inline ? (buf + offsetof(EXTENT_DATA, data[0])) : buf, 0, end, NULL, Irp);
3260         if (!NT_SUCCESS(Status)) {
3261             ERR("read_file returned %08x\n", Status);
3262             ExFreePool(buf);
3263             return Status;
3264         }
3265 
3266         Status = excise_extents(fcb->Vcb, fcb, 0, fcb->inode_item.st_size, Irp, rollback);
3267         if (!NT_SUCCESS(Status)) {
3268             ERR("excise_extents returned %08x\n", Status);
3269             ExFreePool(buf);
3270             return Status;
3271         }
3272 
3273         if (!make_inline) {
3274             RtlZeroMemory(buf + end, (ULONG)(sector_align(end, fcb->Vcb->superblock.sector_size) - end));
3275 
3276             Status = do_write_file(fcb, 0, sector_align(end, fcb->Vcb->superblock.sector_size), buf, Irp, FALSE, 0, rollback);
3277             if (!NT_SUCCESS(Status)) {
3278                 ERR("do_write_file returned %08x\n", Status);
3279                 ExFreePool(buf);
3280                 return Status;
3281             }
3282         } else {
3283             EXTENT_DATA* ed = (EXTENT_DATA*)buf;
3284 
3285             ed->generation = fcb->Vcb->superblock.generation;
3286             ed->decoded_size = end;
3287             ed->compression = BTRFS_COMPRESSION_NONE;
3288             ed->encryption = BTRFS_ENCRYPTION_NONE;
3289             ed->encoding = BTRFS_ENCODING_NONE;
3290             ed->type = EXTENT_TYPE_INLINE;
3291 
3292             Status = add_extent_to_fcb(fcb, 0, ed, (UINT16)(offsetof(EXTENT_DATA, data[0]) + end), FALSE, NULL, rollback);
3293             if (!NT_SUCCESS(Status)) {
3294                 ERR("add_extent_to_fcb returned %08x\n", Status);
3295                 ExFreePool(buf);
3296                 return Status;
3297             }
3298 
3299             fcb->inode_item.st_blocks += end;
3300         }
3301 
3302         ExFreePool(buf);
3303         return STATUS_SUCCESS;
3304     }
3305 
3306     Status = excise_extents(fcb->Vcb, fcb, sector_align(end, fcb->Vcb->superblock.sector_size),
3307                             sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), Irp, rollback);
3308     if (!NT_SUCCESS(Status)) {
3309         ERR("excise_extents returned %08x\n", Status);
3310         return Status;
3311     }
3312 
3313     fcb->inode_item.st_size = end;
3314     fcb->inode_item_changed = TRUE;
3315     TRACE("setting st_size to %llx\n", end);
3316 
3317     fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
3318     fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
3319     fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
3320     // FIXME - inform cache manager of this
3321 
3322     TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart);
3323 
3324     return STATUS_SUCCESS;
3325 }
3326 
3327 NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIRP Irp, LIST_ENTRY* rollback) {
3328     UINT64 oldalloc, newalloc;
3329     BOOL cur_inline;
3330     NTSTATUS Status;
3331 
3332     TRACE("(%p, %p, %x, %u)\n", fcb, fileref, end, prealloc);
3333 
3334     if (fcb->ads) {
3335         if (end > 0xffff)
3336             return STATUS_DISK_FULL;
3337 
3338         return stream_set_end_of_file_information(fcb->Vcb, (UINT16)end, fcb, fileref, FALSE);
3339     } else {
3340         extent* ext = NULL;
3341         LIST_ENTRY* le;
3342 
3343         le = fcb->extents.Blink;
3344         while (le != &fcb->extents) {
3345             extent* ext2 = CONTAINING_RECORD(le, extent, list_entry);
3346 
3347             if (!ext2->ignore) {
3348                 ext = ext2;
3349                 break;
3350             }
3351 
3352             le = le->Blink;
3353         }
3354 
3355         oldalloc = 0;
3356         if (ext) {
3357             EXTENT_DATA* ed = &ext->extent_data;
3358             EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
3359 
3360             oldalloc = ext->offset + (ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes);
3361             cur_inline = ed->type == EXTENT_TYPE_INLINE;
3362 
3363             if (cur_inline && end > fcb->Vcb->options.max_inline) {
3364                 UINT64 origlength, length;
3365                 UINT8* data;
3366 
3367                 TRACE("giving inline file proper extents\n");
3368 
3369                 origlength = ed->decoded_size;
3370 
3371                 cur_inline = FALSE;
3372 
3373                 length = sector_align(origlength, fcb->Vcb->superblock.sector_size);
3374 
3375                 data = ExAllocatePoolWithTag(PagedPool, (ULONG)length, ALLOC_TAG);
3376                 if (!data) {
3377                     ERR("could not allocate %llx bytes for data\n", length);
3378                     return STATUS_INSUFFICIENT_RESOURCES;
3379                 }
3380 
3381                 Status = read_file(fcb, data, 0, origlength, NULL, Irp);
3382                 if (!NT_SUCCESS(Status)) {
3383                     ERR("read_file returned %08x\n", Status);
3384                     ExFreePool(data);
3385                     return Status;
3386                 }
3387 
3388                 RtlZeroMemory(data + origlength, (ULONG)(length - origlength));
3389 
3390                 Status = excise_extents(fcb->Vcb, fcb, 0, fcb->inode_item.st_size, Irp, rollback);
3391                 if (!NT_SUCCESS(Status)) {
3392                     ERR("excise_extents returned %08x\n", Status);
3393                     ExFreePool(data);
3394                     return Status;
3395                 }
3396 
3397                 Status = do_write_file(fcb, 0, length, data, Irp, FALSE, 0, rollback);
3398                 if (!NT_SUCCESS(Status)) {
3399                     ERR("do_write_file returned %08x\n", Status);
3400                     ExFreePool(data);
3401                     return Status;
3402                 }
3403 
3404                 oldalloc = ext->offset + length;
3405 
3406                 ExFreePool(data);
3407             }
3408 
3409             if (cur_inline) {
3410                 UINT16 edsize;
3411 
3412                 if (end > oldalloc) {
3413                     edsize = (UINT16)(offsetof(EXTENT_DATA, data[0]) + end - ext->offset);
3414                     ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
3415 
3416                     if (!ed) {
3417                         ERR("out of memory\n");
3418                         return STATUS_INSUFFICIENT_RESOURCES;
3419                     }
3420 
3421                     ed->generation = fcb->Vcb->superblock.generation;
3422                     ed->decoded_size = end - ext->offset;
3423                     ed->compression = BTRFS_COMPRESSION_NONE;
3424                     ed->encryption = BTRFS_ENCRYPTION_NONE;
3425                     ed->encoding = BTRFS_ENCODING_NONE;
3426                     ed->type = EXTENT_TYPE_INLINE;
3427 
3428                     Status = read_file(fcb, ed->data, ext->offset, oldalloc, NULL, Irp);
3429                     if (!NT_SUCCESS(Status)) {
3430                         ERR("read_file returned %08x\n", Status);
3431                         ExFreePool(ed);
3432                         return Status;
3433                     }
3434 
3435                     RtlZeroMemory(ed->data + oldalloc - ext->offset, (ULONG)(end - oldalloc));
3436 
3437                     remove_fcb_extent(fcb, ext, rollback);
3438 
3439                     Status = add_extent_to_fcb(fcb, ext->offset, ed, edsize, ext->unique, NULL, rollback);
3440                     if (!NT_SUCCESS(Status)) {
3441                         ERR("add_extent_to_fcb returned %08x\n", Status);
3442                         ExFreePool(ed);
3443                         return Status;
3444                     }
3445 
3446                     ExFreePool(ed);
3447 
3448                     fcb->extents_changed = TRUE;
3449                     mark_fcb_dirty(fcb);
3450                 }
3451 
3452                 TRACE("extending inline file (oldalloc = %llx, end = %llx)\n", oldalloc, end);
3453 
3454                 fcb->inode_item.st_size = end;
3455                 TRACE("setting st_size to %llx\n", end);
3456 
3457                 fcb->inode_item.st_blocks = end;
3458 
3459                 fcb->Header.AllocationSize.QuadPart = fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
3460             } else {
3461                 newalloc = sector_align(end, fcb->Vcb->superblock.sector_size);
3462 
3463                 if (newalloc > oldalloc) {
3464                     if (prealloc) {
3465                         // FIXME - try and extend previous extent first
3466 
3467                         Status = insert_prealloc_extent(fcb, oldalloc, newalloc - oldalloc, rollback);
3468 
3469                         if (!NT_SUCCESS(Status)) {
3470                             ERR("insert_prealloc_extent returned %08x\n", Status);
3471                             return Status;
3472                         }
3473                     }
3474 
3475                     fcb->extents_changed = TRUE;
3476                 }
3477 
3478                 fcb->inode_item.st_size = end;
3479                 fcb->inode_item_changed = TRUE;
3480                 mark_fcb_dirty(fcb);
3481 
3482                 TRACE("setting st_size to %llx\n", end);
3483 
3484                 TRACE("newalloc = %llx\n", newalloc);
3485 
3486                 fcb->Header.AllocationSize.QuadPart = newalloc;
3487                 fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
3488             }
3489         } else {
3490             if (end > fcb->Vcb->options.max_inline) {
3491                 newalloc = sector_align(end, fcb->Vcb->superblock.sector_size);
3492 
3493                 if (prealloc) {
3494                     Status = insert_prealloc_extent(fcb, 0, newalloc, rollback);
3495 
3496                     if (!NT_SUCCESS(Status)) {
3497                         ERR("insert_prealloc_extent returned %08x\n", Status);
3498                         return Status;
3499                     }
3500                 }
3501 
3502                 fcb->extents_changed = TRUE;
3503                 fcb->inode_item_changed = TRUE;
3504                 mark_fcb_dirty(fcb);
3505 
3506                 fcb->inode_item.st_size = end;
3507                 TRACE("setting st_size to %llx\n", end);
3508 
3509                 TRACE("newalloc = %llx\n", newalloc);
3510 
3511                 fcb->Header.AllocationSize.QuadPart = newalloc;
3512                 fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
3513             } else {
3514                 EXTENT_DATA* ed;
3515                 UINT16 edsize;
3516 
3517                 edsize = (UINT16)(offsetof(EXTENT_DATA, data[0]) + end);
3518                 ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
3519 
3520                 if (!ed) {
3521                     ERR("out of memory\n");
3522                     return STATUS_INSUFFICIENT_RESOURCES;
3523                 }
3524 
3525                 ed->generation = fcb->Vcb->superblock.generation;
3526                 ed->decoded_size = end;
3527                 ed->compression = BTRFS_COMPRESSION_NONE;
3528                 ed->encryption = BTRFS_ENCRYPTION_NONE;
3529                 ed->encoding = BTRFS_ENCODING_NONE;
3530                 ed->type = EXTENT_TYPE_INLINE;
3531 
3532                 RtlZeroMemory(ed->data, (ULONG)end);
3533 
3534                 Status = add_extent_to_fcb(fcb, 0, ed, edsize, FALSE, NULL, rollback);
3535                 if (!NT_SUCCESS(Status)) {
3536                     ERR("add_extent_to_fcb returned %08x\n", Status);
3537                     ExFreePool(ed);
3538                     return Status;
3539                 }
3540 
3541                 ExFreePool(ed);
3542 
3543                 fcb->extents_changed = TRUE;
3544                 fcb->inode_item_changed = TRUE;
3545                 mark_fcb_dirty(fcb);
3546 
3547                 fcb->inode_item.st_size = end;
3548                 TRACE("setting st_size to %llx\n", end);
3549 
3550                 fcb->inode_item.st_blocks = end;
3551 
3552                 fcb->Header.AllocationSize.QuadPart = fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
3553             }
3554         }
3555     }
3556 
3557     return STATUS_SUCCESS;
3558 }
3559 
3560 static NTSTATUS do_write_file_prealloc(fcb* fcb, extent* ext, UINT64 start_data, UINT64 end_data, void* data, UINT64* written,
3561                                        PIRP Irp, BOOL file_write, UINT64 irp_offset, ULONG priority, LIST_ENTRY* rollback) {
3562     EXTENT_DATA* ed = &ext->extent_data;
3563     EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
3564     NTSTATUS Status;
3565     chunk* c = NULL;
3566 
3567     if (start_data <= ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace all
3568         extent* newext;
3569 
3570         newext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3571         if (!newext) {
3572             ERR("out of memory\n");
3573             return STATUS_INSUFFICIENT_RESOURCES;
3574         }
3575 
3576         RtlCopyMemory(&newext->extent_data, &ext->extent_data, ext->datalen);
3577 
3578         newext->extent_data.type = EXTENT_TYPE_REGULAR;
3579 
3580         Status = write_data_complete(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, (UINT32)ed2->num_bytes, Irp,
3581                                      NULL, file_write, irp_offset + ext->offset - start_data, priority);
3582         if (!NT_SUCCESS(Status)) {
3583             ERR("write_data_complete returned %08x\n", Status);
3584             return Status;
3585         }
3586 
3587         if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3588             ULONG sl = (ULONG)(ed2->num_bytes / fcb->Vcb->superblock.sector_size);
3589             UINT32* csum = ExAllocatePoolWithTag(PagedPool, sl * sizeof(UINT32), ALLOC_TAG);
3590 
3591             if (!csum) {
3592                 ERR("out of memory\n");
3593                 ExFreePool(newext);
3594                 return STATUS_INSUFFICIENT_RESOURCES;
3595             }
3596 
3597             Status = calc_csum(fcb->Vcb, (UINT8*)data + ext->offset - start_data, sl, csum);
3598             if (!NT_SUCCESS(Status)) {
3599                 ERR("calc_csum returned %08x\n", Status);
3600                 ExFreePool(csum);
3601                 ExFreePool(newext);
3602                 return Status;
3603             }
3604 
3605             newext->csum = csum;
3606         } else
3607             newext->csum = NULL;
3608 
3609         *written = ed2->num_bytes;
3610 
3611         newext->offset = ext->offset;
3612         newext->datalen = ext->datalen;
3613         newext->unique = ext->unique;
3614         newext->ignore = FALSE;
3615         newext->inserted = TRUE;
3616         InsertHeadList(&ext->list_entry, &newext->list_entry);
3617 
3618         add_insert_extent_rollback(rollback, fcb, newext);
3619 
3620         remove_fcb_extent(fcb, ext, rollback);
3621 
3622         c = get_chunk_from_address(fcb->Vcb, ed2->address);
3623     } else if (start_data <= ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace beginning
3624         EXTENT_DATA2* ned2;
3625         extent *newext1, *newext2;
3626 
3627         newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3628         if (!newext1) {
3629             ERR("out of memory\n");
3630             return STATUS_INSUFFICIENT_RESOURCES;
3631         }
3632 
3633         newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3634         if (!newext2) {
3635             ERR("out of memory\n");
3636             ExFreePool(newext1);
3637             return STATUS_INSUFFICIENT_RESOURCES;
3638         }
3639 
3640         RtlCopyMemory(&newext1->extent_data, &ext->extent_data, ext->datalen);
3641         newext1->extent_data.type = EXTENT_TYPE_REGULAR;
3642         ned2 = (EXTENT_DATA2*)newext1->extent_data.data;
3643         ned2->num_bytes = end_data - ext->offset;
3644 
3645         RtlCopyMemory(&newext2->extent_data, &ext->extent_data, ext->datalen);
3646         ned2 = (EXTENT_DATA2*)newext2->extent_data.data;
3647         ned2->offset += end_data - ext->offset;
3648         ned2->num_bytes -= end_data - ext->offset;
3649 
3650         Status = write_data_complete(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, (UINT32)(end_data - ext->offset),
3651                                      Irp, NULL, file_write, irp_offset + ext->offset - start_data, priority);
3652         if (!NT_SUCCESS(Status)) {
3653             ERR("write_data_complete returned %08x\n", Status);
3654             ExFreePool(newext1);
3655             ExFreePool(newext2);
3656             return Status;
3657         }
3658 
3659         if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3660             ULONG sl = (ULONG)((end_data - ext->offset) / fcb->Vcb->superblock.sector_size);
3661             UINT32* csum = ExAllocatePoolWithTag(PagedPool, sl * sizeof(UINT32), ALLOC_TAG);
3662 
3663             if (!csum) {
3664                 ERR("out of memory\n");
3665                 ExFreePool(newext1);
3666                 ExFreePool(newext2);
3667                 return STATUS_INSUFFICIENT_RESOURCES;
3668             }
3669 
3670             Status = calc_csum(fcb->Vcb, (UINT8*)data + ext->offset - start_data, sl, csum);
3671             if (!NT_SUCCESS(Status)) {
3672                 ERR("calc_csum returned %08x\n", Status);
3673                 ExFreePool(newext1);
3674                 ExFreePool(newext2);
3675                 ExFreePool(csum);
3676                 return Status;
3677             }
3678 
3679             newext1->csum = csum;
3680         } else
3681             newext1->csum = NULL;
3682 
3683         *written = end_data - ext->offset;
3684 
3685         newext1->offset = ext->offset;
3686         newext1->datalen = ext->datalen;
3687         newext1->unique = ext->unique;
3688         newext1->ignore = FALSE;
3689         newext1->inserted = TRUE;
3690         InsertHeadList(&ext->list_entry, &newext1->list_entry);
3691 
3692         add_insert_extent_rollback(rollback, fcb, newext1);
3693 
3694         newext2->offset = end_data;
3695         newext2->datalen = ext->datalen;
3696         newext2->unique = ext->unique;
3697         newext2->ignore = FALSE;
3698         newext2->inserted = TRUE;
3699         newext2->csum = NULL;
3700         add_extent(fcb, &newext1->list_entry, newext2);
3701 
3702         add_insert_extent_rollback(rollback, fcb, newext2);
3703 
3704         c = get_chunk_from_address(fcb->Vcb, ed2->address);
3705 
3706         if (!c)
3707             ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
3708         else {
3709             Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
3710                                                 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp);
3711 
3712             if (!NT_SUCCESS(Status)) {
3713                 ERR("update_changed_extent_ref returned %08x\n", Status);
3714                 return Status;
3715             }
3716         }
3717 
3718         remove_fcb_extent(fcb, ext, rollback);
3719     } else if (start_data > ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace end
3720         EXTENT_DATA2* ned2;
3721         extent *newext1, *newext2;
3722 
3723         newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3724         if (!newext1) {
3725             ERR("out of memory\n");
3726             return STATUS_INSUFFICIENT_RESOURCES;
3727         }
3728 
3729         newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3730         if (!newext2) {
3731             ERR("out of memory\n");
3732             ExFreePool(newext1);
3733             return STATUS_INSUFFICIENT_RESOURCES;
3734         }
3735 
3736         RtlCopyMemory(&newext1->extent_data, &ext->extent_data, ext->datalen);
3737 
3738         ned2 = (EXTENT_DATA2*)newext1->extent_data.data;
3739         ned2->num_bytes = start_data - ext->offset;
3740 
3741         RtlCopyMemory(&newext2->extent_data, &ext->extent_data, ext->datalen);
3742 
3743         newext2->extent_data.type = EXTENT_TYPE_REGULAR;
3744         ned2 = (EXTENT_DATA2*)newext2->extent_data.data;
3745         ned2->offset += start_data - ext->offset;
3746         ned2->num_bytes = ext->offset + ed2->num_bytes - start_data;
3747 
3748         Status = write_data_complete(fcb->Vcb, ed2->address + ned2->offset, data, (UINT32)ned2->num_bytes, Irp, NULL, file_write, irp_offset, priority);
3749         if (!NT_SUCCESS(Status)) {
3750             ERR("write_data_complete returned %08x\n", Status);
3751             ExFreePool(newext1);
3752             ExFreePool(newext2);
3753             return Status;
3754         }
3755 
3756         if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3757             ULONG sl = (ULONG)(ned2->num_bytes / fcb->Vcb->superblock.sector_size);
3758             UINT32* csum = ExAllocatePoolWithTag(PagedPool, sl * sizeof(UINT32), ALLOC_TAG);
3759 
3760             if (!csum) {
3761                 ERR("out of memory\n");
3762                 ExFreePool(newext1);
3763                 ExFreePool(newext2);
3764                 return STATUS_INSUFFICIENT_RESOURCES;
3765             }
3766 
3767             Status = calc_csum(fcb->Vcb, data, sl, csum);
3768             if (!NT_SUCCESS(Status)) {
3769                 ERR("calc_csum returned %08x\n", Status);
3770                 ExFreePool(newext1);
3771                 ExFreePool(newext2);
3772                 ExFreePool(csum);
3773                 return Status;
3774             }
3775 
3776             newext2->csum = csum;
3777         } else
3778             newext2->csum = NULL;
3779 
3780         *written = ned2->num_bytes;
3781 
3782         newext1->offset = ext->offset;
3783         newext1->datalen = ext->datalen;
3784         newext1->unique = ext->unique;
3785         newext1->ignore = FALSE;
3786         newext1->inserted = TRUE;
3787         newext1->csum = NULL;
3788         InsertHeadList(&ext->list_entry, &newext1->list_entry);
3789 
3790         add_insert_extent_rollback(rollback, fcb, newext1);
3791 
3792         newext2->offset = start_data;
3793         newext2->datalen = ext->datalen;
3794         newext2->unique = ext->unique;
3795         newext2->ignore = FALSE;
3796         newext2->inserted = TRUE;
3797         add_extent(fcb, &newext1->list_entry, newext2);
3798 
3799         add_insert_extent_rollback(rollback, fcb, newext2);
3800 
3801         c = get_chunk_from_address(fcb->Vcb, ed2->address);
3802 
3803         if (!c)
3804             ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
3805         else {
3806             Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
3807                                                fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp);
3808 
3809             if (!NT_SUCCESS(Status)) {
3810                 ERR("update_changed_extent_ref returned %08x\n", Status);
3811                 return Status;
3812             }
3813         }
3814 
3815         remove_fcb_extent(fcb, ext, rollback);
3816     } else if (start_data > ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace middle
3817         EXTENT_DATA2* ned2;
3818         extent *newext1, *newext2, *newext3;
3819 
3820         newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3821         if (!newext1) {
3822             ERR("out of memory\n");
3823             return STATUS_INSUFFICIENT_RESOURCES;
3824         }
3825 
3826         newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3827         if (!newext2) {
3828             ERR("out of memory\n");
3829             ExFreePool(newext1);
3830             return STATUS_INSUFFICIENT_RESOURCES;
3831         }
3832 
3833         newext3 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3834         if (!newext3) {
3835             ERR("out of memory\n");
3836             ExFreePool(newext1);
3837             ExFreePool(newext2);
3838             return STATUS_INSUFFICIENT_RESOURCES;
3839         }
3840 
3841         RtlCopyMemory(&newext1->extent_data, &ext->extent_data, ext->datalen);
3842         RtlCopyMemory(&newext2->extent_data, &ext->extent_data, ext->datalen);
3843         RtlCopyMemory(&newext3->extent_data, &ext->extent_data, ext->datalen);
3844 
3845         ned2 = (EXTENT_DATA2*)newext1->extent_data.data;
3846         ned2->num_bytes = start_data - ext->offset;
3847 
3848         newext2->extent_data.type = EXTENT_TYPE_REGULAR;
3849         ned2 = (EXTENT_DATA2*)newext2->extent_data.data;
3850         ned2->offset += start_data - ext->offset;
3851         ned2->num_bytes = end_data - start_data;
3852 
3853         ned2 = (EXTENT_DATA2*)newext3->extent_data.data;
3854         ned2->offset += end_data - ext->offset;
3855         ned2->num_bytes -= end_data - ext->offset;
3856 
3857         ned2 = (EXTENT_DATA2*)newext2->extent_data.data;
3858         Status = write_data_complete(fcb->Vcb, ed2->address + ned2->offset, data, (UINT32)(end_data - start_data), Irp, NULL, file_write, irp_offset, priority);
3859         if (!NT_SUCCESS(Status)) {
3860             ERR("write_data_complete returned %08x\n", Status);
3861             ExFreePool(newext1);
3862             ExFreePool(newext2);
3863             ExFreePool(newext3);
3864             return Status;
3865         }
3866 
3867         if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3868             ULONG sl = (ULONG)((end_data - start_data) / fcb->Vcb->superblock.sector_size);
3869             UINT32* csum = ExAllocatePoolWithTag(PagedPool, sl * sizeof(UINT32), ALLOC_TAG);
3870 
3871             if (!csum) {
3872                 ERR("out of memory\n");
3873                 ExFreePool(newext1);
3874                 ExFreePool(newext2);
3875                 ExFreePool(newext3);
3876                 return STATUS_INSUFFICIENT_RESOURCES;
3877             }
3878 
3879             Status = calc_csum(fcb->Vcb, data, sl, csum);
3880             if (!NT_SUCCESS(Status)) {
3881                 ERR("calc_csum returned %08x\n", Status);
3882                 ExFreePool(newext1);
3883                 ExFreePool(newext2);
3884                 ExFreePool(newext3);
3885                 ExFreePool(csum);
3886                 return Status;
3887             }
3888 
3889             newext2->csum = csum;
3890         } else
3891             newext2->csum = NULL;
3892 
3893         *written = end_data - start_data;
3894 
3895         newext1->offset = ext->offset;
3896         newext1->datalen = ext->datalen;
3897         newext1->unique = ext->unique;
3898         newext1->ignore = FALSE;
3899         newext1->inserted = TRUE;
3900         newext1->csum = NULL;
3901         InsertHeadList(&ext->list_entry, &newext1->list_entry);
3902 
3903         add_insert_extent_rollback(rollback, fcb, newext1);
3904 
3905         newext2->offset = start_data;
3906         newext2->datalen = ext->datalen;
3907         newext2->unique = ext->unique;
3908         newext2->ignore = FALSE;
3909         newext2->inserted = TRUE;
3910         add_extent(fcb, &newext1->list_entry, newext2);
3911 
3912         add_insert_extent_rollback(rollback, fcb, newext2);
3913 
3914         newext3->offset = end_data;
3915         newext3->datalen = ext->datalen;
3916         newext3->unique = ext->unique;
3917         newext3->ignore = FALSE;
3918         newext3->inserted = TRUE;
3919         newext3->csum = NULL;
3920         add_extent(fcb, &newext2->list_entry, newext3);
3921 
3922         add_insert_extent_rollback(rollback, fcb, newext3);
3923 
3924         c = get_chunk_from_address(fcb->Vcb, ed2->address);
3925 
3926         if (!c)
3927             ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
3928         else {
3929             Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 2,
3930                                                fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp);
3931 
3932             if (!NT_SUCCESS(Status)) {
3933                 ERR("update_changed_extent_ref returned %08x\n", Status);
3934                 return Status;
3935             }
3936         }
3937 
3938         remove_fcb_extent(fcb, ext, rollback);
3939     }
3940 
3941     if (c)
3942         c->changed = TRUE;
3943 
3944     return STATUS_SUCCESS;
3945 }
3946 
3947 NTSTATUS do_write_file(fcb* fcb, UINT64 start, UINT64 end_data, void* data, PIRP Irp, BOOL file_write, UINT32 irp_offset, LIST_ENTRY* rollback) {
3948     NTSTATUS Status;
3949     LIST_ENTRY *le, *le2;
3950     UINT64 written = 0, length = end_data - start;
3951     UINT64 last_cow_start;
3952     ULONG priority = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE ? HighPagePriority : NormalPagePriority;
3953 #ifdef DEBUG_PARANOID
3954     UINT64 last_off;
3955 #endif
3956 
3957     last_cow_start = 0;
3958 
3959     le = fcb->extents.Flink;
3960     while (le != &fcb->extents) {
3961         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
3962 
3963         le2 = le->Flink;
3964 
3965         if (!ext->ignore) {
3966             EXTENT_DATA* ed = &ext->extent_data;
3967             EXTENT_DATA2* ed2 = ed->type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ed->data;
3968             UINT64 len;
3969 
3970             len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
3971 
3972             if (ext->offset + len <= start)
3973                 goto nextitem;
3974 
3975             if (ext->offset > start + written + length)
3976                 break;
3977 
3978             if ((fcb->inode_item.flags & BTRFS_INODE_NODATACOW || ed->type == EXTENT_TYPE_PREALLOC) && ext->unique && ed->compression == BTRFS_COMPRESSION_NONE) {
3979                 if (max(last_cow_start, start + written) < ext->offset) {
3980                     UINT64 start_write = max(last_cow_start, start + written);
3981 
3982                     Status = excise_extents(fcb->Vcb, fcb, start_write, ext->offset, Irp, rollback);
3983                     if (!NT_SUCCESS(Status)) {
3984                         ERR("excise_extents returned %08x\n", Status);
3985                         return Status;
3986                     }
3987 
3988                     Status = insert_extent(fcb->Vcb, fcb, start_write, ext->offset - start_write, (UINT8*)data + written, Irp, file_write, irp_offset + written, rollback);
3989                     if (!NT_SUCCESS(Status)) {
3990                         ERR("insert_extent returned %08x\n", Status);
3991                         return Status;
3992                     }
3993 
3994                     written += ext->offset - start_write;
3995                     length -= ext->offset - start_write;
3996 
3997                     if (length == 0)
3998                         break;
3999                 }
4000 
4001                 if (ed->type == EXTENT_TYPE_REGULAR) {
4002                     UINT64 writeaddr = ed2->address + ed2->offset + start + written - ext->offset;
4003                     UINT64 write_len = min(len, length);
4004                     chunk* c;
4005 
4006                     TRACE("doing non-COW write to %llx\n", writeaddr);
4007 
4008                     Status = write_data_complete(fcb->Vcb, writeaddr, (UINT8*)data + written, (UINT32)write_len, Irp, NULL, file_write, irp_offset + written, priority);
4009                     if (!NT_SUCCESS(Status)) {
4010                         ERR("write_data_complete returned %08x\n", Status);
4011                         return Status;
4012                     }
4013 
4014                     c = get_chunk_from_address(fcb->Vcb, writeaddr);
4015                     if (c)
4016                         c->changed = TRUE;
4017 
4018                     // This shouldn't ever get called - nocow files should always also be nosum.
4019                     if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
4020                         calc_csum(fcb->Vcb, (UINT8*)data + written, (UINT32)(write_len / fcb->Vcb->superblock.sector_size),
4021                                   &ext->csum[(start + written - ext->offset) / fcb->Vcb->superblock.sector_size]);
4022 
4023                         ext->inserted = TRUE;
4024                     }
4025 
4026                     written += write_len;
4027                     length -= write_len;
4028 
4029                     if (length == 0)
4030                         break;
4031                 } else if (ed->type == EXTENT_TYPE_PREALLOC) {
4032                     UINT64 write_len;
4033 
4034                     Status = do_write_file_prealloc(fcb, ext, start + written, end_data, (UINT8*)data + written, &write_len,
4035                                                     Irp, file_write, irp_offset + written, priority, rollback);
4036                     if (!NT_SUCCESS(Status)) {
4037                         ERR("do_write_file_prealloc returned %08x\n", Status);
4038                         return Status;
4039                     }
4040 
4041                     written += write_len;
4042                     length -= write_len;
4043 
4044                     if (length == 0)
4045                         break;
4046                 }
4047 
4048                 last_cow_start = ext->offset + len;
4049             }
4050         }
4051 
4052 nextitem:
4053         le = le2;
4054     }
4055 
4056     if (length > 0) {
4057         UINT64 start_write = max(last_cow_start, start + written);
4058 
4059         Status = excise_extents(fcb->Vcb, fcb, start_write, end_data, Irp, rollback);
4060         if (!NT_SUCCESS(Status)) {
4061             ERR("excise_extents returned %08x\n", Status);
4062             return Status;
4063         }
4064 
4065         Status = insert_extent(fcb->Vcb, fcb, start_write, end_data - start_write, (UINT8*)data + written, Irp, file_write, irp_offset + written, rollback);
4066         if (!NT_SUCCESS(Status)) {
4067             ERR("insert_extent returned %08x\n", Status);
4068             return Status;
4069         }
4070     }
4071 
4072 #ifdef DEBUG_PARANOID
4073     last_off = 0xffffffffffffffff;
4074 
4075     le = fcb->extents.Flink;
4076     while (le != &fcb->extents) {
4077         extent* ext = CONTAINING_RECORD(le, extent, list_entry);
4078 
4079         if (!ext->ignore) {
4080             if (ext->offset == last_off) {
4081                 ERR("offset %llx duplicated\n", ext->offset);
4082                 int3;
4083             } else if (ext->offset < last_off && last_off != 0xffffffffffffffff) {
4084                 ERR("offsets out of order\n");
4085                 int3;
4086             }
4087 
4088             last_off = ext->offset;
4089         }
4090 
4091         le = le->Flink;
4092     }
4093 #endif
4094 
4095     fcb->extents_changed = TRUE;
4096     mark_fcb_dirty(fcb);
4097 
4098     return STATUS_SUCCESS;
4099 }
4100 
4101 NTSTATUS write_compressed(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, PIRP Irp, LIST_ENTRY* rollback) {
4102     NTSTATUS Status;
4103     UINT64 i;
4104 
4105     for (i = 0; i < sector_align(end_data - start_data, COMPRESSED_EXTENT_SIZE) / COMPRESSED_EXTENT_SIZE; i++) {
4106         UINT64 s2, e2;
4107         BOOL compressed;
4108 
4109         s2 = start_data + (i * COMPRESSED_EXTENT_SIZE);
4110         e2 = min(s2 + COMPRESSED_EXTENT_SIZE, end_data);
4111 
4112         Status = write_compressed_bit(fcb, s2, e2, (UINT8*)data + (i * COMPRESSED_EXTENT_SIZE), &compressed, Irp, rollback);
4113 
4114         if (!NT_SUCCESS(Status)) {
4115             ERR("write_compressed_bit returned %08x\n", Status);
4116             return Status;
4117         }
4118 
4119         // If the first 128 KB of a file is incompressible, we set the nocompress flag so we don't
4120         // bother with the rest of it.
4121         if (s2 == 0 && e2 == COMPRESSED_EXTENT_SIZE && !compressed && !fcb->Vcb->options.compress_force) {
4122             fcb->inode_item.flags |= BTRFS_INODE_NOCOMPRESS;
4123             fcb->inode_item_changed = TRUE;
4124             mark_fcb_dirty(fcb);
4125 
4126             // write subsequent data non-compressed
4127             if (e2 < end_data) {
4128                 Status = do_write_file(fcb, e2, end_data, (UINT8*)data + e2, Irp, FALSE, 0, rollback);
4129 
4130                 if (!NT_SUCCESS(Status)) {
4131                     ERR("do_write_file returned %08x\n", Status);
4132                     return Status;
4133                 }
4134             }
4135 
4136             return STATUS_SUCCESS;
4137         }
4138     }
4139 
4140     return STATUS_SUCCESS;
4141 }
4142 
4143 NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOLEAN paging_io, BOOLEAN no_cache,
4144                      BOOLEAN wait, BOOLEAN deferred_write, BOOLEAN write_irp, LIST_ENTRY* rollback) {
4145     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4146     PFILE_OBJECT FileObject = IrpSp->FileObject;
4147     EXTENT_DATA* ed2;
4148     UINT64 off64, newlength, start_data, end_data;
4149     UINT32 bufhead;
4150     BOOL make_inline;
4151     UINT8* data;
4152     INODE_ITEM* origii;
4153     BOOL changed_length = FALSE;
4154     NTSTATUS Status;
4155     LARGE_INTEGER time;
4156     BTRFS_TIME now;
4157     fcb* fcb;
4158     ccb* ccb;
4159     file_ref* fileref;
4160     BOOL paging_lock = FALSE, fcb_lock = FALSE, tree_lock = FALSE, pagefile;
4161     ULONG filter = 0;
4162 
4163     TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb, FileObject, offset.QuadPart, buf, *length, paging_io, no_cache);
4164 
4165     if (*length == 0) {
4166         WARN("returning success for zero-length write\n");
4167         return STATUS_SUCCESS;
4168     }
4169 
4170     if (!FileObject) {
4171         ERR("error - FileObject was NULL\n");
4172         return STATUS_ACCESS_DENIED;
4173     }
4174 
4175     fcb = FileObject->FsContext;
4176     ccb = FileObject->FsContext2;
4177     fileref = ccb ? ccb->fileref : NULL;
4178 
4179     if (!fcb->ads && fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK) {
4180         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);
4181         return STATUS_INVALID_DEVICE_REQUEST;
4182     }
4183 
4184     if (offset.LowPart == FILE_WRITE_TO_END_OF_FILE && offset.HighPart == -1)
4185         offset = fcb->Header.FileSize;
4186 
4187     off64 = offset.QuadPart;
4188 
4189     TRACE("fcb->Header.Flags = %x\n", fcb->Header.Flags);
4190 
4191     if (!no_cache && !CcCanIWrite(FileObject, *length, wait, deferred_write))
4192         return STATUS_PENDING;
4193 
4194     if (!wait && no_cache)
4195         return STATUS_PENDING;
4196 
4197     if (no_cache && !paging_io && FileObject->SectionObjectPointer->DataSectionObject) {
4198         IO_STATUS_BLOCK iosb;
4199 
4200         ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
4201 
4202         CcFlushCache(FileObject->SectionObjectPointer, &offset, *length, &iosb);
4203 
4204         if (!NT_SUCCESS(iosb.Status)) {
4205             ExReleaseResourceLite(fcb->Header.PagingIoResource);
4206             ERR("CcFlushCache returned %08x\n", iosb.Status);
4207             return iosb.Status;
4208         }
4209 
4210         paging_lock = TRUE;
4211 
4212         CcPurgeCacheSection(FileObject->SectionObjectPointer, &offset, *length, FALSE);
4213     }
4214 
4215     if (paging_io) {
4216         if (!ExAcquireResourceSharedLite(fcb->Header.PagingIoResource, wait)) {
4217             Status = STATUS_PENDING;
4218             goto end;
4219         } else
4220             paging_lock = TRUE;
4221     }
4222 
4223     pagefile = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE && paging_io;
4224 
4225     if (!pagefile && !ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
4226         if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) {
4227             Status = STATUS_PENDING;
4228             goto end;
4229         } else
4230             tree_lock = TRUE;
4231     }
4232 
4233     if (no_cache) {
4234         if (pagefile) {
4235             if (!ExAcquireResourceSharedLite(fcb->Header.Resource, wait)) {
4236                 Status = STATUS_PENDING;
4237                 goto end;
4238             } else
4239                 fcb_lock = TRUE;
4240         } else if (!ExIsResourceAcquiredExclusiveLite(fcb->Header.Resource)) {
4241             if (!ExAcquireResourceExclusiveLite(fcb->Header.Resource, wait)) {
4242                 Status = STATUS_PENDING;
4243                 goto end;
4244             } else
4245                 fcb_lock = TRUE;
4246         }
4247     }
4248 
4249     newlength = fcb->ads ? fcb->adsdata.Length : fcb->inode_item.st_size;
4250 
4251     if (fcb->deleted)
4252         newlength = 0;
4253 
4254     TRACE("newlength = %llx\n", newlength);
4255 
4256     if (off64 + *length > newlength) {
4257         if (paging_io) {
4258             if (off64 >= newlength) {
4259                 TRACE("paging IO tried to write beyond end of file (file size = %llx, offset = %llx, length = %x)\n", newlength, off64, *length);
4260                 TRACE("filename %S\n", file_desc(FileObject));
4261                 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
4262                     fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
4263                 Status = STATUS_SUCCESS;
4264                 goto end;
4265             }
4266 
4267             *length = (ULONG)(newlength - off64);
4268         } else {
4269             newlength = off64 + *length;
4270             changed_length = TRUE;
4271 
4272             TRACE("extending length to %llx\n", newlength);
4273         }
4274     }
4275 
4276     if (fcb->ads)
4277         make_inline = FALSE;
4278     else if (fcb->type == BTRFS_TYPE_SYMLINK)
4279         make_inline = newlength <= (Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - offsetof(EXTENT_DATA, data[0]));
4280     else
4281         make_inline = newlength <= fcb->Vcb->options.max_inline;
4282 
4283     if (changed_length) {
4284         if (newlength > (UINT64)fcb->Header.AllocationSize.QuadPart) {
4285             if (!tree_lock) {
4286                 // We need to acquire the tree lock if we don't have it already -
4287                 // we can't give an inline file proper extents at the same time as we're
4288                 // doing a flush.
4289                 if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) {
4290                     Status = STATUS_PENDING;
4291                     goto end;
4292                 } else
4293                     tree_lock = TRUE;
4294             }
4295 
4296             Status = extend_file(fcb, fileref, newlength, FALSE, Irp, rollback);
4297             if (!NT_SUCCESS(Status)) {
4298                 ERR("extend_file returned %08x\n", Status);
4299                 goto end;
4300             }
4301         } else if (!fcb->ads)
4302             fcb->inode_item.st_size = newlength;
4303 
4304         fcb->Header.FileSize.QuadPart = newlength;
4305         fcb->Header.ValidDataLength.QuadPart = newlength;
4306 
4307         TRACE("AllocationSize = %llx\n", fcb->Header.AllocationSize.QuadPart);
4308         TRACE("FileSize = %llx\n", fcb->Header.FileSize.QuadPart);
4309         TRACE("ValidDataLength = %llx\n", fcb->Header.ValidDataLength.QuadPart);
4310     }
4311 
4312     if (!no_cache) {
4313         Status = STATUS_SUCCESS;
4314 
4315         _SEH2_TRY {
4316             if (!FileObject->PrivateCacheMap || changed_length) {
4317                 CC_FILE_SIZES ccfs;
4318 
4319                 ccfs.AllocationSize = fcb->Header.AllocationSize;
4320                 ccfs.FileSize = fcb->Header.FileSize;
4321                 ccfs.ValidDataLength = fcb->Header.ValidDataLength;
4322 
4323                 if (!FileObject->PrivateCacheMap)
4324                     init_file_cache(FileObject, &ccfs);
4325 
4326                 CcSetFileSizes(FileObject, &ccfs);
4327             }
4328 
4329             if (IrpSp->MinorFunction & IRP_MN_MDL) {
4330                 CcPrepareMdlWrite(FileObject, &offset, *length, &Irp->MdlAddress, &Irp->IoStatus);
4331 
4332                 Status = Irp->IoStatus.Status;
4333                 goto end;
4334             } else {
4335                 if (fCcCopyWriteEx) {
4336                     TRACE("CcCopyWriteEx(%p, %llx, %x, %u, %p, %p)\n", FileObject, off64, *length, wait, buf, Irp->Tail.Overlay.Thread);
4337                     if (!fCcCopyWriteEx(FileObject, &offset, *length, wait, buf, Irp->Tail.Overlay.Thread)) {
4338                         Status = STATUS_PENDING;
4339                         goto end;
4340                     }
4341                     TRACE("CcCopyWriteEx finished\n");
4342                 } else {
4343                     TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject, off64, *length, wait, buf);
4344                     if (!CcCopyWrite(FileObject, &offset, *length, wait, buf)) {
4345                         Status = STATUS_PENDING;
4346                         goto end;
4347                     }
4348                     TRACE("CcCopyWrite finished\n");
4349                 }
4350             }
4351         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
4352             Status = _SEH2_GetExceptionCode();
4353         } _SEH2_END;
4354 
4355         if (changed_length) {
4356             send_notification_fcb(fcb->ads ? fileref->parent : fileref, fcb->ads ? FILE_NOTIFY_CHANGE_STREAM_SIZE : FILE_NOTIFY_CHANGE_SIZE,
4357                                   fcb->ads ? FILE_ACTION_MODIFIED_STREAM : FILE_ACTION_MODIFIED, fcb->ads && fileref->dc ? &fileref->dc->name : NULL);
4358         }
4359 
4360         goto end;
4361     }
4362 
4363     if (fcb->ads) {
4364         if (changed_length) {
4365             char* data2;
4366 
4367             if (newlength > fcb->adsmaxlen) {
4368                 ERR("error - xattr too long (%llu > %u)\n", newlength, fcb->adsmaxlen);
4369                 Status = STATUS_DISK_FULL;
4370                 goto end;
4371             }
4372 
4373             data2 = ExAllocatePoolWithTag(PagedPool, (ULONG)newlength, ALLOC_TAG);
4374             if (!data2) {
4375                 ERR("out of memory\n");
4376                 Status = STATUS_INSUFFICIENT_RESOURCES;
4377                 goto end;
4378             }
4379 
4380             if (fcb->adsdata.Buffer) {
4381                 RtlCopyMemory(data2, fcb->adsdata.Buffer, fcb->adsdata.Length);
4382                 ExFreePool(fcb->adsdata.Buffer);
4383             }
4384 
4385             if (newlength > fcb->adsdata.Length)
4386                 RtlZeroMemory(&data2[fcb->adsdata.Length], (ULONG)(newlength - fcb->adsdata.Length));
4387 
4388 
4389             fcb->adsdata.Buffer = data2;
4390             fcb->adsdata.Length = fcb->adsdata.MaximumLength = (USHORT)newlength;
4391 
4392             fcb->Header.AllocationSize.QuadPart = newlength;
4393             fcb->Header.FileSize.QuadPart = newlength;
4394             fcb->Header.ValidDataLength.QuadPart = newlength;
4395         }
4396 
4397         if (*length > 0)
4398             RtlCopyMemory(&fcb->adsdata.Buffer[off64], buf, *length);
4399 
4400         fcb->Header.ValidDataLength.QuadPart = newlength;
4401 
4402         mark_fcb_dirty(fcb);
4403 
4404         if (fileref)
4405             mark_fileref_dirty(fileref);
4406     } else {
4407         BOOL compress = write_fcb_compressed(fcb), no_buf = FALSE;
4408 
4409         if (make_inline) {
4410             start_data = 0;
4411             end_data = sector_align(newlength, fcb->Vcb->superblock.sector_size);
4412             bufhead = sizeof(EXTENT_DATA) - 1;
4413         } else if (compress) {
4414             start_data = off64 & ~(UINT64)(COMPRESSED_EXTENT_SIZE - 1);
4415             end_data = min(sector_align(off64 + *length, COMPRESSED_EXTENT_SIZE),
4416                            sector_align(newlength, fcb->Vcb->superblock.sector_size));
4417             bufhead = 0;
4418         } else {
4419             start_data = off64 & ~(UINT64)(fcb->Vcb->superblock.sector_size - 1);
4420             end_data = sector_align(off64 + *length, fcb->Vcb->superblock.sector_size);
4421             bufhead = 0;
4422         }
4423 
4424         if (fcb_is_inline(fcb))
4425             end_data = max(end_data, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size));
4426 
4427         fcb->Header.ValidDataLength.QuadPart = newlength;
4428         TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart);
4429 
4430         if (!make_inline && !compress && off64 == start_data && off64 + *length == end_data) {
4431             data = buf;
4432             no_buf = TRUE;
4433         } else {
4434             data = ExAllocatePoolWithTag(PagedPool, (ULONG)(end_data - start_data + bufhead), ALLOC_TAG);
4435             if (!data) {
4436                 ERR("out of memory\n");
4437                 Status = STATUS_INSUFFICIENT_RESOURCES;
4438                 goto end;
4439             }
4440 
4441             RtlZeroMemory(data + bufhead, (ULONG)(end_data - start_data));
4442 
4443             TRACE("start_data = %llx\n", start_data);
4444             TRACE("end_data = %llx\n", end_data);
4445 
4446             if (off64 > start_data || off64 + *length < end_data) {
4447                 if (changed_length) {
4448                     if (fcb->inode_item.st_size > start_data)
4449                         Status = read_file(fcb, data + bufhead, start_data, fcb->inode_item.st_size - start_data, NULL, Irp);
4450                     else
4451                         Status = STATUS_SUCCESS;
4452                 } else
4453                     Status = read_file(fcb, data + bufhead, start_data, end_data - start_data, NULL, Irp);
4454 
4455                 if (!NT_SUCCESS(Status)) {
4456                     ERR("read_file returned %08x\n", Status);
4457                     ExFreePool(data);
4458                     goto end;
4459                 }
4460             }
4461 
4462             RtlCopyMemory(data + bufhead + off64 - start_data, buf, *length);
4463         }
4464 
4465         if (make_inline) {
4466             Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback);
4467             if (!NT_SUCCESS(Status)) {
4468                 ERR("error - excise_extents returned %08x\n", Status);
4469                 ExFreePool(data);
4470                 goto end;
4471             }
4472 
4473             ed2 = (EXTENT_DATA*)data;
4474             ed2->generation = fcb->Vcb->superblock.generation;
4475             ed2->decoded_size = newlength;
4476             ed2->compression = BTRFS_COMPRESSION_NONE;
4477             ed2->encryption = BTRFS_ENCRYPTION_NONE;
4478             ed2->encoding = BTRFS_ENCODING_NONE;
4479             ed2->type = EXTENT_TYPE_INLINE;
4480 
4481             Status = add_extent_to_fcb(fcb, 0, ed2, (UINT16)(offsetof(EXTENT_DATA, data[0]) + newlength), FALSE, NULL, rollback);
4482             if (!NT_SUCCESS(Status)) {
4483                 ERR("add_extent_to_fcb returned %08x\n", Status);
4484                 ExFreePool(data);
4485                 goto end;
4486             }
4487 
4488             fcb->inode_item.st_blocks += newlength;
4489         } else if (compress) {
4490             Status = write_compressed(fcb, start_data, end_data, data, Irp, rollback);
4491 
4492             if (!NT_SUCCESS(Status)) {
4493                 ERR("write_compressed returned %08x\n", Status);
4494                 ExFreePool(data);
4495                 goto end;
4496             }
4497 
4498             ExFreePool(data);
4499         } else {
4500             if (write_irp && Irp->MdlAddress && no_buf) {
4501                 BOOL locked = Irp->MdlAddress->MdlFlags & MDL_PAGES_LOCKED;
4502 
4503                 if (!locked) {
4504                     Status = STATUS_SUCCESS;
4505 
4506                     _SEH2_TRY {
4507                         MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoReadAccess);
4508                     } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
4509                         Status = _SEH2_GetExceptionCode();
4510                     } _SEH2_END;
4511 
4512                     if (!NT_SUCCESS(Status)) {
4513                         ERR("MmProbeAndLockPages threw exception %08x\n", Status);
4514                         goto end;
4515                     }
4516                 }
4517 
4518                 _SEH2_TRY {
4519                     Status = do_write_file(fcb, start_data, end_data, data, Irp, TRUE, 0, rollback);
4520                 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
4521                     Status = _SEH2_GetExceptionCode();
4522                 } _SEH2_END;
4523 
4524                 if (!locked)
4525                     MmUnlockPages(Irp->MdlAddress);
4526             } else {
4527                 _SEH2_TRY {
4528                     Status = do_write_file(fcb, start_data, end_data, data, Irp, FALSE, 0, rollback);
4529                 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
4530                     Status = _SEH2_GetExceptionCode();
4531                 } _SEH2_END;
4532             }
4533 
4534             if (!NT_SUCCESS(Status)) {
4535                 ERR("do_write_file returned %08x\n", Status);
4536                 if (!no_buf) ExFreePool(data);
4537                 goto end;
4538             }
4539 
4540             if (!no_buf)
4541                 ExFreePool(data);
4542         }
4543     }
4544 
4545     KeQuerySystemTime(&time);
4546     win_time_to_unix(time, &now);
4547 
4548     if (!pagefile) {
4549         if (fcb->ads) {
4550             if (fileref && fileref->parent)
4551                 origii = &fileref->parent->fcb->inode_item;
4552             else {
4553                 ERR("no parent fcb found for stream\n");
4554                 Status = STATUS_INTERNAL_ERROR;
4555                 goto end;
4556             }
4557         } else
4558             origii = &fcb->inode_item;
4559 
4560         origii->transid = Vcb->superblock.generation;
4561         origii->sequence++;
4562 
4563         if (!ccb->user_set_change_time)
4564             origii->st_ctime = now;
4565 
4566         if (!fcb->ads) {
4567             if (changed_length) {
4568                 TRACE("setting st_size to %llx\n", newlength);
4569                 origii->st_size = newlength;
4570                 filter |= FILE_NOTIFY_CHANGE_SIZE;
4571             }
4572 
4573             fcb->inode_item_changed = TRUE;
4574         } else {
4575             fileref->parent->fcb->inode_item_changed = TRUE;
4576 
4577             if (changed_length)
4578                 filter |= FILE_NOTIFY_CHANGE_STREAM_SIZE;
4579 
4580             filter |= FILE_NOTIFY_CHANGE_STREAM_WRITE;
4581         }
4582 
4583         if (!ccb->user_set_write_time) {
4584             origii->st_mtime = now;
4585             filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
4586         }
4587 
4588         mark_fcb_dirty(fcb->ads ? fileref->parent->fcb : fcb);
4589     }
4590 
4591     if (changed_length) {
4592         CC_FILE_SIZES ccfs;
4593 
4594         ccfs.AllocationSize = fcb->Header.AllocationSize;
4595         ccfs.FileSize = fcb->Header.FileSize;
4596         ccfs.ValidDataLength = fcb->Header.ValidDataLength;
4597 
4598         _SEH2_TRY {
4599             CcSetFileSizes(FileObject, &ccfs);
4600         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
4601             Status = _SEH2_GetExceptionCode();
4602             goto end;
4603         } _SEH2_END;
4604     }
4605 
4606     fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
4607     fcb->subvol->root_item.ctime = now;
4608 
4609     Status = STATUS_SUCCESS;
4610 
4611     if (filter != 0)
4612         send_notification_fcb(fcb->ads ? fileref->parent : fileref, filter, fcb->ads ? FILE_ACTION_MODIFIED_STREAM : FILE_ACTION_MODIFIED,
4613                               fcb->ads && fileref->dc ? &fileref->dc->name : NULL);
4614 
4615 end:
4616     if (NT_SUCCESS(Status) && FileObject->Flags & FO_SYNCHRONOUS_IO && !paging_io) {
4617         TRACE("CurrentByteOffset was: %llx\n", FileObject->CurrentByteOffset.QuadPart);
4618         FileObject->CurrentByteOffset.QuadPart = offset.QuadPart + (NT_SUCCESS(Status) ? *length : 0);
4619         TRACE("CurrentByteOffset now: %llx\n", FileObject->CurrentByteOffset.QuadPart);
4620     }
4621 
4622     if (fcb_lock)
4623         ExReleaseResourceLite(fcb->Header.Resource);
4624 
4625     if (tree_lock)
4626         ExReleaseResourceLite(&Vcb->tree_lock);
4627 
4628     if (paging_lock)
4629         ExReleaseResourceLite(fcb->Header.PagingIoResource);
4630 
4631     return Status;
4632 }
4633 
4634 NTSTATUS write_file(device_extension* Vcb, PIRP Irp, BOOLEAN wait, BOOLEAN deferred_write) {
4635     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4636     void* buf;
4637     NTSTATUS Status;
4638     LARGE_INTEGER offset = IrpSp->Parameters.Write.ByteOffset;
4639     PFILE_OBJECT FileObject = IrpSp->FileObject;
4640     fcb* fcb = FileObject ? FileObject->FsContext : NULL;
4641     LIST_ENTRY rollback;
4642 
4643     InitializeListHead(&rollback);
4644 
4645     TRACE("write\n");
4646 
4647     Irp->IoStatus.Information = 0;
4648 
4649     TRACE("offset = %llx\n", offset.QuadPart);
4650     TRACE("length = %x\n", IrpSp->Parameters.Write.Length);
4651 
4652     if (!Irp->AssociatedIrp.SystemBuffer) {
4653         buf = map_user_buffer(Irp, fcb && fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE ? HighPagePriority : NormalPagePriority);
4654 
4655         if (Irp->MdlAddress && !buf) {
4656             ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
4657             Status = STATUS_INSUFFICIENT_RESOURCES;
4658             goto exit;
4659         }
4660     } else
4661         buf = Irp->AssociatedIrp.SystemBuffer;
4662 
4663     TRACE("buf = %p\n", buf);
4664 
4665     if (fcb && !(Irp->Flags & IRP_PAGING_IO) && !FsRtlCheckLockForWriteAccess(&fcb->lock, Irp)) {
4666         WARN("tried to write to locked region\n");
4667         Status = STATUS_FILE_LOCK_CONFLICT;
4668         goto exit;
4669     }
4670 
4671     Status = write_file2(Vcb, Irp, offset, buf, &IrpSp->Parameters.Write.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE,
4672                          wait, deferred_write, TRUE, &rollback);
4673 
4674     if (Status == STATUS_PENDING)
4675         goto exit;
4676     else if (!NT_SUCCESS(Status)) {
4677         ERR("write_file2 returned %08x\n", Status);
4678         goto exit;
4679     }
4680 
4681     if (NT_SUCCESS(Status)) {
4682         Irp->IoStatus.Information = IrpSp->Parameters.Write.Length;
4683 
4684         if (diskacc && Status != STATUS_PENDING && Irp->Flags & IRP_NOCACHE) {
4685             PETHREAD thread = NULL;
4686 
4687             if (Irp->Tail.Overlay.Thread && !IoIsSystemThread(Irp->Tail.Overlay.Thread))
4688                 thread = Irp->Tail.Overlay.Thread;
4689             else if (!IoIsSystemThread(PsGetCurrentThread()))
4690                 thread = PsGetCurrentThread();
4691             else if (IoIsSystemThread(PsGetCurrentThread()) && IoGetTopLevelIrp() == Irp)
4692                 thread = PsGetCurrentThread();
4693 
4694             if (thread)
4695                 fPsUpdateDiskCounters(PsGetThreadProcess(thread), 0, IrpSp->Parameters.Write.Length, 0, 1, 0);
4696         }
4697     }
4698 
4699 exit:
4700     if (NT_SUCCESS(Status))
4701         clear_rollback(&rollback);
4702     else
4703         do_rollback(Vcb, &rollback);
4704 
4705     return Status;
4706 }
4707 
4708 _Dispatch_type_(IRP_MJ_WRITE)
4709 _Function_class_(DRIVER_DISPATCH)
4710 NTSTATUS NTAPI drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4711     NTSTATUS Status;
4712     BOOL top_level;
4713     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4714     device_extension* Vcb = DeviceObject->DeviceExtension;
4715     PFILE_OBJECT FileObject = IrpSp->FileObject;
4716     fcb* fcb = FileObject ? FileObject->FsContext : NULL;
4717     ccb* ccb = FileObject ? FileObject->FsContext2 : NULL;
4718     BOOLEAN wait = FileObject ? IoIsOperationSynchronous(Irp) : TRUE;
4719 
4720     FsRtlEnterFileSystem();
4721 
4722     top_level = is_top_level(Irp);
4723 
4724     if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4725         Status = vol_write(DeviceObject, Irp);
4726         goto exit;
4727     } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
4728         Status = STATUS_INVALID_PARAMETER;
4729         goto end;
4730     }
4731 
4732     if (!fcb) {
4733         ERR("fcb was NULL\n");
4734         Status = STATUS_INVALID_PARAMETER;
4735         goto end;
4736     }
4737 
4738     if (!ccb) {
4739         ERR("ccb was NULL\n");
4740         Status = STATUS_INVALID_PARAMETER;
4741         goto end;
4742     }
4743 
4744     if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
4745         WARN("insufficient permissions\n");
4746         Status = STATUS_ACCESS_DENIED;
4747         goto end;
4748     }
4749 
4750     if (fcb == Vcb->volume_fcb) {
4751         if (!Vcb->locked || Vcb->locked_fileobj != FileObject) {
4752             ERR("trying to write to volume when not locked, or locked with another FileObject\n");
4753             Status = STATUS_ACCESS_DENIED;
4754             goto end;
4755         }
4756 
4757         TRACE("writing directly to volume\n");
4758 
4759         IoSkipCurrentIrpStackLocation(Irp);
4760 
4761         Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
4762         goto exit;
4763     }
4764 
4765     if (is_subvol_readonly(fcb->subvol, Irp)) {
4766         Status = STATUS_ACCESS_DENIED;
4767         goto end;
4768     }
4769 
4770     if (Vcb->readonly) {
4771         Status = STATUS_MEDIA_WRITE_PROTECTED;
4772         goto end;
4773     }
4774 
4775     _SEH2_TRY {
4776         if (IrpSp->MinorFunction & IRP_MN_COMPLETE) {
4777             CcMdlWriteComplete(IrpSp->FileObject, &IrpSp->Parameters.Write.ByteOffset, Irp->MdlAddress);
4778 
4779             Irp->MdlAddress = NULL;
4780             Status = STATUS_SUCCESS;
4781         } else {
4782             // Don't offload jobs when doing paging IO - otherwise this can lead to
4783             // deadlocks in CcCopyWrite.
4784             if (Irp->Flags & IRP_PAGING_IO)
4785                 wait = TRUE;
4786 
4787             Status = write_file(Vcb, Irp, wait, FALSE);
4788         }
4789     } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
4790         Status = _SEH2_GetExceptionCode();
4791     } _SEH2_END;
4792 
4793 end:
4794     Irp->IoStatus.Status = Status;
4795 
4796     TRACE("wrote %u bytes\n", Irp->IoStatus.Information);
4797 
4798     if (Status != STATUS_PENDING)
4799         IoCompleteRequest(Irp, IO_NO_INCREMENT);
4800     else {
4801         IoMarkIrpPending(Irp);
4802 
4803         if (!add_thread_job(Vcb, Irp))
4804             do_write_job(Vcb, Irp);
4805     }
4806 
4807 exit:
4808     if (top_level)
4809         IoSetTopLevelIrp(NULL);
4810 
4811     TRACE("returning %08x\n", Status);
4812 
4813     FsRtlExitFileSystem();
4814 
4815     return Status;
4816 }
4817