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