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