[BTRFS] Leak the root stream file object on mount.
[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 le = le->Flink;
2337 }
2338
2339 while (!IsListEmpty(&wtc->stripes)) {
2340 write_data_stripe* stripe = CONTAINING_RECORD(RemoveHeadList(&wtc->stripes), write_data_stripe, list_entry);
2341
2342 ExFreePool(stripe);
2343 }
2344 }
2345
2346 void add_extent(_In_ fcb* fcb, _In_ LIST_ENTRY* prevextle, _In_ __drv_aliasesMem extent* newext) {
2347 LIST_ENTRY* le = prevextle->Flink;
2348
2349 while (le != &fcb->extents) {
2350 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
2351
2352 if (ext->offset >= newext->offset) {
2353 InsertHeadList(ext->list_entry.Blink, &newext->list_entry);
2354 return;
2355 }
2356
2357 le = le->Flink;
2358 }
2359
2360 InsertTailList(&fcb->extents, &newext->list_entry);
2361 }
2362
2363 NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, PIRP Irp, LIST_ENTRY* rollback) {
2364 NTSTATUS Status;
2365 LIST_ENTRY* le;
2366
2367 le = fcb->extents.Flink;
2368
2369 while (le != &fcb->extents) {
2370 LIST_ENTRY* le2 = le->Flink;
2371 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
2372 EXTENT_DATA* ed = &ext->extent_data;
2373 EXTENT_DATA2* ed2 = NULL;
2374 UINT64 len;
2375
2376 if (!ext->ignore) {
2377 if (ed->type != EXTENT_TYPE_INLINE)
2378 ed2 = (EXTENT_DATA2*)ed->data;
2379
2380 len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
2381
2382 if (ext->offset < end_data && ext->offset + len > start_data) {
2383 if (ed->type == EXTENT_TYPE_INLINE) {
2384 if (start_data <= ext->offset && end_data >= ext->offset + len) { // remove all
2385 remove_fcb_extent(fcb, ext, rollback);
2386
2387 fcb->inode_item.st_blocks -= len;
2388 fcb->inode_item_changed = TRUE;
2389 } else {
2390 ERR("trying to split inline extent\n");
2391 #ifdef DEBUG_PARANOID
2392 int3;
2393 #endif
2394 return STATUS_INTERNAL_ERROR;
2395 }
2396 } else if (ed->type != EXTENT_TYPE_INLINE) {
2397 if (start_data <= ext->offset && end_data >= ext->offset + len) { // remove all
2398 if (ed2->size != 0) {
2399 chunk* c;
2400
2401 fcb->inode_item.st_blocks -= len;
2402 fcb->inode_item_changed = TRUE;
2403
2404 c = get_chunk_from_address(Vcb, ed2->address);
2405
2406 if (!c) {
2407 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
2408 } else {
2409 Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, -1,
2410 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp);
2411 if (!NT_SUCCESS(Status)) {
2412 ERR("update_changed_extent_ref returned %08x\n", Status);
2413 goto end;
2414 }
2415 }
2416 }
2417
2418 remove_fcb_extent(fcb, ext, rollback);
2419 } else if (start_data <= ext->offset && end_data < ext->offset + len) { // remove beginning
2420 EXTENT_DATA2* ned2;
2421 extent* newext;
2422
2423 if (ed2->size != 0) {
2424 fcb->inode_item.st_blocks -= end_data - ext->offset;
2425 fcb->inode_item_changed = TRUE;
2426 }
2427
2428 newext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
2429 if (!newext) {
2430 ERR("out of memory\n");
2431 Status = STATUS_INSUFFICIENT_RESOURCES;
2432 goto end;
2433 }
2434
2435 ned2 = (EXTENT_DATA2*)newext->extent_data.data;
2436
2437 newext->extent_data.generation = Vcb->superblock.generation;
2438 newext->extent_data.decoded_size = ed->decoded_size;
2439 newext->extent_data.compression = ed->compression;
2440 newext->extent_data.encryption = ed->encryption;
2441 newext->extent_data.encoding = ed->encoding;
2442 newext->extent_data.type = ed->type;
2443 ned2->address = ed2->address;
2444 ned2->size = ed2->size;
2445 ned2->offset = ed2->offset + (end_data - ext->offset);
2446 ned2->num_bytes = ed2->num_bytes - (end_data - ext->offset);
2447
2448 newext->offset = end_data;
2449 newext->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
2450 newext->unique = ext->unique;
2451 newext->ignore = FALSE;
2452 newext->inserted = TRUE;
2453
2454 if (ext->csum) {
2455 if (ed->compression == BTRFS_COMPRESSION_NONE) {
2456 newext->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ned2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2457 if (!newext->csum) {
2458 ERR("out of memory\n");
2459 Status = STATUS_INSUFFICIENT_RESOURCES;
2460 ExFreePool(newext);
2461 goto end;
2462 }
2463
2464 RtlCopyMemory(newext->csum, &ext->csum[(end_data - ext->offset) / Vcb->superblock.sector_size],
2465 (ULONG)(ned2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size));
2466 } else {
2467 newext->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2468 if (!newext->csum) {
2469 ERR("out of memory\n");
2470 Status = STATUS_INSUFFICIENT_RESOURCES;
2471 ExFreePool(newext);
2472 goto end;
2473 }
2474
2475 RtlCopyMemory(newext->csum, ext->csum, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size));
2476 }
2477 } else
2478 newext->csum = NULL;
2479
2480 add_extent(fcb, &ext->list_entry, newext);
2481
2482 remove_fcb_extent(fcb, ext, rollback);
2483 } else if (start_data > ext->offset && end_data >= ext->offset + len) { // remove end
2484 EXTENT_DATA2* ned2;
2485 extent* newext;
2486
2487 if (ed2->size != 0) {
2488 fcb->inode_item.st_blocks -= ext->offset + len - start_data;
2489 fcb->inode_item_changed = TRUE;
2490 }
2491
2492 newext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
2493 if (!newext) {
2494 ERR("out of memory\n");
2495 Status = STATUS_INSUFFICIENT_RESOURCES;
2496 goto end;
2497 }
2498
2499 ned2 = (EXTENT_DATA2*)newext->extent_data.data;
2500
2501 newext->extent_data.generation = Vcb->superblock.generation;
2502 newext->extent_data.decoded_size = ed->decoded_size;
2503 newext->extent_data.compression = ed->compression;
2504 newext->extent_data.encryption = ed->encryption;
2505 newext->extent_data.encoding = ed->encoding;
2506 newext->extent_data.type = ed->type;
2507 ned2->address = ed2->address;
2508 ned2->size = ed2->size;
2509 ned2->offset = ed2->offset;
2510 ned2->num_bytes = start_data - ext->offset;
2511
2512 newext->offset = ext->offset;
2513 newext->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
2514 newext->unique = ext->unique;
2515 newext->ignore = FALSE;
2516 newext->inserted = TRUE;
2517
2518 if (ext->csum) {
2519 if (ed->compression == BTRFS_COMPRESSION_NONE) {
2520 newext->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ned2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2521 if (!newext->csum) {
2522 ERR("out of memory\n");
2523 Status = STATUS_INSUFFICIENT_RESOURCES;
2524 ExFreePool(newext);
2525 goto end;
2526 }
2527
2528 RtlCopyMemory(newext->csum, ext->csum, (ULONG)(ned2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size));
2529 } else {
2530 newext->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2531 if (!newext->csum) {
2532 ERR("out of memory\n");
2533 Status = STATUS_INSUFFICIENT_RESOURCES;
2534 ExFreePool(newext);
2535 goto end;
2536 }
2537
2538 RtlCopyMemory(newext->csum, ext->csum, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size));
2539 }
2540 } else
2541 newext->csum = NULL;
2542
2543 InsertHeadList(&ext->list_entry, &newext->list_entry);
2544
2545 remove_fcb_extent(fcb, ext, rollback);
2546 } else if (start_data > ext->offset && end_data < ext->offset + len) { // remove middle
2547 EXTENT_DATA2 *neda2, *nedb2;
2548 extent *newext1, *newext2;
2549
2550 if (ed2->size != 0) {
2551 chunk* c;
2552
2553 fcb->inode_item.st_blocks -= end_data - start_data;
2554 fcb->inode_item_changed = TRUE;
2555
2556 c = get_chunk_from_address(Vcb, ed2->address);
2557
2558 if (!c) {
2559 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
2560 } else {
2561 Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
2562 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp);
2563 if (!NT_SUCCESS(Status)) {
2564 ERR("update_changed_extent_ref returned %08x\n", Status);
2565 goto end;
2566 }
2567 }
2568 }
2569
2570 newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
2571 if (!newext1) {
2572 ERR("out of memory\n");
2573 Status = STATUS_INSUFFICIENT_RESOURCES;
2574 goto end;
2575 }
2576
2577 newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG);
2578 if (!newext2) {
2579 ERR("out of memory\n");
2580 Status = STATUS_INSUFFICIENT_RESOURCES;
2581 ExFreePool(newext1);
2582 goto end;
2583 }
2584
2585 neda2 = (EXTENT_DATA2*)newext1->extent_data.data;
2586
2587 newext1->extent_data.generation = Vcb->superblock.generation;
2588 newext1->extent_data.decoded_size = ed->decoded_size;
2589 newext1->extent_data.compression = ed->compression;
2590 newext1->extent_data.encryption = ed->encryption;
2591 newext1->extent_data.encoding = ed->encoding;
2592 newext1->extent_data.type = ed->type;
2593 neda2->address = ed2->address;
2594 neda2->size = ed2->size;
2595 neda2->offset = ed2->offset;
2596 neda2->num_bytes = start_data - ext->offset;
2597
2598 nedb2 = (EXTENT_DATA2*)newext2->extent_data.data;
2599
2600 newext2->extent_data.generation = Vcb->superblock.generation;
2601 newext2->extent_data.decoded_size = ed->decoded_size;
2602 newext2->extent_data.compression = ed->compression;
2603 newext2->extent_data.encryption = ed->encryption;
2604 newext2->extent_data.encoding = ed->encoding;
2605 newext2->extent_data.type = ed->type;
2606 nedb2->address = ed2->address;
2607 nedb2->size = ed2->size;
2608 nedb2->offset = ed2->offset + (end_data - ext->offset);
2609 nedb2->num_bytes = ext->offset + len - end_data;
2610
2611 newext1->offset = ext->offset;
2612 newext1->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
2613 newext1->unique = ext->unique;
2614 newext1->ignore = FALSE;
2615 newext1->inserted = TRUE;
2616
2617 newext2->offset = end_data;
2618 newext2->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2);
2619 newext2->unique = ext->unique;
2620 newext2->ignore = FALSE;
2621 newext2->inserted = TRUE;
2622
2623 if (ext->csum) {
2624 if (ed->compression == BTRFS_COMPRESSION_NONE) {
2625 newext1->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(neda2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2626 if (!newext1->csum) {
2627 ERR("out of memory\n");
2628 Status = STATUS_INSUFFICIENT_RESOURCES;
2629 ExFreePool(newext1);
2630 ExFreePool(newext2);
2631 goto end;
2632 }
2633
2634 newext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(nedb2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2635 if (!newext2->csum) {
2636 ERR("out of memory\n");
2637 Status = STATUS_INSUFFICIENT_RESOURCES;
2638 ExFreePool(newext1->csum);
2639 ExFreePool(newext1);
2640 ExFreePool(newext2);
2641 goto end;
2642 }
2643
2644 RtlCopyMemory(newext1->csum, ext->csum, (ULONG)(neda2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size));
2645 RtlCopyMemory(newext2->csum, &ext->csum[(end_data - ext->offset) / Vcb->superblock.sector_size],
2646 (ULONG)(nedb2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size));
2647 } else {
2648 newext1->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2649 if (!newext1->csum) {
2650 ERR("out of memory\n");
2651 Status = STATUS_INSUFFICIENT_RESOURCES;
2652 ExFreePool(newext1);
2653 ExFreePool(newext2);
2654 goto end;
2655 }
2656
2657 newext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG);
2658 if (!newext2->csum) {
2659 ERR("out of memory\n");
2660 Status = STATUS_INSUFFICIENT_RESOURCES;
2661 ExFreePool(newext1->csum);
2662 ExFreePool(newext1);
2663 ExFreePool(newext2);
2664 goto end;
2665 }
2666
2667 RtlCopyMemory(newext1->csum, ext->csum, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size));
2668 RtlCopyMemory(newext2->csum, ext->csum, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size));
2669 }
2670 } else {
2671 newext1->csum = NULL;
2672 newext2->csum = NULL;
2673 }
2674
2675 InsertHeadList(&ext->list_entry, &newext1->list_entry);
2676 add_extent(fcb, &newext1->list_entry, newext2);
2677
2678 remove_fcb_extent(fcb, ext, rollback);
2679 }
2680 }
2681 }
2682 }
2683
2684 le = le2;
2685 }
2686
2687 Status = STATUS_SUCCESS;
2688
2689 end:
2690 fcb->extents_changed = TRUE;
2691 mark_fcb_dirty(fcb);
2692
2693 return Status;
2694 }
2695
2696 void add_insert_extent_rollback(LIST_ENTRY* rollback, fcb* fcb, extent* ext) {
2697 rollback_extent* re;
2698
2699 re = ExAllocatePoolWithTag(NonPagedPool, sizeof(rollback_extent), ALLOC_TAG);
2700 if (!re) {
2701 ERR("out of memory\n");
2702 return;
2703 }
2704
2705 re->fcb = fcb;
2706 re->ext = ext;
2707
2708 add_rollback(rollback, ROLLBACK_INSERT_EXTENT, re);
2709 }
2710
2711 #ifdef _MSC_VER
2712 #pragma warning(push)
2713 #pragma warning(suppress: 28194)
2714 #endif
2715 NTSTATUS add_extent_to_fcb(_In_ fcb* fcb, _In_ UINT64 offset, _In_reads_bytes_(edsize) EXTENT_DATA* ed, _In_ UINT16 edsize,
2716 _In_ BOOL unique, _In_opt_ _When_(return >= 0, __drv_aliasesMem) UINT32* csum, _In_ LIST_ENTRY* rollback) {
2717 extent* ext;
2718 LIST_ENTRY* le;
2719
2720 ext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + edsize, ALLOC_TAG);
2721 if (!ext) {
2722 ERR("out of memory\n");
2723 return STATUS_INSUFFICIENT_RESOURCES;
2724 }
2725
2726 ext->offset = offset;
2727 ext->datalen = edsize;
2728 ext->unique = unique;
2729 ext->ignore = FALSE;
2730 ext->inserted = TRUE;
2731 ext->csum = csum;
2732
2733 RtlCopyMemory(&ext->extent_data, ed, edsize);
2734
2735 le = fcb->extents.Flink;
2736 while (le != &fcb->extents) {
2737 extent* oldext = CONTAINING_RECORD(le, extent, list_entry);
2738
2739 if (oldext->offset >= offset) {
2740 InsertHeadList(le->Blink, &ext->list_entry);
2741 goto end;
2742 }
2743
2744 le = le->Flink;
2745 }
2746
2747 InsertTailList(&fcb->extents, &ext->list_entry);
2748
2749 end:
2750 add_insert_extent_rollback(rollback, fcb, ext);
2751
2752 return STATUS_SUCCESS;
2753 }
2754 #ifdef _MSC_VER
2755 #pragma warning(pop)
2756 #endif
2757
2758 static void remove_fcb_extent(fcb* fcb, extent* ext, LIST_ENTRY* rollback) {
2759 if (!ext->ignore) {
2760 rollback_extent* re;
2761
2762 ext->ignore = TRUE;
2763
2764 re = ExAllocatePoolWithTag(NonPagedPool, sizeof(rollback_extent), ALLOC_TAG);
2765 if (!re) {
2766 ERR("out of memory\n");
2767 return;
2768 }
2769
2770 re->fcb = fcb;
2771 re->ext = ext;
2772
2773 add_rollback(rollback, ROLLBACK_DELETE_EXTENT, re);
2774 }
2775 }
2776
2777 NTSTATUS calc_csum(_In_ device_extension* Vcb, _In_reads_bytes_(sectors*Vcb->superblock.sector_size) UINT8* data,
2778 _In_ UINT32 sectors, _Out_writes_bytes_(sectors*sizeof(UINT32)) UINT32* csum) {
2779 NTSTATUS Status;
2780 calc_job* cj;
2781
2782 // From experimenting, it seems that 40 sectors is roughly the crossover
2783 // point where offloading the crc32 calculation becomes worth it.
2784
2785 if (sectors < 40 || KeQueryActiveProcessorCount(NULL) < 2) {
2786 ULONG j;
2787
2788 for (j = 0; j < sectors; j++) {
2789 csum[j] = ~calc_crc32c(0xffffffff, data + (j * Vcb->superblock.sector_size), Vcb->superblock.sector_size);
2790 }
2791
2792 return STATUS_SUCCESS;
2793 }
2794
2795 Status = add_calc_job(Vcb, data, sectors, csum, &cj);
2796 if (!NT_SUCCESS(Status)) {
2797 ERR("add_calc_job returned %08x\n", Status);
2798 return Status;
2799 }
2800
2801 KeWaitForSingleObject(&cj->event, Executive, KernelMode, FALSE, NULL);
2802 free_calc_job(cj);
2803
2804 return STATUS_SUCCESS;
2805 }
2806
2807 _Requires_lock_held_(c->lock)
2808 _When_(return != 0, _Releases_lock_(c->lock))
2809 BOOL insert_extent_chunk(_In_ device_extension* Vcb, _In_ fcb* fcb, _In_ chunk* c, _In_ UINT64 start_data, _In_ UINT64 length, _In_ BOOL prealloc, _In_opt_ void* data,
2810 _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback, _In_ UINT8 compression, _In_ UINT64 decoded_size, _In_ BOOL file_write, _In_ UINT64 irp_offset) {
2811 UINT64 address;
2812 NTSTATUS Status;
2813 EXTENT_DATA* ed;
2814 EXTENT_DATA2* ed2;
2815 UINT16 edsize = (UINT16)(offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2));
2816 UINT32* csum = NULL;
2817
2818 TRACE("(%p, (%llx, %llx), %llx, %llx, %llx, %u, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, c->offset, start_data, length, prealloc, data, rollback);
2819
2820 if (!find_data_address_in_chunk(Vcb, c, length, &address))
2821 return FALSE;
2822
2823 // add extent data to inode
2824 ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
2825 if (!ed) {
2826 ERR("out of memory\n");
2827 return FALSE;
2828 }
2829
2830 ed->generation = Vcb->superblock.generation;
2831 ed->decoded_size = decoded_size;
2832 ed->compression = compression;
2833 ed->encryption = BTRFS_ENCRYPTION_NONE;
2834 ed->encoding = BTRFS_ENCODING_NONE;
2835 ed->type = prealloc ? EXTENT_TYPE_PREALLOC : EXTENT_TYPE_REGULAR;
2836
2837 ed2 = (EXTENT_DATA2*)ed->data;
2838 ed2->address = address;
2839 ed2->size = length;
2840 ed2->offset = 0;
2841 ed2->num_bytes = decoded_size;
2842
2843 if (!prealloc && data && !(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
2844 ULONG sl = (ULONG)(length / Vcb->superblock.sector_size);
2845
2846 csum = ExAllocatePoolWithTag(PagedPool, sl * sizeof(UINT32), ALLOC_TAG);
2847 if (!csum) {
2848 ERR("out of memory\n");
2849 ExFreePool(ed);
2850 return FALSE;
2851 }
2852
2853 Status = calc_csum(Vcb, data, sl, csum);
2854 if (!NT_SUCCESS(Status)) {
2855 ERR("calc_csum returned %08x\n", Status);
2856 ExFreePool(csum);
2857 ExFreePool(ed);
2858 return FALSE;
2859 }
2860 }
2861
2862 Status = add_extent_to_fcb(fcb, start_data, ed, edsize, TRUE, csum, rollback);
2863 if (!NT_SUCCESS(Status)) {
2864 ERR("add_extent_to_fcb returned %08x\n", Status);
2865 if (csum) ExFreePool(csum);
2866 ExFreePool(ed);
2867 return FALSE;
2868 }
2869
2870 ExFreePool(ed);
2871
2872 c->used += length;
2873 space_list_subtract(c, FALSE, address, length, rollback);
2874
2875 fcb->inode_item.st_blocks += decoded_size;
2876
2877 fcb->extents_changed = TRUE;
2878 fcb->inode_item_changed = TRUE;
2879 mark_fcb_dirty(fcb);
2880
2881 ExAcquireResourceExclusiveLite(&c->changed_extents_lock, TRUE);
2882
2883 add_changed_extent_ref(c, address, length, fcb->subvol->id, fcb->inode, start_data, 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM);
2884
2885 ExReleaseResourceLite(&c->changed_extents_lock);
2886
2887 ExReleaseResourceLite(&c->lock);
2888
2889 if (data) {
2890 Status = write_data_complete(Vcb, address, data, (UINT32)length, Irp, NULL, file_write, irp_offset,
2891 fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE ? HighPagePriority : NormalPagePriority);
2892 if (!NT_SUCCESS(Status))
2893 ERR("write_data_complete returned %08x\n", Status);
2894 }
2895
2896 return TRUE;
2897 }
2898
2899 static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data,
2900 PIRP Irp, UINT64* written, BOOL file_write, UINT64 irp_offset, LIST_ENTRY* rollback) {
2901 BOOL success = FALSE;
2902 EXTENT_DATA* ed;
2903 EXTENT_DATA2* ed2;
2904 chunk* c;
2905 LIST_ENTRY* le;
2906 extent* ext = NULL;
2907
2908 le = fcb->extents.Flink;
2909
2910 while (le != &fcb->extents) {
2911 extent* nextext = CONTAINING_RECORD(le, extent, list_entry);
2912
2913 if (!nextext->ignore) {
2914 if (nextext->offset == start_data) {
2915 ext = nextext;
2916 break;
2917 } else if (nextext->offset > start_data)
2918 break;
2919
2920 ext = nextext;
2921 }
2922
2923 le = le->Flink;
2924 }
2925
2926 if (!ext)
2927 return FALSE;
2928
2929 ed = &ext->extent_data;
2930
2931 if (ed->type != EXTENT_TYPE_REGULAR && ed->type != EXTENT_TYPE_PREALLOC) {
2932 TRACE("not extending extent which is not regular or prealloc\n");
2933 return FALSE;
2934 }
2935
2936 ed2 = (EXTENT_DATA2*)ed->data;
2937
2938 if (ext->offset + ed2->num_bytes != start_data) {
2939 TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", ext->offset, ed2->num_bytes, start_data);
2940 return FALSE;
2941 }
2942
2943 c = get_chunk_from_address(Vcb, ed2->address);
2944
2945 if (c->reloc || c->readonly || c->chunk_item->type != Vcb->data_flags)
2946 return FALSE;
2947
2948 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
2949
2950 if (length > c->chunk_item->size - c->used) {
2951 ExReleaseResourceLite(&c->lock);
2952 return FALSE;
2953 }
2954
2955 if (!c->cache_loaded) {
2956 NTSTATUS Status = load_cache_chunk(Vcb, c, NULL);
2957
2958 if (!NT_SUCCESS(Status)) {
2959 ERR("load_cache_chunk returned %08x\n", Status);
2960 ExReleaseResourceLite(&c->lock);
2961 return FALSE;
2962 }
2963 }
2964
2965 le = c->space.Flink;
2966 while (le != &c->space) {
2967 space* s = CONTAINING_RECORD(le, space, list_entry);
2968
2969 if (s->address == ed2->address + ed2->size) {
2970 UINT64 newlen = min(min(s->size, length), MAX_EXTENT_SIZE);
2971
2972 success = insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen, file_write, irp_offset);
2973
2974 if (success)
2975 *written += newlen;
2976 else
2977 ExReleaseResourceLite(&c->lock);
2978
2979 return success;
2980 } else if (s->address > ed2->address + ed2->size)
2981 break;
2982
2983 le = le->Flink;
2984 }
2985
2986 ExReleaseResourceLite(&c->lock);
2987
2988 return FALSE;
2989 }
2990
2991 static NTSTATUS insert_chunk_fragmented(fcb* fcb, UINT64 start, UINT64 length, UINT8* data, BOOL prealloc, LIST_ENTRY* rollback) {
2992 LIST_ENTRY* le;
2993 UINT64 flags = fcb->Vcb->data_flags;
2994 BOOL page_file = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE;
2995 NTSTATUS Status;
2996 chunk* c;
2997
2998 ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
2999
3000 // first create as many chunks as we can
3001 do {
3002 Status = alloc_chunk(fcb->Vcb, flags, &c, FALSE);
3003 } while (NT_SUCCESS(Status));
3004
3005 if (Status != STATUS_DISK_FULL) {
3006 ERR("alloc_chunk returned %08x\n", Status);
3007 ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
3008 return Status;
3009 }
3010
3011 le = fcb->Vcb->chunks.Flink;
3012 while (le != &fcb->Vcb->chunks) {
3013 c = CONTAINING_RECORD(le, chunk, list_entry);
3014
3015 if (!c->readonly && !c->reloc) {
3016 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
3017
3018 if (c->chunk_item->type == flags) {
3019 while (!IsListEmpty(&c->space_size) && length > 0) {
3020 space* s = CONTAINING_RECORD(c->space_size.Flink, space, list_entry_size);
3021 UINT64 extlen = min(length, s->size);
3022
3023 if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, prealloc && !page_file, data, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen, FALSE, 0)) {
3024 start += extlen;
3025 length -= extlen;
3026 if (data) data += extlen;
3027
3028 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
3029 }
3030 }
3031 }
3032
3033 ExReleaseResourceLite(&c->lock);
3034
3035 if (length == 0)
3036 break;
3037 }
3038
3039 le = le->Flink;
3040 }
3041
3042 ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
3043
3044 return length == 0 ? STATUS_SUCCESS : STATUS_DISK_FULL;
3045 }
3046
3047 static NTSTATUS insert_prealloc_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback) {
3048 LIST_ENTRY* le;
3049 chunk* c;
3050 UINT64 flags;
3051 NTSTATUS Status;
3052 BOOL page_file = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE;
3053
3054 flags = fcb->Vcb->data_flags;
3055
3056 do {
3057 UINT64 extlen = min(MAX_EXTENT_SIZE, length);
3058
3059 ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE);
3060
3061 le = fcb->Vcb->chunks.Flink;
3062 while (le != &fcb->Vcb->chunks) {
3063 c = CONTAINING_RECORD(le, chunk, list_entry);
3064
3065 if (!c->readonly && !c->reloc) {
3066 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
3067
3068 if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= extlen) {
3069 if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen, FALSE, 0)) {
3070 ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
3071 goto cont;
3072 }
3073 }
3074
3075 ExReleaseResourceLite(&c->lock);
3076 }
3077
3078 le = le->Flink;
3079 }
3080
3081 ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
3082
3083 ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE);
3084
3085 Status = alloc_chunk(fcb->Vcb, flags, &c, FALSE);
3086
3087 ExReleaseResourceLite(&fcb->Vcb->chunk_lock);
3088
3089 if (!NT_SUCCESS(Status)) {
3090 ERR("alloc_chunk returned %08x\n", Status);
3091 goto end;
3092 }
3093
3094 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
3095
3096 if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= extlen) {
3097 if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen, FALSE, 0))
3098 goto cont;
3099 }
3100
3101 ExReleaseResourceLite(&c->lock);
3102
3103 Status = insert_chunk_fragmented(fcb, start, length, NULL, TRUE, rollback);
3104 if (!NT_SUCCESS(Status))
3105 ERR("insert_chunk_fragmented returned %08x\n", Status);
3106
3107 goto end;
3108
3109 cont:
3110 length -= extlen;
3111 start += extlen;
3112 } while (length > 0);
3113
3114 Status = STATUS_SUCCESS;
3115
3116 end:
3117 return Status;
3118 }
3119
3120 static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data,
3121 PIRP Irp, BOOL file_write, UINT64 irp_offset, LIST_ENTRY* rollback) {
3122 NTSTATUS Status;
3123 LIST_ENTRY* le;
3124 chunk* c;
3125 UINT64 flags, orig_length = length, written = 0;
3126
3127 TRACE("(%p, (%llx, %llx), %llx, %llx, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, length, data);
3128
3129 if (start_data > 0) {
3130 try_extend_data(Vcb, fcb, start_data, length, data, Irp, &written, file_write, irp_offset, rollback);
3131
3132 if (written == length)
3133 return STATUS_SUCCESS;
3134 else if (written > 0) {
3135 start_data += written;
3136 irp_offset += written;
3137 length -= written;
3138 data = &((UINT8*)data)[written];
3139 }
3140 }
3141
3142 flags = Vcb->data_flags;
3143
3144 while (written < orig_length) {
3145 UINT64 newlen = min(length, MAX_EXTENT_SIZE);
3146 BOOL done = FALSE;
3147
3148 // Rather than necessarily writing the whole extent at once, we deal with it in blocks of 128 MB.
3149 // First, see if we can write the extent part to an existing chunk.
3150
3151 ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE);
3152
3153 le = Vcb->chunks.Flink;
3154 while (le != &Vcb->chunks) {
3155 c = CONTAINING_RECORD(le, chunk, list_entry);
3156
3157 if (!c->readonly && !c->reloc) {
3158 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
3159
3160 if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen &&
3161 insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen, file_write, irp_offset)) {
3162 written += newlen;
3163
3164 if (written == orig_length) {
3165 ExReleaseResourceLite(&Vcb->chunk_lock);
3166 return STATUS_SUCCESS;
3167 } else {
3168 done = TRUE;
3169 start_data += newlen;
3170 irp_offset += newlen;
3171 length -= newlen;
3172 data = &((UINT8*)data)[newlen];
3173 break;
3174 }
3175 } else
3176 ExReleaseResourceLite(&c->lock);
3177 }
3178
3179 le = le->Flink;
3180 }
3181
3182 ExReleaseResourceLite(&Vcb->chunk_lock);
3183
3184 if (done) continue;
3185
3186 // Otherwise, see if we can put it in a new chunk.
3187
3188 ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, TRUE);
3189
3190 Status = alloc_chunk(Vcb, flags, &c, FALSE);
3191
3192 ExReleaseResourceLite(&Vcb->chunk_lock);
3193
3194 if (!NT_SUCCESS(Status)) {
3195 ERR("alloc_chunk returned %08x\n", Status);
3196 return Status;
3197 }
3198
3199 if (c) {
3200 ExAcquireResourceExclusiveLite(&c->lock, TRUE);
3201
3202 if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen &&
3203 insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen, file_write, irp_offset)) {
3204 written += newlen;
3205
3206 if (written == orig_length)
3207 return STATUS_SUCCESS;
3208 else {
3209 done = TRUE;
3210 start_data += newlen;
3211 irp_offset += newlen;
3212 length -= newlen;
3213 data = &((UINT8*)data)[newlen];
3214 }
3215 } else
3216 ExReleaseResourceLite(&c->lock);
3217 }
3218
3219 if (!done) {
3220 Status = insert_chunk_fragmented(fcb, start_data, length, data, FALSE, rollback);
3221 if (!NT_SUCCESS(Status))
3222 ERR("insert_chunk_fragmented returned %08x\n", Status);
3223
3224 return Status;
3225 }
3226 }
3227
3228 return STATUS_DISK_FULL;
3229 }
3230
3231 NTSTATUS truncate_file(fcb* fcb, UINT64 end, PIRP Irp, LIST_ENTRY* rollback) {
3232 NTSTATUS Status;
3233
3234 // FIXME - convert into inline extent if short enough
3235
3236 if (end > 0 && fcb_is_inline(fcb)) {
3237 UINT8* buf;
3238 BOOL make_inline = end <= fcb->Vcb->options.max_inline;
3239
3240 buf = ExAllocatePoolWithTag(PagedPool, (ULONG)(make_inline ? (offsetof(EXTENT_DATA, data[0]) + end) : sector_align(end, fcb->Vcb->superblock.sector_size)), ALLOC_TAG);
3241 if (!buf) {
3242 ERR("out of memory\n");
3243 return STATUS_INSUFFICIENT_RESOURCES;
3244 }
3245
3246 Status = read_file(fcb, make_inline ? (buf + offsetof(EXTENT_DATA, data[0])) : buf, 0, end, NULL, Irp);
3247 if (!NT_SUCCESS(Status)) {
3248 ERR("read_file returned %08x\n", Status);
3249 ExFreePool(buf);
3250 return Status;
3251 }
3252
3253 Status = excise_extents(fcb->Vcb, fcb, 0, fcb->inode_item.st_size, Irp, rollback);
3254 if (!NT_SUCCESS(Status)) {
3255 ERR("excise_extents returned %08x\n", Status);
3256 ExFreePool(buf);
3257 return Status;
3258 }
3259
3260 if (!make_inline) {
3261 RtlZeroMemory(buf + end, (ULONG)(sector_align(end, fcb->Vcb->superblock.sector_size) - end));
3262
3263 Status = do_write_file(fcb, 0, sector_align(end, fcb->Vcb->superblock.sector_size), buf, Irp, FALSE, 0, rollback);
3264 if (!NT_SUCCESS(Status)) {
3265 ERR("do_write_file returned %08x\n", Status);
3266 ExFreePool(buf);
3267 return Status;
3268 }
3269 } else {
3270 EXTENT_DATA* ed = (EXTENT_DATA*)buf;
3271
3272 ed->generation = fcb->Vcb->superblock.generation;
3273 ed->decoded_size = end;
3274 ed->compression = BTRFS_COMPRESSION_NONE;
3275 ed->encryption = BTRFS_ENCRYPTION_NONE;
3276 ed->encoding = BTRFS_ENCODING_NONE;
3277 ed->type = EXTENT_TYPE_INLINE;
3278
3279 Status = add_extent_to_fcb(fcb, 0, ed, (UINT16)(offsetof(EXTENT_DATA, data[0]) + end), FALSE, NULL, rollback);
3280 if (!NT_SUCCESS(Status)) {
3281 ERR("add_extent_to_fcb returned %08x\n", Status);
3282 ExFreePool(buf);
3283 return Status;
3284 }
3285
3286 fcb->inode_item.st_blocks += end;
3287 }
3288
3289 ExFreePool(buf);
3290 return STATUS_SUCCESS;
3291 }
3292
3293 Status = excise_extents(fcb->Vcb, fcb, sector_align(end, fcb->Vcb->superblock.sector_size),
3294 sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), Irp, rollback);
3295 if (!NT_SUCCESS(Status)) {
3296 ERR("excise_extents returned %08x\n", Status);
3297 return Status;
3298 }
3299
3300 fcb->inode_item.st_size = end;
3301 fcb->inode_item_changed = TRUE;
3302 TRACE("setting st_size to %llx\n", end);
3303
3304 fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size);
3305 fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size;
3306 fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size;
3307 // FIXME - inform cache manager of this
3308
3309 TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart);
3310
3311 return STATUS_SUCCESS;
3312 }
3313
3314 NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIRP Irp, LIST_ENTRY* rollback) {
3315 UINT64 oldalloc, newalloc;
3316 BOOL cur_inline;
3317 NTSTATUS Status;
3318
3319 TRACE("(%p, %p, %x, %u)\n", fcb, fileref, end, prealloc);
3320
3321 if (fcb->ads) {
3322 if (end > 0xffff)
3323 return STATUS_DISK_FULL;
3324
3325 return stream_set_end_of_file_information(fcb->Vcb, (UINT16)end, fcb, fileref, FALSE);
3326 } else {
3327 extent* ext = NULL;
3328 LIST_ENTRY* le;
3329
3330 le = fcb->extents.Blink;
3331 while (le != &fcb->extents) {
3332 extent* ext2 = CONTAINING_RECORD(le, extent, list_entry);
3333
3334 if (!ext2->ignore) {
3335 ext = ext2;
3336 break;
3337 }
3338
3339 le = le->Blink;
3340 }
3341
3342 oldalloc = 0;
3343 if (ext) {
3344 EXTENT_DATA* ed = &ext->extent_data;
3345 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
3346
3347 oldalloc = ext->offset + (ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes);
3348 cur_inline = ed->type == EXTENT_TYPE_INLINE;
3349
3350 if (cur_inline && end > fcb->Vcb->options.max_inline) {
3351 UINT64 origlength, length;
3352 UINT8* data;
3353
3354 TRACE("giving inline file proper extents\n");
3355
3356 origlength = ed->decoded_size;
3357
3358 cur_inline = FALSE;
3359
3360 length = sector_align(origlength, fcb->Vcb->superblock.sector_size);
3361
3362 data = ExAllocatePoolWithTag(PagedPool, (ULONG)length, ALLOC_TAG);
3363 if (!data) {
3364 ERR("could not allocate %llx bytes for data\n", length);
3365 return STATUS_INSUFFICIENT_RESOURCES;
3366 }
3367
3368 Status = read_file(fcb, data, 0, origlength, NULL, Irp);
3369 if (!NT_SUCCESS(Status)) {
3370 ERR("read_file returned %08x\n", Status);
3371 ExFreePool(data);
3372 return Status;
3373 }
3374
3375 RtlZeroMemory(data + origlength, (ULONG)(length - origlength));
3376
3377 Status = excise_extents(fcb->Vcb, fcb, 0, fcb->inode_item.st_size, Irp, rollback);
3378 if (!NT_SUCCESS(Status)) {
3379 ERR("excise_extents returned %08x\n", Status);
3380 ExFreePool(data);
3381 return Status;
3382 }
3383
3384 Status = do_write_file(fcb, 0, length, data, Irp, FALSE, 0, rollback);
3385 if (!NT_SUCCESS(Status)) {
3386 ERR("do_write_file returned %08x\n", Status);
3387 ExFreePool(data);
3388 return Status;
3389 }
3390
3391 oldalloc = ext->offset + length;
3392
3393 ExFreePool(data);
3394 }
3395
3396 if (cur_inline) {
3397 UINT16 edsize;
3398
3399 if (end > oldalloc) {
3400 edsize = (UINT16)(offsetof(EXTENT_DATA, data[0]) + end - ext->offset);
3401 ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
3402
3403 if (!ed) {
3404 ERR("out of memory\n");
3405 return STATUS_INSUFFICIENT_RESOURCES;
3406 }
3407
3408 ed->generation = fcb->Vcb->superblock.generation;
3409 ed->decoded_size = end - ext->offset;
3410 ed->compression = BTRFS_COMPRESSION_NONE;
3411 ed->encryption = BTRFS_ENCRYPTION_NONE;
3412 ed->encoding = BTRFS_ENCODING_NONE;
3413 ed->type = EXTENT_TYPE_INLINE;
3414
3415 Status = read_file(fcb, ed->data, ext->offset, oldalloc, NULL, Irp);
3416 if (!NT_SUCCESS(Status)) {
3417 ERR("read_file returned %08x\n", Status);
3418 ExFreePool(ed);
3419 return Status;
3420 }
3421
3422 RtlZeroMemory(ed->data + oldalloc - ext->offset, (ULONG)(end - oldalloc));
3423
3424 remove_fcb_extent(fcb, ext, rollback);
3425
3426 Status = add_extent_to_fcb(fcb, ext->offset, ed, edsize, ext->unique, NULL, rollback);
3427 if (!NT_SUCCESS(Status)) {
3428 ERR("add_extent_to_fcb returned %08x\n", Status);
3429 ExFreePool(ed);
3430 return Status;
3431 }
3432
3433 ExFreePool(ed);
3434
3435 fcb->extents_changed = TRUE;
3436 mark_fcb_dirty(fcb);
3437 }
3438
3439 TRACE("extending inline file (oldalloc = %llx, end = %llx)\n", oldalloc, end);
3440
3441 fcb->inode_item.st_size = end;
3442 TRACE("setting st_size to %llx\n", end);
3443
3444 fcb->inode_item.st_blocks = end;
3445
3446 fcb->Header.AllocationSize.QuadPart = fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
3447 } else {
3448 newalloc = sector_align(end, fcb->Vcb->superblock.sector_size);
3449
3450 if (newalloc > oldalloc) {
3451 if (prealloc) {
3452 // FIXME - try and extend previous extent first
3453
3454 Status = insert_prealloc_extent(fcb, oldalloc, newalloc - oldalloc, rollback);
3455
3456 if (!NT_SUCCESS(Status)) {
3457 ERR("insert_prealloc_extent returned %08x\n", Status);
3458 return Status;
3459 }
3460 }
3461
3462 fcb->extents_changed = TRUE;
3463 }
3464
3465 fcb->inode_item.st_size = end;
3466 fcb->inode_item_changed = TRUE;
3467 mark_fcb_dirty(fcb);
3468
3469 TRACE("setting st_size to %llx\n", end);
3470
3471 TRACE("newalloc = %llx\n", newalloc);
3472
3473 fcb->Header.AllocationSize.QuadPart = newalloc;
3474 fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
3475 }
3476 } else {
3477 if (end > fcb->Vcb->options.max_inline) {
3478 newalloc = sector_align(end, fcb->Vcb->superblock.sector_size);
3479
3480 if (prealloc) {
3481 Status = insert_prealloc_extent(fcb, 0, newalloc, rollback);
3482
3483 if (!NT_SUCCESS(Status)) {
3484 ERR("insert_prealloc_extent returned %08x\n", Status);
3485 return Status;
3486 }
3487 }
3488
3489 fcb->extents_changed = TRUE;
3490 fcb->inode_item_changed = TRUE;
3491 mark_fcb_dirty(fcb);
3492
3493 fcb->inode_item.st_size = end;
3494 TRACE("setting st_size to %llx\n", end);
3495
3496 TRACE("newalloc = %llx\n", newalloc);
3497
3498 fcb->Header.AllocationSize.QuadPart = newalloc;
3499 fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
3500 } else {
3501 EXTENT_DATA* ed;
3502 UINT16 edsize;
3503
3504 edsize = (UINT16)(offsetof(EXTENT_DATA, data[0]) + end);
3505 ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG);
3506
3507 if (!ed) {
3508 ERR("out of memory\n");
3509 return STATUS_INSUFFICIENT_RESOURCES;
3510 }
3511
3512 ed->generation = fcb->Vcb->superblock.generation;
3513 ed->decoded_size = end;
3514 ed->compression = BTRFS_COMPRESSION_NONE;
3515 ed->encryption = BTRFS_ENCRYPTION_NONE;
3516 ed->encoding = BTRFS_ENCODING_NONE;
3517 ed->type = EXTENT_TYPE_INLINE;
3518
3519 RtlZeroMemory(ed->data, (ULONG)end);
3520
3521 Status = add_extent_to_fcb(fcb, 0, ed, edsize, FALSE, NULL, rollback);
3522 if (!NT_SUCCESS(Status)) {
3523 ERR("add_extent_to_fcb returned %08x\n", Status);
3524 ExFreePool(ed);
3525 return Status;
3526 }
3527
3528 ExFreePool(ed);
3529
3530 fcb->extents_changed = TRUE;
3531 fcb->inode_item_changed = TRUE;
3532 mark_fcb_dirty(fcb);
3533
3534 fcb->inode_item.st_size = end;
3535 TRACE("setting st_size to %llx\n", end);
3536
3537 fcb->inode_item.st_blocks = end;
3538
3539 fcb->Header.AllocationSize.QuadPart = fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end;
3540 }
3541 }
3542 }
3543
3544 return STATUS_SUCCESS;
3545 }
3546
3547 static NTSTATUS do_write_file_prealloc(fcb* fcb, extent* ext, UINT64 start_data, UINT64 end_data, void* data, UINT64* written,
3548 PIRP Irp, BOOL file_write, UINT64 irp_offset, ULONG priority, LIST_ENTRY* rollback) {
3549 EXTENT_DATA* ed = &ext->extent_data;
3550 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data;
3551 NTSTATUS Status;
3552 chunk* c = NULL;
3553
3554 if (start_data <= ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace all
3555 extent* newext;
3556
3557 newext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3558 if (!newext) {
3559 ERR("out of memory\n");
3560 return STATUS_INSUFFICIENT_RESOURCES;
3561 }
3562
3563 RtlCopyMemory(&newext->extent_data, &ext->extent_data, ext->datalen);
3564
3565 newext->extent_data.type = EXTENT_TYPE_REGULAR;
3566
3567 Status = write_data_complete(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, (UINT32)ed2->num_bytes, Irp,
3568 NULL, file_write, irp_offset + ext->offset - start_data, priority);
3569 if (!NT_SUCCESS(Status)) {
3570 ERR("write_data_complete returned %08x\n", Status);
3571 return Status;
3572 }
3573
3574 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3575 ULONG sl = (ULONG)(ed2->num_bytes / fcb->Vcb->superblock.sector_size);
3576 UINT32* csum = ExAllocatePoolWithTag(PagedPool, sl * sizeof(UINT32), ALLOC_TAG);
3577
3578 if (!csum) {
3579 ERR("out of memory\n");
3580 ExFreePool(newext);
3581 return STATUS_INSUFFICIENT_RESOURCES;
3582 }
3583
3584 Status = calc_csum(fcb->Vcb, (UINT8*)data + ext->offset - start_data, sl, csum);
3585 if (!NT_SUCCESS(Status)) {
3586 ERR("calc_csum returned %08x\n", Status);
3587 ExFreePool(csum);
3588 ExFreePool(newext);
3589 return Status;
3590 }
3591
3592 newext->csum = csum;
3593 } else
3594 newext->csum = NULL;
3595
3596 *written = ed2->num_bytes;
3597
3598 newext->offset = ext->offset;
3599 newext->datalen = ext->datalen;
3600 newext->unique = ext->unique;
3601 newext->ignore = FALSE;
3602 newext->inserted = TRUE;
3603 InsertHeadList(&ext->list_entry, &newext->list_entry);
3604
3605 add_insert_extent_rollback(rollback, fcb, newext);
3606
3607 remove_fcb_extent(fcb, ext, rollback);
3608
3609 c = get_chunk_from_address(fcb->Vcb, ed2->address);
3610 } else if (start_data <= ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace beginning
3611 EXTENT_DATA2* ned2;
3612 extent *newext1, *newext2;
3613
3614 newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3615 if (!newext1) {
3616 ERR("out of memory\n");
3617 return STATUS_INSUFFICIENT_RESOURCES;
3618 }
3619
3620 newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3621 if (!newext2) {
3622 ERR("out of memory\n");
3623 ExFreePool(newext1);
3624 return STATUS_INSUFFICIENT_RESOURCES;
3625 }
3626
3627 RtlCopyMemory(&newext1->extent_data, &ext->extent_data, ext->datalen);
3628 newext1->extent_data.type = EXTENT_TYPE_REGULAR;
3629 ned2 = (EXTENT_DATA2*)newext1->extent_data.data;
3630 ned2->num_bytes = end_data - ext->offset;
3631
3632 RtlCopyMemory(&newext2->extent_data, &ext->extent_data, ext->datalen);
3633 ned2 = (EXTENT_DATA2*)newext2->extent_data.data;
3634 ned2->offset += end_data - ext->offset;
3635 ned2->num_bytes -= end_data - ext->offset;
3636
3637 Status = write_data_complete(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, (UINT32)(end_data - ext->offset),
3638 Irp, NULL, file_write, irp_offset + ext->offset - start_data, priority);
3639 if (!NT_SUCCESS(Status)) {
3640 ERR("write_data_complete returned %08x\n", Status);
3641 return Status;
3642 }
3643
3644 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3645 ULONG sl = (ULONG)((end_data - ext->offset) / fcb->Vcb->superblock.sector_size);
3646 UINT32* csum = ExAllocatePoolWithTag(PagedPool, sl * sizeof(UINT32), ALLOC_TAG);
3647
3648 if (!csum) {
3649 ERR("out of memory\n");
3650 ExFreePool(newext1);
3651 ExFreePool(newext2);
3652 return STATUS_INSUFFICIENT_RESOURCES;
3653 }
3654
3655 Status = calc_csum(fcb->Vcb, (UINT8*)data + ext->offset - start_data, sl, csum);
3656 if (!NT_SUCCESS(Status)) {
3657 ERR("calc_csum returned %08x\n", Status);
3658 ExFreePool(newext1);
3659 ExFreePool(newext2);
3660 ExFreePool(csum);
3661 return Status;
3662 }
3663
3664 newext1->csum = csum;
3665 } else
3666 newext1->csum = NULL;
3667
3668 *written = end_data - ext->offset;
3669
3670 newext1->offset = ext->offset;
3671 newext1->datalen = ext->datalen;
3672 newext1->unique = ext->unique;
3673 newext1->ignore = FALSE;
3674 newext1->inserted = TRUE;
3675 InsertHeadList(&ext->list_entry, &newext1->list_entry);
3676
3677 add_insert_extent_rollback(rollback, fcb, newext1);
3678
3679 newext2->offset = end_data;
3680 newext2->datalen = ext->datalen;
3681 newext2->unique = ext->unique;
3682 newext2->ignore = FALSE;
3683 newext2->inserted = TRUE;
3684 newext2->csum = NULL;
3685 add_extent(fcb, &newext1->list_entry, newext2);
3686
3687 add_insert_extent_rollback(rollback, fcb, newext2);
3688
3689 c = get_chunk_from_address(fcb->Vcb, ed2->address);
3690
3691 if (!c)
3692 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
3693 else {
3694 Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
3695 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp);
3696
3697 if (!NT_SUCCESS(Status)) {
3698 ERR("update_changed_extent_ref returned %08x\n", Status);
3699 return Status;
3700 }
3701 }
3702
3703 remove_fcb_extent(fcb, ext, rollback);
3704 } else if (start_data > ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace end
3705 EXTENT_DATA2* ned2;
3706 extent *newext1, *newext2;
3707
3708 newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3709 if (!newext1) {
3710 ERR("out of memory\n");
3711 return STATUS_INSUFFICIENT_RESOURCES;
3712 }
3713
3714 newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3715 if (!newext2) {
3716 ERR("out of memory\n");
3717 ExFreePool(newext1);
3718 return STATUS_INSUFFICIENT_RESOURCES;
3719 }
3720
3721 RtlCopyMemory(&newext1->extent_data, &ext->extent_data, ext->datalen);
3722
3723 ned2 = (EXTENT_DATA2*)newext1->extent_data.data;
3724 ned2->num_bytes = start_data - ext->offset;
3725
3726 RtlCopyMemory(&newext2->extent_data, &ext->extent_data, ext->datalen);
3727
3728 newext2->extent_data.type = EXTENT_TYPE_REGULAR;
3729 ned2 = (EXTENT_DATA2*)newext2->extent_data.data;
3730 ned2->offset += start_data - ext->offset;
3731 ned2->num_bytes = ext->offset + ed2->num_bytes - start_data;
3732
3733 Status = write_data_complete(fcb->Vcb, ed2->address + ned2->offset, data, (UINT32)ned2->num_bytes, Irp, NULL, file_write, irp_offset, priority);
3734 if (!NT_SUCCESS(Status)) {
3735 ERR("write_data_complete returned %08x\n", Status);
3736 return Status;
3737 }
3738
3739 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3740 ULONG sl = (ULONG)(ned2->num_bytes / fcb->Vcb->superblock.sector_size);
3741 UINT32* csum = ExAllocatePoolWithTag(PagedPool, sl * sizeof(UINT32), ALLOC_TAG);
3742
3743 if (!csum) {
3744 ERR("out of memory\n");
3745 ExFreePool(newext1);
3746 ExFreePool(newext2);
3747 return STATUS_INSUFFICIENT_RESOURCES;
3748 }
3749
3750 Status = calc_csum(fcb->Vcb, data, sl, csum);
3751 if (!NT_SUCCESS(Status)) {
3752 ERR("calc_csum returned %08x\n", Status);
3753 ExFreePool(newext1);
3754 ExFreePool(newext2);
3755 ExFreePool(csum);
3756 return Status;
3757 }
3758
3759 newext2->csum = csum;
3760 } else
3761 newext2->csum = NULL;
3762
3763 *written = ned2->num_bytes;
3764
3765 newext1->offset = ext->offset;
3766 newext1->datalen = ext->datalen;
3767 newext1->unique = ext->unique;
3768 newext1->ignore = FALSE;
3769 newext1->inserted = TRUE;
3770 newext1->csum = NULL;
3771 InsertHeadList(&ext->list_entry, &newext1->list_entry);
3772
3773 add_insert_extent_rollback(rollback, fcb, newext1);
3774
3775 newext2->offset = start_data;
3776 newext2->datalen = ext->datalen;
3777 newext2->unique = ext->unique;
3778 newext2->ignore = FALSE;
3779 newext2->inserted = TRUE;
3780 add_extent(fcb, &newext1->list_entry, newext2);
3781
3782 add_insert_extent_rollback(rollback, fcb, newext2);
3783
3784 c = get_chunk_from_address(fcb->Vcb, ed2->address);
3785
3786 if (!c)
3787 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
3788 else {
3789 Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1,
3790 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp);
3791
3792 if (!NT_SUCCESS(Status)) {
3793 ERR("update_changed_extent_ref returned %08x\n", Status);
3794 return Status;
3795 }
3796 }
3797
3798 remove_fcb_extent(fcb, ext, rollback);
3799 } else if (start_data > ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace middle
3800 EXTENT_DATA2* ned2;
3801 extent *newext1, *newext2, *newext3;
3802
3803 newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3804 if (!newext1) {
3805 ERR("out of memory\n");
3806 return STATUS_INSUFFICIENT_RESOURCES;
3807 }
3808
3809 newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3810 if (!newext2) {
3811 ERR("out of memory\n");
3812 ExFreePool(newext1);
3813 return STATUS_INSUFFICIENT_RESOURCES;
3814 }
3815
3816 newext3 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG);
3817 if (!newext3) {
3818 ERR("out of memory\n");
3819 ExFreePool(newext1);
3820 ExFreePool(newext2);
3821 return STATUS_INSUFFICIENT_RESOURCES;
3822 }
3823
3824 RtlCopyMemory(&newext1->extent_data, &ext->extent_data, ext->datalen);
3825 RtlCopyMemory(&newext2->extent_data, &ext->extent_data, ext->datalen);
3826 RtlCopyMemory(&newext3->extent_data, &ext->extent_data, ext->datalen);
3827
3828 ned2 = (EXTENT_DATA2*)newext1->extent_data.data;
3829 ned2->num_bytes = start_data - ext->offset;
3830
3831 newext2->extent_data.type = EXTENT_TYPE_REGULAR;
3832 ned2 = (EXTENT_DATA2*)newext2->extent_data.data;
3833 ned2->offset += start_data - ext->offset;
3834 ned2->num_bytes = end_data - start_data;
3835
3836 ned2 = (EXTENT_DATA2*)newext3->extent_data.data;
3837 ned2->offset += end_data - ext->offset;
3838 ned2->num_bytes -= end_data - ext->offset;
3839
3840 ned2 = (EXTENT_DATA2*)newext2->extent_data.data;
3841 Status = write_data_complete(fcb->Vcb, ed2->address + ned2->offset, data, (UINT32)(end_data - start_data), Irp, NULL, file_write, irp_offset, priority);
3842 if (!NT_SUCCESS(Status)) {
3843 ERR("write_data_complete returned %08x\n", Status);
3844 ExFreePool(newext1);
3845 ExFreePool(newext2);
3846 ExFreePool(newext3);
3847 return Status;
3848 }
3849
3850 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
3851 ULONG sl = (ULONG)((end_data - start_data) / fcb->Vcb->superblock.sector_size);
3852 UINT32* csum = ExAllocatePoolWithTag(PagedPool, sl * sizeof(UINT32), ALLOC_TAG);
3853
3854 if (!csum) {
3855 ERR("out of memory\n");
3856 ExFreePool(newext1);
3857 ExFreePool(newext2);
3858 ExFreePool(newext3);
3859 return STATUS_INSUFFICIENT_RESOURCES;
3860 }
3861
3862 Status = calc_csum(fcb->Vcb, data, sl, csum);
3863 if (!NT_SUCCESS(Status)) {
3864 ERR("calc_csum returned %08x\n", Status);
3865 ExFreePool(newext1);
3866 ExFreePool(newext2);
3867 ExFreePool(newext3);
3868 ExFreePool(csum);
3869 return Status;
3870 }
3871
3872 newext2->csum = csum;
3873 } else
3874 newext2->csum = NULL;
3875
3876 *written = end_data - start_data;
3877
3878 newext1->offset = ext->offset;
3879 newext1->datalen = ext->datalen;
3880 newext1->unique = ext->unique;
3881 newext1->ignore = FALSE;
3882 newext1->inserted = TRUE;
3883 newext1->csum = NULL;
3884 InsertHeadList(&ext->list_entry, &newext1->list_entry);
3885
3886 add_insert_extent_rollback(rollback, fcb, newext1);
3887
3888 newext2->offset = start_data;
3889 newext2->datalen = ext->datalen;
3890 newext2->unique = ext->unique;
3891 newext2->ignore = FALSE;
3892 newext2->inserted = TRUE;
3893 add_extent(fcb, &newext1->list_entry, newext2);
3894
3895 add_insert_extent_rollback(rollback, fcb, newext2);
3896
3897 newext3->offset = end_data;
3898 newext3->datalen = ext->datalen;
3899 newext3->unique = ext->unique;
3900 newext3->ignore = FALSE;
3901 newext3->inserted = TRUE;
3902 newext3->csum = NULL;
3903 add_extent(fcb, &newext2->list_entry, newext3);
3904
3905 add_insert_extent_rollback(rollback, fcb, newext3);
3906
3907 c = get_chunk_from_address(fcb->Vcb, ed2->address);
3908
3909 if (!c)
3910 ERR("get_chunk_from_address(%llx) failed\n", ed2->address);
3911 else {
3912 Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 2,
3913 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp);
3914
3915 if (!NT_SUCCESS(Status)) {
3916 ERR("update_changed_extent_ref returned %08x\n", Status);
3917 return Status;
3918 }
3919 }
3920
3921 remove_fcb_extent(fcb, ext, rollback);
3922 }
3923
3924 if (c)
3925 c->changed = TRUE;
3926
3927 return STATUS_SUCCESS;
3928 }
3929
3930 NTSTATUS do_write_file(fcb* fcb, UINT64 start, UINT64 end_data, void* data, PIRP Irp, BOOL file_write, UINT32 irp_offset, LIST_ENTRY* rollback) {
3931 NTSTATUS Status;
3932 LIST_ENTRY *le, *le2;
3933 UINT64 written = 0, length = end_data - start;
3934 UINT64 last_cow_start;
3935 ULONG priority = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE ? HighPagePriority : NormalPagePriority;
3936 #ifdef DEBUG_PARANOID
3937 UINT64 last_off;
3938 #endif
3939
3940 last_cow_start = 0;
3941
3942 le = fcb->extents.Flink;
3943 while (le != &fcb->extents) {
3944 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
3945
3946 le2 = le->Flink;
3947
3948 if (!ext->ignore) {
3949 EXTENT_DATA* ed = &ext->extent_data;
3950 EXTENT_DATA2* ed2 = ed->type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ed->data;
3951 UINT64 len;
3952
3953 len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
3954
3955 if (ext->offset + len <= start)
3956 goto nextitem;
3957
3958 if (ext->offset > start + written + length)
3959 break;
3960
3961 if ((fcb->inode_item.flags & BTRFS_INODE_NODATACOW || ed->type == EXTENT_TYPE_PREALLOC) && ext->unique && ed->compression == BTRFS_COMPRESSION_NONE) {
3962 if (max(last_cow_start, start + written) < ext->offset) {
3963 UINT64 start_write = max(last_cow_start, start + written);
3964
3965 Status = excise_extents(fcb->Vcb, fcb, start_write, ext->offset, Irp, rollback);
3966 if (!NT_SUCCESS(Status)) {
3967 ERR("excise_extents returned %08x\n", Status);
3968 return Status;
3969 }
3970
3971 Status = insert_extent(fcb->Vcb, fcb, start_write, ext->offset - start_write, (UINT8*)data + written, Irp, file_write, irp_offset + written, rollback);
3972 if (!NT_SUCCESS(Status)) {
3973 ERR("insert_extent returned %08x\n", Status);
3974 return Status;
3975 }
3976
3977 written += ext->offset - start_write;
3978 length -= ext->offset - start_write;
3979
3980 if (length == 0)
3981 break;
3982 }
3983
3984 if (ed->type == EXTENT_TYPE_REGULAR) {
3985 UINT64 writeaddr = ed2->address + ed2->offset + start + written - ext->offset;
3986 UINT64 write_len = min(len, length);
3987 chunk* c;
3988
3989 TRACE("doing non-COW write to %llx\n", writeaddr);
3990
3991 Status = write_data_complete(fcb->Vcb, writeaddr, (UINT8*)data + written, (UINT32)write_len, Irp, NULL, file_write, irp_offset + written, priority);
3992 if (!NT_SUCCESS(Status)) {
3993 ERR("write_data_complete returned %08x\n", Status);
3994 return Status;
3995 }
3996
3997 c = get_chunk_from_address(fcb->Vcb, writeaddr);
3998 if (c)
3999 c->changed = TRUE;
4000
4001 // This shouldn't ever get called - nocow files should always also be nosum.
4002 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
4003 calc_csum(fcb->Vcb, (UINT8*)data + written, (UINT32)(write_len / fcb->Vcb->superblock.sector_size),
4004 &ext->csum[(start + written - ext->offset) / fcb->Vcb->superblock.sector_size]);
4005
4006 ext->inserted = TRUE;
4007 }
4008
4009 written += write_len;
4010 length -= write_len;
4011
4012 if (length == 0)
4013 break;
4014 } else if (ed->type == EXTENT_TYPE_PREALLOC) {
4015 UINT64 write_len;
4016
4017 Status = do_write_file_prealloc(fcb, ext, start + written, end_data, (UINT8*)data + written, &write_len,
4018 Irp, file_write, irp_offset + written, priority, rollback);
4019 if (!NT_SUCCESS(Status)) {
4020 ERR("do_write_file_prealloc returned %08x\n", Status);
4021 return Status;
4022 }
4023
4024 written += write_len;
4025 length -= write_len;
4026
4027 if (length == 0)
4028 break;
4029 }
4030
4031 last_cow_start = ext->offset + len;
4032 }
4033 }
4034
4035 nextitem:
4036 le = le2;
4037 }
4038
4039 if (length > 0) {
4040 UINT64 start_write = max(last_cow_start, start + written);
4041
4042 Status = excise_extents(fcb->Vcb, fcb, start_write, end_data, Irp, rollback);
4043 if (!NT_SUCCESS(Status)) {
4044 ERR("excise_extents returned %08x\n", Status);
4045 return Status;
4046 }
4047
4048 Status = insert_extent(fcb->Vcb, fcb, start_write, end_data - start_write, (UINT8*)data + written, Irp, file_write, irp_offset + written, rollback);
4049 if (!NT_SUCCESS(Status)) {
4050 ERR("insert_extent returned %08x\n", Status);
4051 return Status;
4052 }
4053 }
4054
4055 #ifdef DEBUG_PARANOID
4056 last_off = 0xffffffffffffffff;
4057
4058 le = fcb->extents.Flink;
4059 while (le != &fcb->extents) {
4060 extent* ext = CONTAINING_RECORD(le, extent, list_entry);
4061
4062 if (!ext->ignore) {
4063 if (ext->offset == last_off) {
4064 ERR("offset %llx duplicated\n", ext->offset);
4065 int3;
4066 } else if (ext->offset < last_off && last_off != 0xffffffffffffffff) {
4067 ERR("offsets out of order\n");
4068 int3;
4069 }
4070
4071 last_off = ext->offset;
4072 }
4073
4074 le = le->Flink;
4075 }
4076 #endif
4077
4078 fcb->extents_changed = TRUE;
4079 mark_fcb_dirty(fcb);
4080
4081 return STATUS_SUCCESS;
4082 }
4083
4084 NTSTATUS write_compressed(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, PIRP Irp, LIST_ENTRY* rollback) {
4085 NTSTATUS Status;
4086 UINT64 i;
4087
4088 for (i = 0; i < sector_align(end_data - start_data, COMPRESSED_EXTENT_SIZE) / COMPRESSED_EXTENT_SIZE; i++) {
4089 UINT64 s2, e2;
4090 BOOL compressed;
4091
4092 s2 = start_data + (i * COMPRESSED_EXTENT_SIZE);
4093 e2 = min(s2 + COMPRESSED_EXTENT_SIZE, end_data);
4094
4095 Status = write_compressed_bit(fcb, s2, e2, (UINT8*)data + (i * COMPRESSED_EXTENT_SIZE), &compressed, Irp, rollback);
4096
4097 if (!NT_SUCCESS(Status)) {
4098 ERR("write_compressed_bit returned %08x\n", Status);
4099 return Status;
4100 }
4101
4102 // If the first 128 KB of a file is incompressible, we set the nocompress flag so we don't
4103 // bother with the rest of it.
4104 if (s2 == 0 && e2 == COMPRESSED_EXTENT_SIZE && !compressed && !fcb->Vcb->options.compress_force) {
4105 fcb->inode_item.flags |= BTRFS_INODE_NOCOMPRESS;
4106 fcb->inode_item_changed = TRUE;
4107 mark_fcb_dirty(fcb);
4108
4109 // write subsequent data non-compressed
4110 if (e2 < end_data) {
4111 Status = do_write_file(fcb, e2, end_data, (UINT8*)data + e2, Irp, FALSE, 0, rollback);
4112
4113 if (!NT_SUCCESS(Status)) {
4114 ERR("do_write_file returned %08x\n", Status);
4115 return Status;
4116 }
4117 }
4118
4119 return STATUS_SUCCESS;
4120 }
4121 }
4122
4123 return STATUS_SUCCESS;
4124 }
4125
4126 NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOLEAN paging_io, BOOLEAN no_cache,
4127 BOOLEAN wait, BOOLEAN deferred_write, BOOLEAN write_irp, LIST_ENTRY* rollback) {
4128 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4129 PFILE_OBJECT FileObject = IrpSp->FileObject;
4130 EXTENT_DATA* ed2;
4131 UINT64 off64, newlength, start_data, end_data;
4132 UINT32 bufhead;
4133 BOOL make_inline;
4134 UINT8* data;
4135 INODE_ITEM* origii;
4136 BOOL changed_length = FALSE;
4137 NTSTATUS Status;
4138 LARGE_INTEGER time;
4139 BTRFS_TIME now;
4140 fcb* fcb;
4141 ccb* ccb;
4142 file_ref* fileref;
4143 BOOL paging_lock = FALSE, fcb_lock = FALSE, tree_lock = FALSE, pagefile;
4144 ULONG filter = 0;
4145
4146 TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb, FileObject, offset.QuadPart, buf, *length, paging_io, no_cache);
4147
4148 if (*length == 0) {
4149 WARN("returning success for zero-length write\n");
4150 return STATUS_SUCCESS;
4151 }
4152
4153 if (!FileObject) {
4154 ERR("error - FileObject was NULL\n");
4155 return STATUS_ACCESS_DENIED;
4156 }
4157
4158 fcb = FileObject->FsContext;
4159 ccb = FileObject->FsContext2;
4160 fileref = ccb ? ccb->fileref : NULL;
4161
4162 if (!fcb->ads && fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK) {
4163 WARN("tried to write to something other than a file or symlink (inode %llx, type %u, %p, %p)\n", fcb->inode, fcb->type, &fcb->type, fcb);
4164 return STATUS_INVALID_DEVICE_REQUEST;
4165 }
4166
4167 if (offset.LowPart == FILE_WRITE_TO_END_OF_FILE && offset.HighPart == -1)
4168 offset = fcb->Header.FileSize;
4169
4170 off64 = offset.QuadPart;
4171
4172 TRACE("fcb->Header.Flags = %x\n", fcb->Header.Flags);
4173
4174 if (!no_cache && !CcCanIWrite(FileObject, *length, wait, deferred_write))
4175 return STATUS_PENDING;
4176
4177 if (!wait && no_cache)
4178 return STATUS_PENDING;
4179
4180 if (no_cache && !paging_io && FileObject->SectionObjectPointer->DataSectionObject) {
4181 IO_STATUS_BLOCK iosb;
4182
4183 ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE);
4184
4185 CcFlushCache(FileObject->SectionObjectPointer, &offset, *length, &iosb);
4186
4187 if (!NT_SUCCESS(iosb.Status)) {
4188 ExReleaseResourceLite(fcb->Header.PagingIoResource);
4189 ERR("CcFlushCache returned %08x\n", iosb.Status);
4190 return iosb.Status;
4191 }
4192
4193 paging_lock = TRUE;
4194
4195 CcPurgeCacheSection(FileObject->SectionObjectPointer, &offset, *length, FALSE);
4196 }
4197
4198 if (paging_io) {
4199 if (!ExAcquireResourceSharedLite(fcb->Header.PagingIoResource, wait)) {
4200 Status = STATUS_PENDING;
4201 goto end;
4202 } else
4203 paging_lock = TRUE;
4204 }
4205
4206 pagefile = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE && paging_io;
4207
4208 if (!pagefile && !ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) {
4209 if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) {
4210 Status = STATUS_PENDING;
4211 goto end;
4212 } else
4213 tree_lock = TRUE;
4214 }
4215
4216 if (no_cache) {
4217 if (pagefile) {
4218 if (!ExAcquireResourceSharedLite(fcb->Header.Resource, wait)) {
4219 Status = STATUS_PENDING;
4220 goto end;
4221 } else
4222 fcb_lock = TRUE;
4223 } else if (!ExIsResourceAcquiredExclusiveLite(fcb->Header.Resource)) {
4224 if (!ExAcquireResourceExclusiveLite(fcb->Header.Resource, wait)) {
4225 Status = STATUS_PENDING;
4226 goto end;
4227 } else
4228 fcb_lock = TRUE;
4229 }
4230 }
4231
4232 newlength = fcb->ads ? fcb->adsdata.Length : fcb->inode_item.st_size;
4233
4234 if (fcb->deleted)
4235 newlength = 0;
4236
4237 TRACE("newlength = %llx\n", newlength);
4238
4239 if (off64 + *length > newlength) {
4240 if (paging_io) {
4241 if (off64 >= newlength) {
4242 TRACE("paging IO tried to write beyond end of file (file size = %llx, offset = %llx, length = %x)\n", newlength, off64, *length);
4243 TRACE("filename %S\n", file_desc(FileObject));
4244 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
4245 fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart);
4246 Status = STATUS_SUCCESS;
4247 goto end;
4248 }
4249
4250 *length = (ULONG)(newlength - off64);
4251 } else {
4252 newlength = off64 + *length;
4253 changed_length = TRUE;
4254
4255 TRACE("extending length to %llx\n", newlength);
4256 }
4257 }
4258
4259 if (fcb->ads)
4260 make_inline = FALSE;
4261 else if (fcb->type == BTRFS_TYPE_SYMLINK)
4262 make_inline = newlength <= (Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - offsetof(EXTENT_DATA, data[0]));
4263 else
4264 make_inline = newlength <= fcb->Vcb->options.max_inline;
4265
4266 if (changed_length) {
4267 if (newlength > (UINT64)fcb->Header.AllocationSize.QuadPart) {
4268 if (!tree_lock) {
4269 // We need to acquire the tree lock if we don't have it already -
4270 // we can't give an inline file proper extents at the same time as we're
4271 // doing a flush.
4272 if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) {
4273 Status = STATUS_PENDING;
4274 goto end;
4275 } else
4276 tree_lock = TRUE;
4277 }
4278
4279 Status = extend_file(fcb, fileref, newlength, FALSE, Irp, rollback);
4280 if (!NT_SUCCESS(Status)) {
4281 ERR("extend_file returned %08x\n", Status);
4282 goto end;
4283 }
4284 } else if (!fcb->ads)
4285 fcb->inode_item.st_size = newlength;
4286
4287 fcb->Header.FileSize.QuadPart = newlength;
4288 fcb->Header.ValidDataLength.QuadPart = newlength;
4289
4290 TRACE("AllocationSize = %llx\n", fcb->Header.AllocationSize.QuadPart);
4291 TRACE("FileSize = %llx\n", fcb->Header.FileSize.QuadPart);
4292 TRACE("ValidDataLength = %llx\n", fcb->Header.ValidDataLength.QuadPart);
4293 }
4294
4295 if (!no_cache) {
4296 Status = STATUS_SUCCESS;
4297
4298 _SEH2_TRY {
4299 if (!FileObject->PrivateCacheMap || changed_length) {
4300 CC_FILE_SIZES ccfs;
4301
4302 ccfs.AllocationSize = fcb->Header.AllocationSize;
4303 ccfs.FileSize = fcb->Header.FileSize;
4304 ccfs.ValidDataLength = fcb->Header.ValidDataLength;
4305
4306 if (!FileObject->PrivateCacheMap)
4307 init_file_cache(FileObject, &ccfs);
4308
4309 CcSetFileSizes(FileObject, &ccfs);
4310 }
4311
4312 if (IrpSp->MinorFunction & IRP_MN_MDL) {
4313 CcPrepareMdlWrite(FileObject, &offset, *length, &Irp->MdlAddress, &Irp->IoStatus);
4314
4315 Status = Irp->IoStatus.Status;
4316 goto end;
4317 } else {
4318 if (fCcCopyWriteEx) {
4319 TRACE("CcCopyWriteEx(%p, %llx, %x, %u, %p, %p)\n", FileObject, off64, *length, wait, buf, Irp->Tail.Overlay.Thread);
4320 if (!fCcCopyWriteEx(FileObject, &offset, *length, wait, buf, Irp->Tail.Overlay.Thread)) {
4321 Status = STATUS_PENDING;
4322 goto end;
4323 }
4324 TRACE("CcCopyWriteEx finished\n");
4325 } else {
4326 TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject, off64, *length, wait, buf);
4327 if (!CcCopyWrite(FileObject, &offset, *length, wait, buf)) {
4328 Status = STATUS_PENDING;
4329 goto end;
4330 }
4331 TRACE("CcCopyWrite finished\n");
4332 }
4333 }
4334 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
4335 Status = _SEH2_GetExceptionCode();
4336 } _SEH2_END;
4337
4338 if (changed_length) {
4339 send_notification_fcb(fcb->ads ? fileref->parent : fileref, fcb->ads ? FILE_NOTIFY_CHANGE_STREAM_SIZE : FILE_NOTIFY_CHANGE_SIZE,
4340 fcb->ads ? FILE_ACTION_MODIFIED_STREAM : FILE_ACTION_MODIFIED, fcb->ads && fileref->dc ? &fileref->dc->name : NULL);
4341 }
4342
4343 goto end;
4344 }
4345
4346 if (fcb->ads) {
4347 if (changed_length) {
4348 char* data2;
4349
4350 if (newlength > fcb->adsmaxlen) {
4351 ERR("error - xattr too long (%llu > %u)\n", newlength, fcb->adsmaxlen);
4352 Status = STATUS_DISK_FULL;
4353 goto end;
4354 }
4355
4356 data2 = ExAllocatePoolWithTag(PagedPool, (ULONG)newlength, ALLOC_TAG);
4357 if (!data2) {
4358 ERR("out of memory\n");
4359 Status = STATUS_INSUFFICIENT_RESOURCES;
4360 goto end;
4361 }
4362
4363 if (fcb->adsdata.Buffer) {
4364 RtlCopyMemory(data2, fcb->adsdata.Buffer, fcb->adsdata.Length);
4365 ExFreePool(fcb->adsdata.Buffer);
4366 }
4367
4368 if (newlength > fcb->adsdata.Length)
4369 RtlZeroMemory(&data2[fcb->adsdata.Length], (ULONG)(newlength - fcb->adsdata.Length));
4370
4371
4372 fcb->adsdata.Buffer = data2;
4373 fcb->adsdata.Length = fcb->adsdata.MaximumLength = (USHORT)newlength;
4374
4375 fcb->Header.AllocationSize.QuadPart = newlength;
4376 fcb->Header.FileSize.QuadPart = newlength;
4377 fcb->Header.ValidDataLength.QuadPart = newlength;
4378 }
4379
4380 if (*length > 0)
4381 RtlCopyMemory(&fcb->adsdata.Buffer[off64], buf, *length);
4382
4383 fcb->Header.ValidDataLength.QuadPart = newlength;
4384
4385 mark_fcb_dirty(fcb);
4386
4387 if (fileref)
4388 mark_fileref_dirty(fileref);
4389 } else {
4390 BOOL compress = write_fcb_compressed(fcb), no_buf = FALSE;
4391
4392 if (make_inline) {
4393 start_data = 0;
4394 end_data = sector_align(newlength, fcb->Vcb->superblock.sector_size);
4395 bufhead = sizeof(EXTENT_DATA) - 1;
4396 } else if (compress) {
4397 start_data = off64 & ~(UINT64)(COMPRESSED_EXTENT_SIZE - 1);
4398 end_data = min(sector_align(off64 + *length, COMPRESSED_EXTENT_SIZE),
4399 sector_align(newlength, fcb->Vcb->superblock.sector_size));
4400 bufhead = 0;
4401 } else {
4402 start_data = off64 & ~(UINT64)(fcb->Vcb->superblock.sector_size - 1);
4403 end_data = sector_align(off64 + *length, fcb->Vcb->superblock.sector_size);
4404 bufhead = 0;
4405 }
4406
4407 if (fcb_is_inline(fcb))
4408 end_data = max(end_data, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size));
4409
4410 fcb->Header.ValidDataLength.QuadPart = newlength;
4411 TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart);
4412
4413 if (!make_inline && !compress && off64 == start_data && off64 + *length == end_data) {
4414 data = buf;
4415 no_buf = TRUE;
4416 } else {
4417 data = ExAllocatePoolWithTag(PagedPool, (ULONG)(end_data - start_data + bufhead), ALLOC_TAG);
4418 if (!data) {
4419 ERR("out of memory\n");
4420 Status = STATUS_INSUFFICIENT_RESOURCES;
4421 goto end;
4422 }
4423
4424 RtlZeroMemory(data + bufhead, (ULONG)(end_data - start_data));
4425
4426 TRACE("start_data = %llx\n", start_data);
4427 TRACE("end_data = %llx\n", end_data);
4428
4429 if (off64 > start_data || off64 + *length < end_data) {
4430 if (changed_length) {
4431 if (fcb->inode_item.st_size > start_data)
4432 Status = read_file(fcb, data + bufhead, start_data, fcb->inode_item.st_size - start_data, NULL, Irp);
4433 else
4434 Status = STATUS_SUCCESS;
4435 } else
4436 Status = read_file(fcb, data + bufhead, start_data, end_data - start_data, NULL, Irp);
4437
4438 if (!NT_SUCCESS(Status)) {
4439 ERR("read_file returned %08x\n", Status);
4440 ExFreePool(data);
4441 goto end;
4442 }
4443 }
4444
4445 RtlCopyMemory(data + bufhead + off64 - start_data, buf, *length);
4446 }
4447
4448 if (make_inline) {
4449 Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback);
4450 if (!NT_SUCCESS(Status)) {
4451 ERR("error - excise_extents returned %08x\n", Status);
4452 ExFreePool(data);
4453 goto end;
4454 }
4455
4456 ed2 = (EXTENT_DATA*)data;
4457 ed2->generation = fcb->Vcb->superblock.generation;
4458 ed2->decoded_size = newlength;
4459 ed2->compression = BTRFS_COMPRESSION_NONE;
4460 ed2->encryption = BTRFS_ENCRYPTION_NONE;
4461 ed2->encoding = BTRFS_ENCODING_NONE;
4462 ed2->type = EXTENT_TYPE_INLINE;
4463
4464 Status = add_extent_to_fcb(fcb, 0, ed2, (UINT16)(offsetof(EXTENT_DATA, data[0]) + newlength), FALSE, NULL, rollback);
4465 if (!NT_SUCCESS(Status)) {
4466 ERR("add_extent_to_fcb returned %08x\n", Status);
4467 ExFreePool(data);
4468 goto end;
4469 }
4470
4471 fcb->inode_item.st_blocks += newlength;
4472 } else if (compress) {
4473 Status = write_compressed(fcb, start_data, end_data, data, Irp, rollback);
4474
4475 if (!NT_SUCCESS(Status)) {
4476 ERR("write_compressed returned %08x\n", Status);
4477 ExFreePool(data);
4478 goto end;
4479 }
4480
4481 ExFreePool(data);
4482 } else {
4483 if (write_irp && Irp->MdlAddress && no_buf) {
4484 BOOL locked = Irp->MdlAddress->MdlFlags & MDL_PAGES_LOCKED;
4485
4486 if (!locked) {
4487 Status = STATUS_SUCCESS;
4488
4489 _SEH2_TRY {
4490 MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoReadAccess);
4491 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
4492 Status = _SEH2_GetExceptionCode();
4493 } _SEH2_END;
4494
4495 if (!NT_SUCCESS(Status)) {
4496 ERR("MmProbeAndLockPages threw exception %08x\n", Status);
4497 goto end;
4498 }
4499 }
4500
4501 _SEH2_TRY {
4502 Status = do_write_file(fcb, start_data, end_data, data, Irp, TRUE, 0, rollback);
4503 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
4504 Status = _SEH2_GetExceptionCode();
4505 } _SEH2_END;
4506
4507 if (!locked)
4508 MmUnlockPages(Irp->MdlAddress);
4509 } else {
4510 _SEH2_TRY {
4511 Status = do_write_file(fcb, start_data, end_data, data, Irp, FALSE, 0, rollback);
4512 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
4513 Status = _SEH2_GetExceptionCode();
4514 } _SEH2_END;
4515 }
4516
4517 if (!NT_SUCCESS(Status)) {
4518 ERR("do_write_file returned %08x\n", Status);
4519 if (!no_buf) ExFreePool(data);
4520 goto end;
4521 }
4522
4523 if (!no_buf)
4524 ExFreePool(data);
4525 }
4526 }
4527
4528 KeQuerySystemTime(&time);
4529 win_time_to_unix(time, &now);
4530
4531 if (!pagefile) {
4532 if (fcb->ads) {
4533 if (fileref && fileref->parent)
4534 origii = &fileref->parent->fcb->inode_item;
4535 else {
4536 ERR("no parent fcb found for stream\n");
4537 Status = STATUS_INTERNAL_ERROR;
4538 goto end;
4539 }
4540 } else
4541 origii = &fcb->inode_item;
4542
4543 origii->transid = Vcb->superblock.generation;
4544 origii->sequence++;
4545
4546 if (!ccb->user_set_change_time)
4547 origii->st_ctime = now;
4548
4549 if (!fcb->ads) {
4550 if (changed_length) {
4551 TRACE("setting st_size to %llx\n", newlength);
4552 origii->st_size = newlength;
4553 filter |= FILE_NOTIFY_CHANGE_SIZE;
4554 }
4555
4556 fcb->inode_item_changed = TRUE;
4557 } else {
4558 fileref->parent->fcb->inode_item_changed = TRUE;
4559
4560 if (changed_length)
4561 filter |= FILE_NOTIFY_CHANGE_STREAM_SIZE;
4562
4563 filter |= FILE_NOTIFY_CHANGE_STREAM_WRITE;
4564 }
4565
4566 if (!ccb->user_set_write_time) {
4567 origii->st_mtime = now;
4568 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
4569 }
4570
4571 mark_fcb_dirty(fcb->ads ? fileref->parent->fcb : fcb);
4572 }
4573
4574 if (changed_length) {
4575 CC_FILE_SIZES ccfs;
4576
4577 ccfs.AllocationSize = fcb->Header.AllocationSize;
4578 ccfs.FileSize = fcb->Header.FileSize;
4579 ccfs.ValidDataLength = fcb->Header.ValidDataLength;
4580
4581 _SEH2_TRY {
4582 CcSetFileSizes(FileObject, &ccfs);
4583 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
4584 Status = _SEH2_GetExceptionCode();
4585 goto end;
4586 } _SEH2_END;
4587 }
4588
4589 fcb->subvol->root_item.ctransid = Vcb->superblock.generation;
4590 fcb->subvol->root_item.ctime = now;
4591
4592 Status = STATUS_SUCCESS;
4593
4594 if (filter != 0)
4595 send_notification_fcb(fcb->ads ? fileref->parent : fileref, filter, fcb->ads ? FILE_ACTION_MODIFIED_STREAM : FILE_ACTION_MODIFIED,
4596 fcb->ads && fileref->dc ? &fileref->dc->name : NULL);
4597
4598 end:
4599 if (NT_SUCCESS(Status) && FileObject->Flags & FO_SYNCHRONOUS_IO && !paging_io) {
4600 TRACE("CurrentByteOffset was: %llx\n", FileObject->CurrentByteOffset.QuadPart);
4601 FileObject->CurrentByteOffset.QuadPart = offset.QuadPart + (NT_SUCCESS(Status) ? *length : 0);
4602 TRACE("CurrentByteOffset now: %llx\n", FileObject->CurrentByteOffset.QuadPart);
4603 }
4604
4605 if (fcb_lock)
4606 ExReleaseResourceLite(fcb->Header.Resource);
4607
4608 if (tree_lock)
4609 ExReleaseResourceLite(&Vcb->tree_lock);
4610
4611 if (paging_lock)
4612 ExReleaseResourceLite(fcb->Header.PagingIoResource);
4613
4614 return Status;
4615 }
4616
4617 NTSTATUS write_file(device_extension* Vcb, PIRP Irp, BOOLEAN wait, BOOLEAN deferred_write) {
4618 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4619 void* buf;
4620 NTSTATUS Status;
4621 LARGE_INTEGER offset = IrpSp->Parameters.Write.ByteOffset;
4622 PFILE_OBJECT FileObject = IrpSp->FileObject;
4623 fcb* fcb = FileObject ? FileObject->FsContext : NULL;
4624 LIST_ENTRY rollback;
4625
4626 InitializeListHead(&rollback);
4627
4628 TRACE("write\n");
4629
4630 Irp->IoStatus.Information = 0;
4631
4632 TRACE("offset = %llx\n", offset.QuadPart);
4633 TRACE("length = %x\n", IrpSp->Parameters.Write.Length);
4634
4635 if (!Irp->AssociatedIrp.SystemBuffer) {
4636 buf = map_user_buffer(Irp, fcb && fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE ? HighPagePriority : NormalPagePriority);
4637
4638 if (Irp->MdlAddress && !buf) {
4639 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
4640 Status = STATUS_INSUFFICIENT_RESOURCES;
4641 goto exit;
4642 }
4643 } else
4644 buf = Irp->AssociatedIrp.SystemBuffer;
4645
4646 TRACE("buf = %p\n", buf);
4647
4648 if (fcb && !(Irp->Flags & IRP_PAGING_IO) && !FsRtlCheckLockForWriteAccess(&fcb->lock, Irp)) {
4649 WARN("tried to write to locked region\n");
4650 Status = STATUS_FILE_LOCK_CONFLICT;
4651 goto exit;
4652 }
4653
4654 Status = write_file2(Vcb, Irp, offset, buf, &IrpSp->Parameters.Write.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE,
4655 wait, deferred_write, TRUE, &rollback);
4656
4657 if (Status == STATUS_PENDING)
4658 goto exit;
4659 else if (!NT_SUCCESS(Status)) {
4660 ERR("write_file2 returned %08x\n", Status);
4661 goto exit;
4662 }
4663
4664 if (NT_SUCCESS(Status)) {
4665 Irp->IoStatus.Information = IrpSp->Parameters.Write.Length;
4666
4667 if (diskacc && Status != STATUS_PENDING && Irp->Flags & IRP_NOCACHE) {
4668 PETHREAD thread = NULL;
4669
4670 if (Irp->Tail.Overlay.Thread && !IoIsSystemThread(Irp->Tail.Overlay.Thread))
4671 thread = Irp->Tail.Overlay.Thread;
4672 else if (!IoIsSystemThread(PsGetCurrentThread()))
4673 thread = PsGetCurrentThread();
4674 else if (IoIsSystemThread(PsGetCurrentThread()) && IoGetTopLevelIrp() == Irp)
4675 thread = PsGetCurrentThread();
4676
4677 if (thread)
4678 fPsUpdateDiskCounters(PsGetThreadProcess(thread), 0, IrpSp->Parameters.Write.Length, 0, 1, 0);
4679 }
4680 }
4681
4682 exit:
4683 if (NT_SUCCESS(Status))
4684 clear_rollback(&rollback);
4685 else
4686 do_rollback(Vcb, &rollback);
4687
4688 return Status;
4689 }
4690
4691 _Dispatch_type_(IRP_MJ_WRITE)
4692 _Function_class_(DRIVER_DISPATCH)
4693 NTSTATUS drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
4694 NTSTATUS Status;
4695 BOOL top_level;
4696 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
4697 device_extension* Vcb = DeviceObject->DeviceExtension;
4698 PFILE_OBJECT FileObject = IrpSp->FileObject;
4699 fcb* fcb = FileObject ? FileObject->FsContext : NULL;
4700 ccb* ccb = FileObject ? FileObject->FsContext2 : NULL;
4701 BOOLEAN wait = FileObject ? IoIsOperationSynchronous(Irp) : TRUE;
4702
4703 FsRtlEnterFileSystem();
4704
4705 top_level = is_top_level(Irp);
4706
4707 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) {
4708 Status = vol_write(DeviceObject, Irp);
4709 goto exit;
4710 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) {
4711 Status = STATUS_INVALID_PARAMETER;
4712 goto end;
4713 }
4714
4715 if (!fcb) {
4716 ERR("fcb was NULL\n");
4717 Status = STATUS_INVALID_PARAMETER;
4718 goto end;
4719 }
4720
4721 if (!ccb) {
4722 ERR("ccb was NULL\n");
4723 Status = STATUS_INVALID_PARAMETER;
4724 goto end;
4725 }
4726
4727 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
4728 WARN("insufficient permissions\n");
4729 Status = STATUS_ACCESS_DENIED;
4730 goto end;
4731 }
4732
4733 if (fcb == Vcb->volume_fcb) {
4734 if (!Vcb->locked || Vcb->locked_fileobj != FileObject) {
4735 ERR("trying to write to volume when not locked, or locked with another FileObject\n");
4736 Status = STATUS_ACCESS_DENIED;
4737 goto end;
4738 }
4739
4740 TRACE("writing directly to volume\n");
4741
4742 IoSkipCurrentIrpStackLocation(Irp);
4743
4744 Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
4745 goto exit;
4746 }
4747
4748 if (is_subvol_readonly(fcb->subvol, Irp)) {
4749 Status = STATUS_ACCESS_DENIED;
4750 goto end;
4751 }
4752
4753 if (Vcb->readonly) {
4754 Status = STATUS_MEDIA_WRITE_PROTECTED;
4755 goto end;
4756 }
4757
4758 _SEH2_TRY {
4759 if (IrpSp->MinorFunction & IRP_MN_COMPLETE) {
4760 CcMdlWriteComplete(IrpSp->FileObject, &IrpSp->Parameters.Write.ByteOffset, Irp->MdlAddress);
4761
4762 Irp->MdlAddress = NULL;
4763 Status = STATUS_SUCCESS;
4764 } else {
4765 // Don't offload jobs when doing paging IO - otherwise this can lead to
4766 // deadlocks in CcCopyWrite.
4767 if (Irp->Flags & IRP_PAGING_IO)
4768 wait = TRUE;
4769
4770 Status = write_file(Vcb, Irp, wait, FALSE);
4771 }
4772 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
4773 Status = _SEH2_GetExceptionCode();
4774 } _SEH2_END;
4775
4776 end:
4777 Irp->IoStatus.Status = Status;
4778
4779 TRACE("wrote %u bytes\n", Irp->IoStatus.Information);
4780
4781 if (Status != STATUS_PENDING)
4782 IoCompleteRequest(Irp, IO_NO_INCREMENT);
4783 else {
4784 IoMarkIrpPending(Irp);
4785
4786 if (!add_thread_job(Vcb, Irp))
4787 do_write_job(Vcb, Irp);
4788 }
4789
4790 exit:
4791 if (top_level)
4792 IoSetTopLevelIrp(NULL);
4793
4794 TRACE("returning %08x\n", Status);
4795
4796 FsRtlExitFileSystem();
4797
4798 return Status;
4799 }