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