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