1 /* Copyright (c) Mark Harmstone 2017
3 * This file is part of WinBtrfs.
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.
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.
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/>. */
18 #include "btrfs_drv.h"
20 #define SCRUB_UNIT 0x100000 // 1 MB
22 struct _scrub_context
;
25 struct _scrub_context
* context
;
33 } scrub_context_stripe
;
35 typedef struct _scrub_context
{
37 scrub_context_stripe
* stripes
;
44 LIST_ENTRY list_entry
;
47 static void log_file_checksum_error(device_extension
* Vcb
, UINT64 addr
, UINT64 devid
, UINT64 subvol
, UINT64 inode
, UINT64 offset
) {
48 LIST_ENTRY
*le
, parts
;
53 BOOL orig_subvol
= TRUE
, not_in_tree
= FALSE
;
59 le
= Vcb
->roots
.Flink
;
60 while (le
!= &Vcb
->roots
) {
61 root
* r2
= CONTAINING_RECORD(le
, root
, list_entry
);
63 if (r2
->id
== subvol
) {
72 ERR("could not find subvol %llx\n", subvol
);
76 InitializeListHead(&parts
);
81 if (dir
== r
->root_item
.objid
) {
82 if (r
== Vcb
->root_fileref
->fcb
->subvol
)
85 searchkey
.obj_id
= r
->id
;
86 searchkey
.obj_type
= TYPE_ROOT_BACKREF
;
87 searchkey
.offset
= 0xffffffffffffffff;
89 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, NULL
);
90 if (!NT_SUCCESS(Status
)) {
91 ERR("find_item returned %08x\n", Status
);
95 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
96 ROOT_REF
* rr
= (ROOT_REF
*)tp
.item
->data
;
99 if (tp
.item
->size
< sizeof(ROOT_REF
)) {
100 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(ROOT_REF
));
104 if (tp
.item
->size
< offsetof(ROOT_REF
, name
[0]) + rr
->n
) {
105 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
,
106 tp
.item
->size
, offsetof(ROOT_REF
, name
[0]) + rr
->n
);
110 pp
= ExAllocatePoolWithTag(PagedPool
, sizeof(path_part
), ALLOC_TAG
);
112 ERR("out of memory\n");
116 pp
->name
.Buffer
= rr
->name
;
117 pp
->name
.Length
= pp
->name
.MaximumLength
= rr
->n
;
118 pp
->orig_subvol
= FALSE
;
120 InsertTailList(&parts
, &pp
->list_entry
);
124 le
= Vcb
->roots
.Flink
;
125 while (le
!= &Vcb
->roots
) {
126 root
* r2
= CONTAINING_RECORD(le
, root
, list_entry
);
128 if (r2
->id
== tp
.item
->key
.offset
) {
137 ERR("could not find subvol %llx\n", tp
.item
->key
.offset
);
148 searchkey
.obj_id
= dir
;
149 searchkey
.obj_type
= TYPE_INODE_EXTREF
;
150 searchkey
.offset
= 0xffffffffffffffff;
152 Status
= find_item(Vcb
, r
, &tp
, &searchkey
, FALSE
, NULL
);
153 if (!NT_SUCCESS(Status
)) {
154 ERR("find_item returned %08x\n", Status
);
158 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== TYPE_INODE_REF
) {
159 INODE_REF
* ir
= (INODE_REF
*)tp
.item
->data
;
162 if (tp
.item
->size
< sizeof(INODE_REF
)) {
163 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(INODE_REF
));
167 if (tp
.item
->size
< offsetof(INODE_REF
, name
[0]) + ir
->n
) {
168 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
,
169 tp
.item
->size
, offsetof(INODE_REF
, name
[0]) + ir
->n
);
173 pp
= ExAllocatePoolWithTag(PagedPool
, sizeof(path_part
), ALLOC_TAG
);
175 ERR("out of memory\n");
179 pp
->name
.Buffer
= ir
->name
;
180 pp
->name
.Length
= pp
->name
.MaximumLength
= ir
->n
;
181 pp
->orig_subvol
= orig_subvol
;
183 InsertTailList(&parts
, &pp
->list_entry
);
185 if (dir
== tp
.item
->key
.offset
)
188 dir
= tp
.item
->key
.offset
;
189 } else if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== TYPE_INODE_EXTREF
) {
190 INODE_EXTREF
* ier
= (INODE_EXTREF
*)tp
.item
->data
;
193 if (tp
.item
->size
< sizeof(INODE_EXTREF
)) {
194 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
,
195 tp
.item
->size
, sizeof(INODE_EXTREF
));
199 if (tp
.item
->size
< offsetof(INODE_EXTREF
, name
[0]) + ier
->n
) {
200 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
,
201 tp
.item
->size
, offsetof(INODE_EXTREF
, name
[0]) + ier
->n
);
205 pp
= ExAllocatePoolWithTag(PagedPool
, sizeof(path_part
), ALLOC_TAG
);
207 ERR("out of memory\n");
211 pp
->name
.Buffer
= ier
->name
;
212 pp
->name
.Length
= pp
->name
.MaximumLength
= ier
->n
;
213 pp
->orig_subvol
= orig_subvol
;
215 InsertTailList(&parts
, &pp
->list_entry
);
222 ERR("could not find INODE_REF for inode %llx in subvol %llx\n", dir
, r
->id
);
228 fn
.MaximumLength
= 0;
232 while (le
!= &parts
) {
233 path_part
* pp
= CONTAINING_RECORD(le
, path_part
, list_entry
);
234 LIST_ENTRY
* le2
= le
->Blink
;
239 RemoveTailList(&parts
);
247 while (le
!= &parts
) {
248 path_part
* pp
= CONTAINING_RECORD(le
, path_part
, list_entry
);
250 fn
.MaximumLength
+= pp
->name
.Length
+ 1;
255 fn
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fn
.MaximumLength
, ALLOC_TAG
);
257 ERR("out of memory\n");
264 while (le
!= &parts
) {
265 path_part
* pp
= CONTAINING_RECORD(le
, path_part
, list_entry
);
267 fn
.Buffer
[fn
.Length
] = '\\';
270 RtlCopyMemory(&fn
.Buffer
[fn
.Length
], pp
->name
.Buffer
, pp
->name
.Length
);
271 fn
.Length
+= pp
->name
.Length
;
277 ERR("subvol %llx, %.*s, offset %llx\n", subvol
, fn
.Length
, fn
.Buffer
, offset
);
279 ERR("%.*s, offset %llx\n", fn
.Length
, fn
.Buffer
, offset
);
281 Status
= RtlUTF8ToUnicodeN(NULL
, 0, &utf16len
, fn
.Buffer
, fn
.Length
);
282 if (!NT_SUCCESS(Status
)) {
283 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status
);
284 ExFreePool(fn
.Buffer
);
288 err
= ExAllocatePoolWithTag(PagedPool
, offsetof(scrub_error
, data
.filename
[0]) + utf16len
, ALLOC_TAG
);
290 ERR("out of memory\n");
291 ExFreePool(fn
.Buffer
);
297 err
->recovered
= FALSE
;
298 err
->is_metadata
= FALSE
;
301 err
->data
.subvol
= not_in_tree
? subvol
: 0;
302 err
->data
.offset
= offset
;
303 err
->data
.filename_length
= (UINT16
)utf16len
;
305 Status
= RtlUTF8ToUnicodeN(err
->data
.filename
, utf16len
, &utf16len
, fn
.Buffer
, fn
.Length
);
306 if (!NT_SUCCESS(Status
)) {
307 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status
);
308 ExFreePool(fn
.Buffer
);
313 ExAcquireResourceExclusiveLite(&Vcb
->scrub
.stats_lock
, TRUE
);
315 Vcb
->scrub
.num_errors
++;
316 InsertTailList(&Vcb
->scrub
.errors
, &err
->list_entry
);
318 ExReleaseResourceLite(&Vcb
->scrub
.stats_lock
);
320 ExFreePool(fn
.Buffer
);
323 while (!IsListEmpty(&parts
)) {
324 path_part
* pp
= CONTAINING_RECORD(RemoveHeadList(&parts
), path_part
, list_entry
);
330 static void log_file_checksum_error_shared(device_extension
* Vcb
, UINT64 treeaddr
, UINT64 addr
, UINT64 devid
, UINT64 extent
) {
336 tree
= ExAllocatePoolWithTag(PagedPool
, Vcb
->superblock
.node_size
, ALLOC_TAG
);
338 ERR("out of memory\n");
342 Status
= read_data(Vcb
, treeaddr
, Vcb
->superblock
.node_size
, NULL
, TRUE
, (UINT8
*)tree
, NULL
, NULL
, NULL
, 0, FALSE
, NormalPagePriority
);
343 if (!NT_SUCCESS(Status
)) {
344 ERR("read_data returned %08x\n", Status
);
348 if (tree
->level
!= 0) {
349 ERR("tree level was %x, expected 0\n", tree
->level
);
353 ln
= (leaf_node
*)&tree
[1];
355 for (i
= 0; i
< tree
->num_items
; i
++) {
356 if (ln
[i
].key
.obj_type
== TYPE_EXTENT_DATA
&& ln
[i
].size
>= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
357 EXTENT_DATA
* ed
= (EXTENT_DATA
*)((UINT8
*)tree
+ sizeof(tree_header
) + ln
[i
].offset
);
358 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
360 if (ed
->type
== EXTENT_TYPE_REGULAR
&& ed2
->size
!= 0 && ed2
->address
== addr
)
361 log_file_checksum_error(Vcb
, addr
, devid
, tree
->tree_id
, ln
[i
].key
.obj_id
, ln
[i
].key
.offset
+ addr
- extent
);
369 static void log_tree_checksum_error(device_extension
* Vcb
, UINT64 addr
, UINT64 devid
, UINT64 root
, UINT8 level
, KEY
* firstitem
) {
372 err
= ExAllocatePoolWithTag(PagedPool
, sizeof(scrub_error
), ALLOC_TAG
);
374 ERR("out of memory\n");
380 err
->recovered
= FALSE
;
381 err
->is_metadata
= TRUE
;
384 err
->metadata
.root
= root
;
385 err
->metadata
.level
= level
;
388 ERR("root %llx, level %u, first item (%llx,%x,%llx)\n", root
, level
, firstitem
->obj_id
,
389 firstitem
->obj_type
, firstitem
->offset
);
391 err
->metadata
.firstitem
= *firstitem
;
393 ERR("root %llx, level %u\n", root
, level
);
395 RtlZeroMemory(&err
->metadata
.firstitem
, sizeof(KEY
));
398 ExAcquireResourceExclusiveLite(&Vcb
->scrub
.stats_lock
, TRUE
);
400 Vcb
->scrub
.num_errors
++;
401 InsertTailList(&Vcb
->scrub
.errors
, &err
->list_entry
);
403 ExReleaseResourceLite(&Vcb
->scrub
.stats_lock
);
406 static void log_tree_checksum_error_shared(device_extension
* Vcb
, UINT64 offset
, UINT64 address
, UINT64 devid
) {
412 tree
= ExAllocatePoolWithTag(PagedPool
, Vcb
->superblock
.node_size
, ALLOC_TAG
);
414 ERR("out of memory\n");
418 Status
= read_data(Vcb
, offset
, Vcb
->superblock
.node_size
, NULL
, TRUE
, (UINT8
*)tree
, NULL
, NULL
, NULL
, 0, FALSE
, NormalPagePriority
);
419 if (!NT_SUCCESS(Status
)) {
420 ERR("read_data returned %08x\n", Status
);
424 if (tree
->level
== 0) {
425 ERR("tree level was 0\n");
429 in
= (internal_node
*)&tree
[1];
431 for (i
= 0; i
< tree
->num_items
; i
++) {
432 if (in
[i
].address
== address
) {
433 log_tree_checksum_error(Vcb
, address
, devid
, tree
->tree_id
, tree
->level
- 1, &in
[i
].key
);
442 static void log_unrecoverable_error(device_extension
* Vcb
, UINT64 address
, UINT64 devid
) {
447 EXTENT_ITEM2
* ei2
= NULL
;
452 // FIXME - still log even if rest of this function fails
454 searchkey
.obj_id
= address
;
455 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
456 searchkey
.offset
= 0xffffffffffffffff;
458 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, NULL
);
459 if (!NT_SUCCESS(Status
)) {
460 ERR("find_item returned %08x\n", Status
);
464 if ((tp
.item
->key
.obj_type
!= TYPE_EXTENT_ITEM
&& tp
.item
->key
.obj_type
!= TYPE_METADATA_ITEM
) ||
465 tp
.item
->key
.obj_id
>= address
+ Vcb
->superblock
.sector_size
||
466 (tp
.item
->key
.obj_type
== TYPE_EXTENT_ITEM
&& tp
.item
->key
.obj_id
+ tp
.item
->key
.offset
<= address
) ||
467 (tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
&& tp
.item
->key
.obj_id
+ Vcb
->superblock
.node_size
<= address
)
471 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
472 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM
));
476 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
477 ptr
= (UINT8
*)&ei
[1];
478 len
= tp
.item
->size
- sizeof(EXTENT_ITEM
);
480 if (tp
.item
->key
.obj_id
== TYPE_EXTENT_ITEM
&& ei
->flags
& EXTENT_ITEM_TREE_BLOCK
) {
481 if (tp
.item
->size
< sizeof(EXTENT_ITEM
) + sizeof(EXTENT_ITEM2
)) {
482 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
,
483 tp
.item
->size
, sizeof(EXTENT_ITEM
) + sizeof(EXTENT_ITEM2
));
487 ei2
= (EXTENT_ITEM2
*)ptr
;
489 ptr
+= sizeof(EXTENT_ITEM2
);
490 len
-= sizeof(EXTENT_ITEM2
);
501 if (type
== TYPE_TREE_BLOCK_REF
) {
504 if (len
< sizeof(TREE_BLOCK_REF
)) {
505 ERR("TREE_BLOCK_REF takes up %u bytes, but only %u remaining\n", sizeof(TREE_BLOCK_REF
), len
);
509 tbr
= (TREE_BLOCK_REF
*)ptr
;
511 log_tree_checksum_error(Vcb
, address
, devid
, tbr
->offset
, ei2
? ei2
->level
: (UINT8
)tp
.item
->key
.offset
, ei2
? &ei2
->firstitem
: NULL
);
515 ptr
+= sizeof(TREE_BLOCK_REF
);
516 len
-= sizeof(TREE_BLOCK_REF
);
517 } else if (type
== TYPE_EXTENT_DATA_REF
) {
518 EXTENT_DATA_REF
* edr
;
520 if (len
< sizeof(EXTENT_DATA_REF
)) {
521 ERR("EXTENT_DATA_REF takes up %u bytes, but only %u remaining\n", sizeof(EXTENT_DATA_REF
), len
);
525 edr
= (EXTENT_DATA_REF
*)ptr
;
527 log_file_checksum_error(Vcb
, address
, devid
, edr
->root
, edr
->objid
, edr
->offset
+ address
- tp
.item
->key
.obj_id
);
531 ptr
+= sizeof(EXTENT_DATA_REF
);
532 len
-= sizeof(EXTENT_DATA_REF
);
533 } else if (type
== TYPE_SHARED_BLOCK_REF
) {
534 SHARED_BLOCK_REF
* sbr
;
536 if (len
< sizeof(SHARED_BLOCK_REF
)) {
537 ERR("SHARED_BLOCK_REF takes up %u bytes, but only %u remaining\n", sizeof(SHARED_BLOCK_REF
), len
);
541 sbr
= (SHARED_BLOCK_REF
*)ptr
;
543 log_tree_checksum_error_shared(Vcb
, sbr
->offset
, address
, devid
);
547 ptr
+= sizeof(SHARED_BLOCK_REF
);
548 len
-= sizeof(SHARED_BLOCK_REF
);
549 } else if (type
== TYPE_SHARED_DATA_REF
) {
550 SHARED_DATA_REF
* sdr
;
552 if (len
< sizeof(SHARED_DATA_REF
)) {
553 ERR("SHARED_DATA_REF takes up %u bytes, but only %u remaining\n", sizeof(SHARED_DATA_REF
), len
);
557 sdr
= (SHARED_DATA_REF
*)ptr
;
559 log_file_checksum_error_shared(Vcb
, sdr
->offset
, address
, devid
, tp
.item
->key
.obj_id
);
563 ptr
+= sizeof(SHARED_DATA_REF
);
564 len
-= sizeof(SHARED_DATA_REF
);
566 ERR("unknown extent type %x\n", type
);
571 if (rc
< ei
->refcount
) {
573 traverse_ptr next_tp
;
575 if (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, NULL
))
580 if (tp
.item
->key
.obj_id
== address
) {
581 if (tp
.item
->key
.obj_type
== TYPE_TREE_BLOCK_REF
)
582 log_tree_checksum_error(Vcb
, address
, devid
, tp
.item
->key
.offset
, ei2
? ei2
->level
: (UINT8
)tp
.item
->key
.offset
, ei2
? &ei2
->firstitem
: NULL
);
583 else if (tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA_REF
) {
584 EXTENT_DATA_REF
* edr
;
586 if (tp
.item
->size
< sizeof(EXTENT_DATA_REF
)) {
587 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
,
588 tp
.item
->size
, sizeof(EXTENT_DATA_REF
));
592 edr
= (EXTENT_DATA_REF
*)tp
.item
->data
;
594 log_file_checksum_error(Vcb
, address
, devid
, edr
->root
, edr
->objid
, edr
->offset
+ address
- tp
.item
->key
.obj_id
);
595 } else if (tp
.item
->key
.obj_type
== TYPE_SHARED_BLOCK_REF
)
596 log_tree_checksum_error_shared(Vcb
, tp
.item
->key
.offset
, address
, devid
);
597 else if (tp
.item
->key
.obj_type
== TYPE_SHARED_DATA_REF
)
598 log_file_checksum_error_shared(Vcb
, tp
.item
->key
.offset
, address
, devid
, tp
.item
->key
.obj_id
);
605 static void log_error(device_extension
* Vcb
, UINT64 addr
, UINT64 devid
, BOOL metadata
, BOOL recoverable
, BOOL parity
) {
610 ERR("recovering from parity error at %llx on device %llx\n", addr
, devid
);
613 ERR("recovering from metadata checksum error at %llx on device %llx\n", addr
, devid
);
615 ERR("recovering from data checksum error at %llx on device %llx\n", addr
, devid
);
618 err
= ExAllocatePoolWithTag(PagedPool
, sizeof(scrub_error
), ALLOC_TAG
);
620 ERR("out of memory\n");
626 err
->recovered
= TRUE
;
627 err
->is_metadata
= metadata
;
628 err
->parity
= parity
;
631 RtlZeroMemory(&err
->metadata
, sizeof(err
->metadata
));
633 RtlZeroMemory(&err
->data
, sizeof(err
->data
));
635 ExAcquireResourceExclusiveLite(&Vcb
->scrub
.stats_lock
, TRUE
);
637 Vcb
->scrub
.num_errors
++;
638 InsertTailList(&Vcb
->scrub
.errors
, &err
->list_entry
);
640 ExReleaseResourceLite(&Vcb
->scrub
.stats_lock
);
643 ERR("unrecoverable metadata checksum error at %llx\n", addr
);
645 ERR("unrecoverable data checksum error at %llx\n", addr
);
647 log_unrecoverable_error(Vcb
, addr
, devid
);
651 _Function_class_(IO_COMPLETION_ROUTINE
)
653 static NTSTATUS NTAPI
scrub_read_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
655 static NTSTATUS
scrub_read_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
657 scrub_context_stripe
* stripe
= conptr
;
658 scrub_context
* context
= (scrub_context
*)stripe
->context
;
659 ULONG left
= InterlockedDecrement(&context
->stripes_left
);
661 UNUSED(DeviceObject
);
663 stripe
->iosb
= Irp
->IoStatus
;
666 KeSetEvent(&context
->Event
, 0, FALSE
);
668 return STATUS_MORE_PROCESSING_REQUIRED
;
671 static NTSTATUS
scrub_extent_dup(device_extension
* Vcb
, chunk
* c
, UINT64 offset
, UINT32
* csum
, scrub_context
* context
) {
673 BOOL csum_error
= FALSE
;
675 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
676 UINT16 present_devices
= 0;
679 ULONG good_stripe
= 0xffffffff;
681 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
682 if (c
->devices
[i
]->devobj
) {
685 // if first stripe is okay, we only need to check that the others are identical to it
686 if (good_stripe
!= 0xffffffff) {
687 if (RtlCompareMemory(context
->stripes
[i
].buf
, context
->stripes
[good_stripe
].buf
,
688 context
->stripes
[good_stripe
].length
) != context
->stripes
[i
].length
) {
689 context
->stripes
[i
].csum_error
= TRUE
;
691 log_device_error(Vcb
, c
->devices
[i
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
694 Status
= check_csum(Vcb
, context
->stripes
[i
].buf
, context
->stripes
[i
].length
/ Vcb
->superblock
.sector_size
, csum
);
695 if (Status
== STATUS_CRC_ERROR
) {
696 context
->stripes
[i
].csum_error
= TRUE
;
698 log_device_error(Vcb
, c
->devices
[i
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
699 } else if (!NT_SUCCESS(Status
)) {
700 ERR("check_csum returned %08x\n", Status
);
708 ULONG good_stripe
= 0xffffffff;
710 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
713 if (c
->devices
[i
]->devobj
) {
714 // if first stripe is okay, we only need to check that the others are identical to it
715 if (good_stripe
!= 0xffffffff) {
716 if (RtlCompareMemory(context
->stripes
[i
].buf
, context
->stripes
[good_stripe
].buf
,
717 context
->stripes
[good_stripe
].length
) != context
->stripes
[i
].length
) {
718 context
->stripes
[i
].csum_error
= TRUE
;
720 log_device_error(Vcb
, c
->devices
[i
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
723 for (j
= 0; j
< context
->stripes
[i
].length
/ Vcb
->superblock
.node_size
; j
++) {
724 tree_header
* th
= (tree_header
*)&context
->stripes
[i
].buf
[j
* Vcb
->superblock
.node_size
];
725 UINT32 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&th
->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(th
->csum
));
727 if (crc32
!= *((UINT32
*)th
->csum
) || th
->address
!= offset
+ UInt32x32To64(j
, Vcb
->superblock
.node_size
)) {
728 context
->stripes
[i
].csum_error
= TRUE
;
730 log_device_error(Vcb
, c
->devices
[i
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
734 if (!context
->stripes
[i
].csum_error
)
742 return STATUS_SUCCESS
;
744 // handle checksum error
746 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
747 if (context
->stripes
[i
].csum_error
) {
749 context
->stripes
[i
].bad_csums
= ExAllocatePoolWithTag(PagedPool
, context
->stripes
[i
].length
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
, ALLOC_TAG
);
750 if (!context
->stripes
[i
].bad_csums
) {
751 ERR("out of memory\n");
752 return STATUS_INSUFFICIENT_RESOURCES
;
755 Status
= calc_csum(Vcb
, context
->stripes
[i
].buf
, context
->stripes
[i
].length
/ Vcb
->superblock
.sector_size
, context
->stripes
[i
].bad_csums
);
756 if (!NT_SUCCESS(Status
)) {
757 ERR("calc_csum returned %08x\n", Status
);
763 context
->stripes
[i
].bad_csums
= ExAllocatePoolWithTag(PagedPool
, context
->stripes
[i
].length
* sizeof(UINT32
) / Vcb
->superblock
.node_size
, ALLOC_TAG
);
764 if (!context
->stripes
[i
].bad_csums
) {
765 ERR("out of memory\n");
766 return STATUS_INSUFFICIENT_RESOURCES
;
769 for (j
= 0; j
< context
->stripes
[i
].length
/ Vcb
->superblock
.node_size
; j
++) {
770 tree_header
* th
= (tree_header
*)&context
->stripes
[i
].buf
[j
* Vcb
->superblock
.node_size
];
771 UINT32 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&th
->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(th
->csum
));
773 context
->stripes
[i
].bad_csums
[j
] = crc32
;
779 if (present_devices
> 1) {
780 ULONG good_stripe
= 0xffffffff;
782 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
783 if (c
->devices
[i
]->devobj
&& !context
->stripes
[i
].csum_error
) {
789 if (good_stripe
!= 0xffffffff) {
792 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
793 if (context
->stripes
[i
].csum_error
) {
797 for (j
= 0; j
< context
->stripes
[i
].length
/ Vcb
->superblock
.sector_size
; j
++) {
798 if (context
->stripes
[i
].bad_csums
[j
] != csum
[j
]) {
799 UINT64 addr
= offset
+ UInt32x32To64(j
, Vcb
->superblock
.sector_size
);
801 log_error(Vcb
, addr
, c
->devices
[i
]->devitem
.dev_id
, FALSE
, TRUE
, FALSE
);
802 log_device_error(Vcb
, c
->devices
[i
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
806 for (j
= 0; j
< context
->stripes
[i
].length
/ Vcb
->superblock
.node_size
; j
++) {
807 tree_header
* th
= (tree_header
*)&context
->stripes
[i
].buf
[j
* Vcb
->superblock
.node_size
];
808 UINT64 addr
= offset
+ UInt32x32To64(j
, Vcb
->superblock
.node_size
);
810 if (context
->stripes
[i
].bad_csums
[j
] != *((UINT32
*)th
->csum
) || th
->address
!= addr
) {
811 log_error(Vcb
, addr
, c
->devices
[i
]->devitem
.dev_id
, TRUE
, TRUE
, FALSE
);
812 log_device_error(Vcb
, c
->devices
[i
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
819 // write good data over bad
821 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
822 if (context
->stripes
[i
].csum_error
&& !c
->devices
[i
]->readonly
) {
823 Status
= write_data_phys(c
->devices
[i
]->devobj
, cis
[i
].offset
+ offset
- c
->offset
,
824 context
->stripes
[good_stripe
].buf
, context
->stripes
[i
].length
);
826 if (!NT_SUCCESS(Status
)) {
827 ERR("write_data_phys returned %08x\n", Status
);
828 log_device_error(Vcb
, c
->devices
[i
], BTRFS_DEV_STAT_WRITE_ERRORS
);
834 return STATUS_SUCCESS
;
837 // if csum errors on all stripes, check sector by sector
839 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
842 if (c
->devices
[i
]->devobj
) {
844 for (j
= 0; j
< context
->stripes
[i
].length
/ Vcb
->superblock
.sector_size
; j
++) {
845 if (context
->stripes
[i
].bad_csums
[j
] != csum
[j
]) {
847 UINT64 addr
= offset
+ UInt32x32To64(j
, Vcb
->superblock
.sector_size
);
848 BOOL recovered
= FALSE
;
850 for (k
= 0; k
< c
->chunk_item
->num_stripes
; k
++) {
851 if (i
!= k
&& c
->devices
[k
]->devobj
&& context
->stripes
[k
].bad_csums
[j
] == csum
[j
]) {
852 log_error(Vcb
, addr
, c
->devices
[i
]->devitem
.dev_id
, FALSE
, TRUE
, FALSE
);
853 log_device_error(Vcb
, c
->devices
[i
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
855 RtlCopyMemory(context
->stripes
[i
].buf
+ (j
* Vcb
->superblock
.sector_size
),
856 context
->stripes
[k
].buf
+ (j
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
864 log_error(Vcb
, addr
, c
->devices
[i
]->devitem
.dev_id
, FALSE
, FALSE
, FALSE
);
865 log_device_error(Vcb
, c
->devices
[i
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
870 for (j
= 0; j
< context
->stripes
[i
].length
/ Vcb
->superblock
.node_size
; j
++) {
871 tree_header
* th
= (tree_header
*)&context
->stripes
[i
].buf
[j
* Vcb
->superblock
.node_size
];
872 UINT64 addr
= offset
+ UInt32x32To64(j
, Vcb
->superblock
.node_size
);
874 if (context
->stripes
[i
].bad_csums
[j
] != *((UINT32
*)th
->csum
) || th
->address
!= addr
) {
876 BOOL recovered
= FALSE
;
878 for (k
= 0; k
< c
->chunk_item
->num_stripes
; k
++) {
879 if (i
!= k
&& c
->devices
[k
]->devobj
) {
880 tree_header
* th2
= (tree_header
*)&context
->stripes
[k
].buf
[j
* Vcb
->superblock
.node_size
];
882 if (context
->stripes
[k
].bad_csums
[j
] == *((UINT32
*)th2
->csum
) && th2
->address
== addr
) {
883 log_error(Vcb
, addr
, c
->devices
[i
]->devitem
.dev_id
, TRUE
, TRUE
, FALSE
);
884 log_device_error(Vcb
, c
->devices
[i
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
886 RtlCopyMemory(th
, th2
, Vcb
->superblock
.node_size
);
895 log_error(Vcb
, addr
, c
->devices
[i
]->devitem
.dev_id
, TRUE
, FALSE
, FALSE
);
896 log_device_error(Vcb
, c
->devices
[i
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
904 // write good data over bad
906 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
907 if (c
->devices
[i
]->devobj
&& !c
->devices
[i
]->readonly
) {
908 Status
= write_data_phys(c
->devices
[i
]->devobj
, cis
[i
].offset
+ offset
- c
->offset
,
909 context
->stripes
[i
].buf
, context
->stripes
[i
].length
);
910 if (!NT_SUCCESS(Status
)) {
911 ERR("write_data_phys returned %08x\n", Status
);
912 log_device_error(Vcb
, c
->devices
[i
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
918 return STATUS_SUCCESS
;
921 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
922 if (c
->devices
[i
]->devobj
) {
926 for (j
= 0; j
< context
->stripes
[i
].length
/ Vcb
->superblock
.sector_size
; j
++) {
927 if (context
->stripes
[i
].bad_csums
[j
] != csum
[j
]) {
928 UINT64 addr
= offset
+ UInt32x32To64(j
, Vcb
->superblock
.sector_size
);
930 log_error(Vcb
, addr
, c
->devices
[i
]->devitem
.dev_id
, FALSE
, FALSE
, FALSE
);
934 for (j
= 0; j
< context
->stripes
[i
].length
/ Vcb
->superblock
.node_size
; j
++) {
935 tree_header
* th
= (tree_header
*)&context
->stripes
[i
].buf
[j
* Vcb
->superblock
.node_size
];
936 UINT64 addr
= offset
+ UInt32x32To64(j
, Vcb
->superblock
.node_size
);
938 if (context
->stripes
[i
].bad_csums
[j
] != *((UINT32
*)th
->csum
) || th
->address
!= addr
)
939 log_error(Vcb
, addr
, c
->devices
[i
]->devitem
.dev_id
, TRUE
, FALSE
, FALSE
);
945 return STATUS_SUCCESS
;
948 static NTSTATUS
scrub_extent_raid0(device_extension
* Vcb
, chunk
* c
, UINT64 offset
, UINT32 length
, UINT16 startoffstripe
, UINT32
* csum
, scrub_context
* context
) {
951 UINT32 pos
, *stripeoff
;
954 stripeoff
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(UINT32
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
956 ERR("out of memory\n");
957 return STATUS_INSUFFICIENT_RESOURCES
;
960 RtlZeroMemory(stripeoff
, sizeof(UINT32
) * c
->chunk_item
->num_stripes
);
962 stripe
= startoffstripe
;
963 while (pos
< length
) {
967 readlen
= (UINT32
)min(context
->stripes
[stripe
].length
, c
->chunk_item
->stripe_length
- (context
->stripes
[stripe
].start
% c
->chunk_item
->stripe_length
));
969 readlen
= min(length
- pos
, (UINT32
)c
->chunk_item
->stripe_length
);
972 for (j
= 0; j
< readlen
; j
+= Vcb
->superblock
.sector_size
) {
973 UINT32 crc32
= ~calc_crc32c(0xffffffff, context
->stripes
[stripe
].buf
+ stripeoff
[stripe
], Vcb
->superblock
.sector_size
);
975 if (crc32
!= csum
[pos
/ Vcb
->superblock
.sector_size
]) {
976 UINT64 addr
= offset
+ pos
;
978 log_error(Vcb
, addr
, c
->devices
[stripe
]->devitem
.dev_id
, FALSE
, FALSE
, FALSE
);
979 log_device_error(Vcb
, c
->devices
[stripe
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
982 pos
+= Vcb
->superblock
.sector_size
;
983 stripeoff
[stripe
] += Vcb
->superblock
.sector_size
;
986 for (j
= 0; j
< readlen
; j
+= Vcb
->superblock
.node_size
) {
987 tree_header
* th
= (tree_header
*)(context
->stripes
[stripe
].buf
+ stripeoff
[stripe
]);
988 UINT32 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&th
->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(th
->csum
));
989 UINT64 addr
= offset
+ pos
;
991 if (crc32
!= *((UINT32
*)th
->csum
) || th
->address
!= addr
) {
992 log_error(Vcb
, addr
, c
->devices
[stripe
]->devitem
.dev_id
, TRUE
, FALSE
, FALSE
);
993 log_device_error(Vcb
, c
->devices
[stripe
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
996 pos
+= Vcb
->superblock
.node_size
;
997 stripeoff
[stripe
] += Vcb
->superblock
.node_size
;
1001 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1004 ExFreePool(stripeoff
);
1006 return STATUS_SUCCESS
;
1009 static NTSTATUS
scrub_extent_raid10(device_extension
* Vcb
, chunk
* c
, UINT64 offset
, UINT32 length
, UINT16 startoffstripe
, UINT32
* csum
, scrub_context
* context
) {
1011 UINT16 stripe
, sub_stripes
= max(c
->chunk_item
->sub_stripes
, 1);
1012 UINT32 pos
, *stripeoff
;
1013 BOOL csum_error
= FALSE
;
1017 stripeoff
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(UINT32
) * c
->chunk_item
->num_stripes
/ sub_stripes
, ALLOC_TAG
);
1019 ERR("out of memory\n");
1020 return STATUS_INSUFFICIENT_RESOURCES
;
1023 RtlZeroMemory(stripeoff
, sizeof(UINT32
) * c
->chunk_item
->num_stripes
/ sub_stripes
);
1025 stripe
= startoffstripe
;
1026 while (pos
< length
) {
1030 readlen
= (UINT32
)min(context
->stripes
[stripe
* sub_stripes
].length
,
1031 c
->chunk_item
->stripe_length
- (context
->stripes
[stripe
* sub_stripes
].start
% c
->chunk_item
->stripe_length
));
1033 readlen
= min(length
- pos
, (UINT32
)c
->chunk_item
->stripe_length
);
1036 ULONG good_stripe
= 0xffffffff;
1039 for (k
= 0; k
< sub_stripes
; k
++) {
1040 if (c
->devices
[(stripe
* sub_stripes
) + k
]->devobj
) {
1041 // if first stripe is okay, we only need to check that the others are identical to it
1042 if (good_stripe
!= 0xffffffff) {
1043 if (RtlCompareMemory(context
->stripes
[(stripe
* sub_stripes
) + k
].buf
+ stripeoff
[stripe
],
1044 context
->stripes
[(stripe
* sub_stripes
) + good_stripe
].buf
+ stripeoff
[stripe
],
1045 readlen
) != readlen
) {
1046 context
->stripes
[(stripe
* sub_stripes
) + k
].csum_error
= TRUE
;
1048 log_device_error(Vcb
, c
->devices
[(stripe
* sub_stripes
) + k
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
1051 for (j
= 0; j
< readlen
; j
+= Vcb
->superblock
.sector_size
) {
1052 UINT32 crc32
= ~calc_crc32c(0xffffffff, context
->stripes
[(stripe
* sub_stripes
) + k
].buf
+ stripeoff
[stripe
] + j
, Vcb
->superblock
.sector_size
);
1054 if (crc32
!= csum
[(pos
+ j
) / Vcb
->superblock
.sector_size
]) {
1056 context
->stripes
[(stripe
* sub_stripes
) + k
].csum_error
= TRUE
;
1057 log_device_error(Vcb
, c
->devices
[(stripe
* sub_stripes
) + k
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
1062 if (!context
->stripes
[(stripe
* sub_stripes
) + k
].csum_error
)
1069 stripeoff
[stripe
] += readlen
;
1071 ULONG good_stripe
= 0xffffffff;
1074 for (k
= 0; k
< sub_stripes
; k
++) {
1075 if (c
->devices
[(stripe
* sub_stripes
) + k
]->devobj
) {
1076 // if first stripe is okay, we only need to check that the others are identical to it
1077 if (good_stripe
!= 0xffffffff) {
1078 if (RtlCompareMemory(context
->stripes
[(stripe
* sub_stripes
) + k
].buf
+ stripeoff
[stripe
],
1079 context
->stripes
[(stripe
* sub_stripes
) + good_stripe
].buf
+ stripeoff
[stripe
],
1080 readlen
) != readlen
) {
1081 context
->stripes
[(stripe
* sub_stripes
) + k
].csum_error
= TRUE
;
1083 log_device_error(Vcb
, c
->devices
[(stripe
* sub_stripes
) + k
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
1086 for (j
= 0; j
< readlen
; j
+= Vcb
->superblock
.node_size
) {
1087 tree_header
* th
= (tree_header
*)(context
->stripes
[(stripe
* sub_stripes
) + k
].buf
+ stripeoff
[stripe
] + j
);
1088 UINT32 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&th
->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(th
->csum
));
1089 UINT64 addr
= offset
+ pos
+ j
;
1091 if (crc32
!= *((UINT32
*)th
->csum
) || th
->address
!= addr
) {
1093 context
->stripes
[(stripe
* sub_stripes
) + k
].csum_error
= TRUE
;
1094 log_device_error(Vcb
, c
->devices
[(stripe
* sub_stripes
) + k
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
1099 if (!context
->stripes
[(stripe
* sub_stripes
) + k
].csum_error
)
1106 stripeoff
[stripe
] += readlen
;
1109 stripe
= (stripe
+ 1) % (c
->chunk_item
->num_stripes
/ sub_stripes
);
1113 Status
= STATUS_SUCCESS
;
1117 for (j
= 0; j
< c
->chunk_item
->num_stripes
; j
+= sub_stripes
) {
1118 ULONG goodstripe
= 0xffffffff;
1120 BOOL hasbadstripe
= FALSE
;
1122 if (context
->stripes
[j
].length
== 0)
1125 for (k
= 0; k
< sub_stripes
; k
++) {
1126 if (c
->devices
[j
+ k
]->devobj
) {
1127 if (!context
->stripes
[j
+ k
].csum_error
)
1130 hasbadstripe
= TRUE
;
1135 if (goodstripe
!= 0xffffffff) {
1136 for (k
= 0; k
< sub_stripes
; k
++) {
1137 if (c
->devices
[j
+ k
]->devobj
&& context
->stripes
[j
+ k
].csum_error
) {
1139 BOOL recovered
= FALSE
;
1143 stripe
= startoffstripe
;
1144 while (pos
< length
) {
1148 readlen
= (UINT32
)min(context
->stripes
[stripe
* sub_stripes
].length
,
1149 c
->chunk_item
->stripe_length
- (context
->stripes
[stripe
* sub_stripes
].start
% c
->chunk_item
->stripe_length
));
1151 readlen
= min(length
- pos
, (UINT32
)c
->chunk_item
->stripe_length
);
1153 if (stripe
== j
/ sub_stripes
) {
1157 for (l
= 0; l
< readlen
; l
+= Vcb
->superblock
.sector_size
) {
1158 if (RtlCompareMemory(context
->stripes
[j
+ k
].buf
+ so
,
1159 context
->stripes
[j
+ goodstripe
].buf
+ so
,
1160 Vcb
->superblock
.sector_size
) != Vcb
->superblock
.sector_size
) {
1161 UINT64 addr
= offset
+ pos
;
1163 log_error(Vcb
, addr
, c
->devices
[j
+ k
]->devitem
.dev_id
, FALSE
, TRUE
, FALSE
);
1168 pos
+= Vcb
->superblock
.sector_size
;
1169 so
+= Vcb
->superblock
.sector_size
;
1174 for (l
= 0; l
< readlen
; l
+= Vcb
->superblock
.node_size
) {
1175 if (RtlCompareMemory(context
->stripes
[j
+ k
].buf
+ so
,
1176 context
->stripes
[j
+ goodstripe
].buf
+ so
,
1177 Vcb
->superblock
.node_size
) != Vcb
->superblock
.node_size
) {
1178 UINT64 addr
= offset
+ pos
;
1180 log_error(Vcb
, addr
, c
->devices
[j
+ k
]->devitem
.dev_id
, TRUE
, TRUE
, FALSE
);
1185 pos
+= Vcb
->superblock
.node_size
;
1186 so
+= Vcb
->superblock
.node_size
;
1192 stripe
= (stripe
+ 1) % (c
->chunk_item
->num_stripes
/ sub_stripes
);
1196 // write good data over bad
1198 if (!c
->devices
[j
+ k
]->readonly
) {
1199 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
1201 Status
= write_data_phys(c
->devices
[j
+ k
]->devobj
, cis
[j
+ k
].offset
+ offset
- c
->offset
,
1202 context
->stripes
[j
+ goodstripe
].buf
, context
->stripes
[j
+ goodstripe
].length
);
1204 if (!NT_SUCCESS(Status
)) {
1205 ERR("write_data_phys returned %08x\n", Status
);
1206 log_device_error(Vcb
, c
->devices
[j
+ k
], BTRFS_DEV_STAT_WRITE_ERRORS
);
1215 BOOL recovered
= FALSE
;
1218 for (k
= 0; k
< sub_stripes
; k
++) {
1219 if (c
->devices
[j
+ k
]->devobj
) {
1220 context
->stripes
[j
+ k
].bad_csums
= ExAllocatePoolWithTag(PagedPool
, context
->stripes
[j
+ k
].length
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
, ALLOC_TAG
);
1221 if (!context
->stripes
[j
+ k
].bad_csums
) {
1222 ERR("out of memory\n");
1223 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1227 Status
= calc_csum(Vcb
, context
->stripes
[j
+ k
].buf
, context
->stripes
[j
+ k
].length
/ Vcb
->superblock
.sector_size
, context
->stripes
[j
+ k
].bad_csums
);
1228 if (!NT_SUCCESS(Status
)) {
1229 ERR("calc_csum returned %08x\n", Status
);
1235 for (k
= 0; k
< sub_stripes
; k
++) {
1236 if (c
->devices
[j
+ k
]->devobj
) {
1239 context
->stripes
[j
+ k
].bad_csums
= ExAllocatePoolWithTag(PagedPool
, context
->stripes
[j
+ k
].length
* sizeof(UINT32
) / Vcb
->superblock
.node_size
, ALLOC_TAG
);
1240 if (!context
->stripes
[j
+ k
].bad_csums
) {
1241 ERR("out of memory\n");
1242 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1246 for (l
= 0; l
< context
->stripes
[j
+ k
].length
/ Vcb
->superblock
.node_size
; l
++) {
1247 tree_header
* th
= (tree_header
*)&context
->stripes
[j
+ k
].buf
[l
* Vcb
->superblock
.node_size
];
1248 UINT32 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&th
->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(th
->csum
));
1250 context
->stripes
[j
+ k
].bad_csums
[l
] = crc32
;
1258 stripe
= startoffstripe
;
1259 while (pos
< length
) {
1263 readlen
= (UINT32
)min(context
->stripes
[stripe
* sub_stripes
].length
,
1264 c
->chunk_item
->stripe_length
- (context
->stripes
[stripe
* sub_stripes
].start
% c
->chunk_item
->stripe_length
));
1266 readlen
= min(length
- pos
, (UINT32
)c
->chunk_item
->stripe_length
);
1268 if (stripe
== j
/ sub_stripes
) {
1272 for (l
= 0; l
< readlen
; l
+= Vcb
->superblock
.sector_size
) {
1273 UINT32 crc32
= csum
[pos
/ Vcb
->superblock
.sector_size
];
1274 BOOL has_error
= FALSE
;
1276 goodstripe
= 0xffffffff;
1277 for (k
= 0; k
< sub_stripes
; k
++) {
1278 if (c
->devices
[j
+ k
]->devobj
) {
1279 if (context
->stripes
[j
+ k
].bad_csums
[so
/ Vcb
->superblock
.sector_size
] != crc32
)
1287 if (goodstripe
!= 0xffffffff) {
1288 for (k
= 0; k
< sub_stripes
; k
++) {
1289 if (c
->devices
[j
+ k
]->devobj
&& context
->stripes
[j
+ k
].bad_csums
[so
/ Vcb
->superblock
.sector_size
] != crc32
) {
1290 UINT64 addr
= offset
+ pos
;
1292 log_error(Vcb
, addr
, c
->devices
[j
+ k
]->devitem
.dev_id
, FALSE
, TRUE
, FALSE
);
1296 RtlCopyMemory(context
->stripes
[j
+ k
].buf
+ so
, context
->stripes
[j
+ goodstripe
].buf
+ so
,
1297 Vcb
->superblock
.sector_size
);
1301 UINT64 addr
= offset
+ pos
;
1303 for (k
= 0; k
< sub_stripes
; k
++) {
1304 if (c
->devices
[j
+ j
]->devobj
) {
1305 log_error(Vcb
, addr
, c
->devices
[j
+ k
]->devitem
.dev_id
, FALSE
, FALSE
, FALSE
);
1306 log_device_error(Vcb
, c
->devices
[j
+ k
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
1312 pos
+= Vcb
->superblock
.sector_size
;
1313 so
+= Vcb
->superblock
.sector_size
;
1316 for (l
= 0; l
< readlen
; l
+= Vcb
->superblock
.node_size
) {
1317 for (k
= 0; k
< sub_stripes
; k
++) {
1318 if (c
->devices
[j
+ k
]->devobj
) {
1319 tree_header
* th
= (tree_header
*)&context
->stripes
[j
+ k
].buf
[so
];
1320 UINT64 addr
= offset
+ pos
;
1322 if (context
->stripes
[j
+ k
].bad_csums
[so
/ Vcb
->superblock
.node_size
] != *((UINT32
*)th
->csum
) || th
->address
!= addr
) {
1327 for (m
= 0; m
< sub_stripes
; m
++) {
1329 tree_header
* th2
= (tree_header
*)&context
->stripes
[j
+ m
].buf
[so
];
1331 if (context
->stripes
[j
+ m
].bad_csums
[so
/ Vcb
->superblock
.node_size
] == *((UINT32
*)th2
->csum
) && th2
->address
== addr
) {
1332 log_error(Vcb
, addr
, c
->devices
[j
+ k
]->devitem
.dev_id
, TRUE
, TRUE
, FALSE
);
1334 RtlCopyMemory(th
, th2
, Vcb
->superblock
.node_size
);
1339 log_device_error(Vcb
, c
->devices
[j
+ m
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
1344 log_error(Vcb
, addr
, c
->devices
[j
+ k
]->devitem
.dev_id
, TRUE
, FALSE
, FALSE
);
1349 pos
+= Vcb
->superblock
.node_size
;
1350 so
+= Vcb
->superblock
.node_size
;
1356 stripe
= (stripe
+ 1) % (c
->chunk_item
->num_stripes
/ sub_stripes
);
1360 // write good data over bad
1362 for (k
= 0; k
< sub_stripes
; k
++) {
1363 if (c
->devices
[j
+ k
]->devobj
&& !c
->devices
[j
+ k
]->readonly
) {
1364 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
1366 Status
= write_data_phys(c
->devices
[j
+ k
]->devobj
, cis
[j
+ k
].offset
+ offset
- c
->offset
,
1367 context
->stripes
[j
+ k
].buf
, context
->stripes
[j
+ k
].length
);
1369 if (!NT_SUCCESS(Status
)) {
1370 ERR("write_data_phys returned %08x\n", Status
);
1371 log_device_error(Vcb
, c
->devices
[j
+ k
], BTRFS_DEV_STAT_WRITE_ERRORS
);
1381 Status
= STATUS_SUCCESS
;
1384 ExFreePool(stripeoff
);
1389 static NTSTATUS
scrub_extent(device_extension
* Vcb
, chunk
* c
, ULONG type
, UINT64 offset
, UINT32 size
, UINT32
* csum
) {
1391 scrub_context context
;
1392 CHUNK_ITEM_STRIPE
* cis
;
1394 UINT16 startoffstripe
, num_missing
, allowed_missing
;
1396 TRACE("(%p, %p, %llx, %llx, %p)\n", Vcb
, c
, offset
, size
, csum
);
1398 context
.stripes
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(scrub_context_stripe
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
1399 if (!context
.stripes
) {
1400 ERR("out of memory\n");
1401 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1405 RtlZeroMemory(context
.stripes
, sizeof(scrub_context_stripe
) * c
->chunk_item
->num_stripes
);
1407 context
.stripes_left
= 0;
1409 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
1411 if (type
== BLOCK_FLAG_RAID0
) {
1412 UINT64 startoff
, endoff
;
1413 UINT16 endoffstripe
;
1415 get_raid0_offset(offset
- c
->offset
, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
, &startoff
, &startoffstripe
);
1416 get_raid0_offset(offset
+ size
- c
->offset
- 1, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
, &endoff
, &endoffstripe
);
1418 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1419 if (startoffstripe
> i
)
1420 context
.stripes
[i
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
1421 else if (startoffstripe
== i
)
1422 context
.stripes
[i
].start
= startoff
;
1424 context
.stripes
[i
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
1426 if (endoffstripe
> i
)
1427 context
.stripes
[i
].length
= (UINT32
)(endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
- context
.stripes
[i
].start
);
1428 else if (endoffstripe
== i
)
1429 context
.stripes
[i
].length
= (UINT32
)(endoff
+ 1 - context
.stripes
[i
].start
);
1431 context
.stripes
[i
].length
= (UINT32
)(endoff
- (endoff
% c
->chunk_item
->stripe_length
) - context
.stripes
[i
].start
);
1434 allowed_missing
= 0;
1435 } else if (type
== BLOCK_FLAG_RAID10
) {
1436 UINT64 startoff
, endoff
;
1437 UINT16 endoffstripe
, j
, sub_stripes
= max(c
->chunk_item
->sub_stripes
, 1);
1439 get_raid0_offset(offset
- c
->offset
, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
/ sub_stripes
, &startoff
, &startoffstripe
);
1440 get_raid0_offset(offset
+ size
- c
->offset
- 1, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
/ sub_stripes
, &endoff
, &endoffstripe
);
1442 if ((c
->chunk_item
->num_stripes
% sub_stripes
) != 0) {
1443 ERR("chunk %llx: num_stripes %x was not a multiple of sub_stripes %x!\n", c
->offset
, c
->chunk_item
->num_stripes
, sub_stripes
);
1444 Status
= STATUS_INTERNAL_ERROR
;
1448 startoffstripe
*= sub_stripes
;
1449 endoffstripe
*= sub_stripes
;
1451 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
+= sub_stripes
) {
1452 if (startoffstripe
> i
)
1453 context
.stripes
[i
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
1454 else if (startoffstripe
== i
)
1455 context
.stripes
[i
].start
= startoff
;
1457 context
.stripes
[i
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
1459 if (endoffstripe
> i
)
1460 context
.stripes
[i
].length
= (UINT32
)(endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
- context
.stripes
[i
].start
);
1461 else if (endoffstripe
== i
)
1462 context
.stripes
[i
].length
= (UINT32
)(endoff
+ 1 - context
.stripes
[i
].start
);
1464 context
.stripes
[i
].length
= (UINT32
)(endoff
- (endoff
% c
->chunk_item
->stripe_length
) - context
.stripes
[i
].start
);
1466 for (j
= 1; j
< sub_stripes
; j
++) {
1467 context
.stripes
[i
+j
].start
= context
.stripes
[i
].start
;
1468 context
.stripes
[i
+j
].length
= context
.stripes
[i
].length
;
1472 startoffstripe
/= sub_stripes
;
1473 allowed_missing
= 1;
1475 allowed_missing
= c
->chunk_item
->num_stripes
- 1;
1479 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1480 PIO_STACK_LOCATION IrpSp
;
1482 context
.stripes
[i
].context
= (struct _scrub_context
*)&context
;
1484 if (type
== BLOCK_FLAG_DUPLICATE
) {
1485 context
.stripes
[i
].start
= offset
- c
->offset
;
1486 context
.stripes
[i
].length
= size
;
1487 } else if (type
!= BLOCK_FLAG_RAID0
&& type
!= BLOCK_FLAG_RAID10
) {
1488 ERR("unexpected chunk type %x\n", type
);
1489 Status
= STATUS_INTERNAL_ERROR
;
1493 if (!c
->devices
[i
]->devobj
) {
1496 if (num_missing
> allowed_missing
) {
1497 ERR("too many missing devices (at least %u, maximum allowed %u)\n", num_missing
, allowed_missing
);
1498 Status
= STATUS_INTERNAL_ERROR
;
1501 } else if (context
.stripes
[i
].length
> 0) {
1502 context
.stripes
[i
].buf
= ExAllocatePoolWithTag(NonPagedPool
, context
.stripes
[i
].length
, ALLOC_TAG
);
1504 if (!context
.stripes
[i
].buf
) {
1505 ERR("out of memory\n");
1506 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1510 context
.stripes
[i
].Irp
= IoAllocateIrp(c
->devices
[i
]->devobj
->StackSize
, FALSE
);
1512 if (!context
.stripes
[i
].Irp
) {
1513 ERR("IoAllocateIrp failed\n");
1514 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1518 IrpSp
= IoGetNextIrpStackLocation(context
.stripes
[i
].Irp
);
1519 IrpSp
->MajorFunction
= IRP_MJ_READ
;
1521 if (c
->devices
[i
]->devobj
->Flags
& DO_BUFFERED_IO
) {
1522 context
.stripes
[i
].Irp
->AssociatedIrp
.SystemBuffer
= ExAllocatePoolWithTag(NonPagedPool
, context
.stripes
[i
].length
, ALLOC_TAG
);
1523 if (!context
.stripes
[i
].Irp
->AssociatedIrp
.SystemBuffer
) {
1524 ERR("out of memory\n");
1525 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1529 context
.stripes
[i
].Irp
->Flags
|= IRP_BUFFERED_IO
| IRP_DEALLOCATE_BUFFER
| IRP_INPUT_OPERATION
;
1531 context
.stripes
[i
].Irp
->UserBuffer
= context
.stripes
[i
].buf
;
1532 } else if (c
->devices
[i
]->devobj
->Flags
& DO_DIRECT_IO
) {
1533 context
.stripes
[i
].Irp
->MdlAddress
= IoAllocateMdl(context
.stripes
[i
].buf
, context
.stripes
[i
].length
, FALSE
, FALSE
, NULL
);
1534 if (!context
.stripes
[i
].Irp
->MdlAddress
) {
1535 ERR("IoAllocateMdl failed\n");
1536 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1540 Status
= STATUS_SUCCESS
;
1543 MmProbeAndLockPages(context
.stripes
[i
].Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
1544 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
1545 Status
= _SEH2_GetExceptionCode();
1548 if (!NT_SUCCESS(Status
)) {
1549 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
1550 IoFreeMdl(context
.stripes
[i
].Irp
->MdlAddress
);
1551 context
.stripes
[i
].Irp
->MdlAddress
= NULL
;
1555 context
.stripes
[i
].Irp
->UserBuffer
= context
.stripes
[i
].buf
;
1557 IrpSp
->Parameters
.Read
.Length
= context
.stripes
[i
].length
;
1558 IrpSp
->Parameters
.Read
.ByteOffset
.QuadPart
= context
.stripes
[i
].start
+ cis
[i
].offset
;
1560 context
.stripes
[i
].Irp
->UserIosb
= &context
.stripes
[i
].iosb
;
1562 IoSetCompletionRoutine(context
.stripes
[i
].Irp
, scrub_read_completion
, &context
.stripes
[i
], TRUE
, TRUE
, TRUE
);
1564 context
.stripes_left
++;
1566 Vcb
->scrub
.data_scrubbed
+= context
.stripes
[i
].length
;
1570 if (context
.stripes_left
== 0) {
1571 ERR("error - not reading any stripes\n");
1572 Status
= STATUS_INTERNAL_ERROR
;
1576 KeInitializeEvent(&context
.Event
, NotificationEvent
, FALSE
);
1578 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1579 if (c
->devices
[i
]->devobj
&& context
.stripes
[i
].length
> 0)
1580 IoCallDriver(c
->devices
[i
]->devobj
, context
.stripes
[i
].Irp
);
1583 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
1585 // return an error if any of the stripes returned an error
1586 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1587 if (!NT_SUCCESS(context
.stripes
[i
].iosb
.Status
)) {
1588 Status
= context
.stripes
[i
].iosb
.Status
;
1589 log_device_error(Vcb
, c
->devices
[i
], BTRFS_DEV_STAT_READ_ERRORS
);
1594 if (type
== BLOCK_FLAG_DUPLICATE
) {
1595 Status
= scrub_extent_dup(Vcb
, c
, offset
, csum
, &context
);
1596 if (!NT_SUCCESS(Status
)) {
1597 ERR("scrub_extent_dup returned %08x\n", Status
);
1600 } else if (type
== BLOCK_FLAG_RAID0
) {
1601 Status
= scrub_extent_raid0(Vcb
, c
, offset
, size
, startoffstripe
, csum
, &context
);
1602 if (!NT_SUCCESS(Status
)) {
1603 ERR("scrub_extent_raid0 returned %08x\n", Status
);
1606 } else if (type
== BLOCK_FLAG_RAID10
) {
1607 Status
= scrub_extent_raid10(Vcb
, c
, offset
, size
, startoffstripe
, csum
, &context
);
1608 if (!NT_SUCCESS(Status
)) {
1609 ERR("scrub_extent_raid10 returned %08x\n", Status
);
1615 if (context
.stripes
) {
1616 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1617 if (context
.stripes
[i
].Irp
) {
1618 if (c
->devices
[i
]->devobj
->Flags
& DO_DIRECT_IO
&& context
.stripes
[i
].Irp
->MdlAddress
) {
1619 MmUnlockPages(context
.stripes
[i
].Irp
->MdlAddress
);
1620 IoFreeMdl(context
.stripes
[i
].Irp
->MdlAddress
);
1622 IoFreeIrp(context
.stripes
[i
].Irp
);
1625 if (context
.stripes
[i
].buf
)
1626 ExFreePool(context
.stripes
[i
].buf
);
1628 if (context
.stripes
[i
].bad_csums
)
1629 ExFreePool(context
.stripes
[i
].bad_csums
);
1632 ExFreePool(context
.stripes
);
1638 static NTSTATUS
scrub_data_extent(device_extension
* Vcb
, chunk
* c
, UINT64 offset
, ULONG type
, UINT32
* csum
, RTL_BITMAP
* bmp
) {
1640 ULONG runlength
, index
;
1642 runlength
= RtlFindFirstRunClear(bmp
, &index
);
1644 while (runlength
!= 0) {
1648 if (runlength
* Vcb
->superblock
.sector_size
> SCRUB_UNIT
)
1649 rl
= SCRUB_UNIT
/ Vcb
->superblock
.sector_size
;
1653 Status
= scrub_extent(Vcb
, c
, type
, offset
+ UInt32x32To64(index
, Vcb
->superblock
.sector_size
), rl
* Vcb
->superblock
.sector_size
, &csum
[index
]);
1654 if (!NT_SUCCESS(Status
)) {
1655 ERR("scrub_data_extent_dup returned %08x\n", Status
);
1661 } while (runlength
> 0);
1663 runlength
= RtlFindNextForwardRunClear(bmp
, index
, &index
);
1666 return STATUS_SUCCESS
;
1673 IO_STATUS_BLOCK iosb
;
1675 BOOL rewrite
, missing
;
1678 } scrub_context_raid56_stripe
;
1681 scrub_context_raid56_stripe
* stripes
;
1685 RTL_BITMAP has_csum
;
1688 UINT8
* parity_scratch
;
1689 UINT8
* parity_scratch2
;
1690 } scrub_context_raid56
;
1692 _Function_class_(IO_COMPLETION_ROUTINE
)
1694 static NTSTATUS NTAPI
scrub_read_completion_raid56(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
1696 static NTSTATUS
scrub_read_completion_raid56(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
1698 scrub_context_raid56_stripe
* stripe
= conptr
;
1699 scrub_context_raid56
* context
= (scrub_context_raid56
*)stripe
->context
;
1700 LONG left
= InterlockedDecrement(&context
->stripes_left
);
1702 UNUSED(DeviceObject
);
1704 stripe
->iosb
= Irp
->IoStatus
;
1707 KeSetEvent(&context
->Event
, 0, FALSE
);
1709 return STATUS_MORE_PROCESSING_REQUIRED
;
1712 static void scrub_raid5_stripe(device_extension
* Vcb
, chunk
* c
, scrub_context_raid56
* context
, UINT64 stripe_start
, UINT64 bit_start
,
1713 UINT64 num
, UINT16 missing_devices
) {
1714 ULONG sectors_per_stripe
= (ULONG
)(c
->chunk_item
->stripe_length
/ Vcb
->superblock
.sector_size
), i
, off
;
1715 UINT16 stripe
, parity
= (bit_start
+ num
+ c
->chunk_item
->num_stripes
- 1) % c
->chunk_item
->num_stripes
;
1718 stripe
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
1719 off
= (ULONG
)(bit_start
+ num
- stripe_start
) * sectors_per_stripe
* (c
->chunk_item
->num_stripes
- 1);
1720 stripeoff
= num
* sectors_per_stripe
;
1722 if (missing_devices
== 0)
1723 RtlCopyMemory(context
->parity_scratch
, &context
->stripes
[parity
].buf
[num
* c
->chunk_item
->stripe_length
], (ULONG
)c
->chunk_item
->stripe_length
);
1725 while (stripe
!= parity
) {
1726 RtlClearAllBits(&context
->stripes
[stripe
].error
);
1728 for (i
= 0; i
< sectors_per_stripe
; i
++) {
1729 if (c
->devices
[stripe
]->devobj
&& RtlCheckBit(&context
->alloc
, off
)) {
1730 if (RtlCheckBit(&context
->is_tree
, off
)) {
1731 tree_header
* th
= (tree_header
*)&context
->stripes
[stripe
].buf
[stripeoff
* Vcb
->superblock
.sector_size
];
1732 UINT64 addr
= c
->offset
+ (stripe_start
* (c
->chunk_item
->num_stripes
- 1) * c
->chunk_item
->stripe_length
) + (off
* Vcb
->superblock
.sector_size
);
1733 UINT32 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&th
->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(th
->csum
));
1735 if (crc32
!= *((UINT32
*)th
->csum
) || th
->address
!= addr
) {
1736 RtlSetBits(&context
->stripes
[stripe
].error
, i
, Vcb
->superblock
.node_size
/ Vcb
->superblock
.sector_size
);
1737 log_device_error(Vcb
, c
->devices
[stripe
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
1739 if (missing_devices
> 0)
1740 log_error(Vcb
, addr
, c
->devices
[stripe
]->devitem
.dev_id
, TRUE
, FALSE
, FALSE
);
1743 off
+= Vcb
->superblock
.node_size
/ Vcb
->superblock
.sector_size
;
1744 stripeoff
+= Vcb
->superblock
.node_size
/ Vcb
->superblock
.sector_size
;
1745 i
+= (Vcb
->superblock
.node_size
/ Vcb
->superblock
.sector_size
) - 1;
1748 } else if (RtlCheckBit(&context
->has_csum
, off
)) {
1749 UINT32 crc32
= ~calc_crc32c(0xffffffff, context
->stripes
[stripe
].buf
+ (stripeoff
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
1751 if (crc32
!= context
->csum
[off
]) {
1752 RtlSetBit(&context
->stripes
[stripe
].error
, i
);
1753 log_device_error(Vcb
, c
->devices
[stripe
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
1755 if (missing_devices
> 0) {
1756 UINT64 addr
= c
->offset
+ (stripe_start
* (c
->chunk_item
->num_stripes
- 1) * c
->chunk_item
->stripe_length
) + (off
* Vcb
->superblock
.sector_size
);
1758 log_error(Vcb
, addr
, c
->devices
[stripe
]->devitem
.dev_id
, FALSE
, FALSE
, FALSE
);
1768 if (missing_devices
== 0)
1769 do_xor(context
->parity_scratch
, &context
->stripes
[stripe
].buf
[num
* c
->chunk_item
->stripe_length
], (ULONG
)c
->chunk_item
->stripe_length
);
1771 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1772 stripeoff
= num
* sectors_per_stripe
;
1777 if (missing_devices
== 0) {
1778 RtlClearAllBits(&context
->stripes
[parity
].error
);
1780 for (i
= 0; i
< sectors_per_stripe
; i
++) {
1783 o
= i
* Vcb
->superblock
.sector_size
;
1784 for (j
= 0; j
< Vcb
->superblock
.sector_size
; j
++) { // FIXME - use SSE
1785 if (context
->parity_scratch
[o
] != 0) {
1786 RtlSetBit(&context
->stripes
[parity
].error
, i
);
1794 // log and fix errors
1796 if (missing_devices
> 0)
1799 for (i
= 0; i
< sectors_per_stripe
; i
++) {
1800 ULONG num_errors
= 0, bad_off
;
1804 stripe
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
1805 off
= (ULONG
)((bit_start
+ num
- stripe_start
) * sectors_per_stripe
* (c
->chunk_item
->num_stripes
- 1)) + i
;
1807 while (stripe
!= parity
) {
1808 if (RtlCheckBit(&context
->alloc
, off
)) {
1811 if (RtlCheckBit(&context
->stripes
[stripe
].error
, i
)) {
1812 bad_stripe
= stripe
;
1818 off
+= sectors_per_stripe
;
1819 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1825 if (num_errors
== 0 && !RtlCheckBit(&context
->stripes
[parity
].error
, i
)) // everything fine
1828 if (num_errors
== 0 && RtlCheckBit(&context
->stripes
[parity
].error
, i
)) { // parity error
1831 do_xor(&context
->stripes
[parity
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
1832 &context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
],
1833 Vcb
->superblock
.sector_size
);
1835 bad_off
= (ULONG
)((bit_start
+ num
- stripe_start
) * sectors_per_stripe
* (c
->chunk_item
->num_stripes
- 1)) + i
;
1836 addr
= c
->offset
+ (stripe_start
* (c
->chunk_item
->num_stripes
- 1) * c
->chunk_item
->stripe_length
) + (bad_off
* Vcb
->superblock
.sector_size
);
1838 context
->stripes
[parity
].rewrite
= TRUE
;
1840 log_error(Vcb
, addr
, c
->devices
[parity
]->devitem
.dev_id
, FALSE
, TRUE
, TRUE
);
1841 log_device_error(Vcb
, c
->devices
[parity
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
1842 } else if (num_errors
== 1) {
1844 UINT64 addr
= c
->offset
+ (stripe_start
* (c
->chunk_item
->num_stripes
- 1) * c
->chunk_item
->stripe_length
) + (bad_off
* Vcb
->superblock
.sector_size
);
1846 if (RtlCheckBit(&context
->is_tree
, bad_off
)) {
1849 do_xor(&context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
],
1850 &context
->stripes
[bad_stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
1851 Vcb
->superblock
.node_size
);
1853 th
= (tree_header
*)&context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
];
1854 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&th
->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(th
->csum
));
1856 if (crc32
== *((UINT32
*)th
->csum
) && th
->address
== addr
) {
1857 RtlCopyMemory(&context
->stripes
[bad_stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
1858 &context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
], Vcb
->superblock
.node_size
);
1860 context
->stripes
[bad_stripe
].rewrite
= TRUE
;
1862 RtlClearBits(&context
->stripes
[bad_stripe
].error
, i
+ 1, (Vcb
->superblock
.node_size
/ Vcb
->superblock
.sector_size
) - 1);
1864 log_error(Vcb
, addr
, c
->devices
[bad_stripe
]->devitem
.dev_id
, TRUE
, TRUE
, FALSE
);
1866 log_error(Vcb
, addr
, c
->devices
[bad_stripe
]->devitem
.dev_id
, TRUE
, FALSE
, FALSE
);
1868 do_xor(&context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
],
1869 &context
->stripes
[bad_stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
1870 Vcb
->superblock
.sector_size
);
1872 crc32
= ~calc_crc32c(0xffffffff, &context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
], Vcb
->superblock
.sector_size
);
1874 if (crc32
== context
->csum
[bad_off
]) {
1875 RtlCopyMemory(&context
->stripes
[bad_stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
1876 &context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
], Vcb
->superblock
.sector_size
);
1878 context
->stripes
[bad_stripe
].rewrite
= TRUE
;
1880 log_error(Vcb
, addr
, c
->devices
[bad_stripe
]->devitem
.dev_id
, FALSE
, TRUE
, FALSE
);
1882 log_error(Vcb
, addr
, c
->devices
[bad_stripe
]->devitem
.dev_id
, FALSE
, FALSE
, FALSE
);
1885 stripe
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
1886 off
= (ULONG
)((bit_start
+ num
- stripe_start
) * sectors_per_stripe
* (c
->chunk_item
->num_stripes
- 1)) + i
;
1888 while (stripe
!= parity
) {
1889 if (RtlCheckBit(&context
->alloc
, off
)) {
1890 if (RtlCheckBit(&context
->stripes
[stripe
].error
, i
)) {
1891 UINT64 addr
= c
->offset
+ (stripe_start
* (c
->chunk_item
->num_stripes
- 1) * c
->chunk_item
->stripe_length
) + (off
* Vcb
->superblock
.sector_size
);
1893 log_error(Vcb
, addr
, c
->devices
[stripe
]->devitem
.dev_id
, RtlCheckBit(&context
->is_tree
, off
), FALSE
, FALSE
);
1897 off
+= sectors_per_stripe
;
1898 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1904 static void scrub_raid6_stripe(device_extension
* Vcb
, chunk
* c
, scrub_context_raid56
* context
, UINT64 stripe_start
, UINT64 bit_start
,
1905 UINT64 num
, UINT16 missing_devices
) {
1906 ULONG sectors_per_stripe
= (ULONG
)(c
->chunk_item
->stripe_length
/ Vcb
->superblock
.sector_size
), i
, off
;
1907 UINT16 stripe
, parity1
= (bit_start
+ num
+ c
->chunk_item
->num_stripes
- 2) % c
->chunk_item
->num_stripes
;
1908 UINT16 parity2
= (parity1
+ 1) % c
->chunk_item
->num_stripes
;
1911 stripe
= (parity1
+ 2) % c
->chunk_item
->num_stripes
;
1912 off
= (ULONG
)(bit_start
+ num
- stripe_start
) * sectors_per_stripe
* (c
->chunk_item
->num_stripes
- 2);
1913 stripeoff
= num
* sectors_per_stripe
;
1915 if (c
->devices
[parity1
]->devobj
)
1916 RtlCopyMemory(context
->parity_scratch
, &context
->stripes
[parity1
].buf
[num
* c
->chunk_item
->stripe_length
], (ULONG
)c
->chunk_item
->stripe_length
);
1918 if (c
->devices
[parity2
]->devobj
)
1919 RtlZeroMemory(context
->parity_scratch2
, (ULONG
)c
->chunk_item
->stripe_length
);
1921 while (stripe
!= parity1
) {
1922 RtlClearAllBits(&context
->stripes
[stripe
].error
);
1924 for (i
= 0; i
< sectors_per_stripe
; i
++) {
1925 if (c
->devices
[stripe
]->devobj
&& RtlCheckBit(&context
->alloc
, off
)) {
1926 if (RtlCheckBit(&context
->is_tree
, off
)) {
1927 tree_header
* th
= (tree_header
*)&context
->stripes
[stripe
].buf
[stripeoff
* Vcb
->superblock
.sector_size
];
1928 UINT64 addr
= c
->offset
+ (stripe_start
* (c
->chunk_item
->num_stripes
- 2) * c
->chunk_item
->stripe_length
) + (off
* Vcb
->superblock
.sector_size
);
1929 UINT32 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&th
->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(th
->csum
));
1931 if (crc32
!= *((UINT32
*)th
->csum
) || th
->address
!= addr
) {
1932 RtlSetBits(&context
->stripes
[stripe
].error
, i
, Vcb
->superblock
.node_size
/ Vcb
->superblock
.sector_size
);
1933 log_device_error(Vcb
, c
->devices
[stripe
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
1935 if (missing_devices
== 2)
1936 log_error(Vcb
, addr
, c
->devices
[stripe
]->devitem
.dev_id
, TRUE
, FALSE
, FALSE
);
1939 off
+= Vcb
->superblock
.node_size
/ Vcb
->superblock
.sector_size
;
1940 stripeoff
+= Vcb
->superblock
.node_size
/ Vcb
->superblock
.sector_size
;
1941 i
+= (Vcb
->superblock
.node_size
/ Vcb
->superblock
.sector_size
) - 1;
1944 } else if (RtlCheckBit(&context
->has_csum
, off
)) {
1945 UINT32 crc32
= ~calc_crc32c(0xffffffff, context
->stripes
[stripe
].buf
+ (stripeoff
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
1947 if (crc32
!= context
->csum
[off
]) {
1948 UINT64 addr
= c
->offset
+ (stripe_start
* (c
->chunk_item
->num_stripes
- 2) * c
->chunk_item
->stripe_length
) + (off
* Vcb
->superblock
.sector_size
);
1950 RtlSetBit(&context
->stripes
[stripe
].error
, i
);
1951 log_device_error(Vcb
, c
->devices
[stripe
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
1953 if (missing_devices
== 2)
1954 log_error(Vcb
, addr
, c
->devices
[stripe
]->devitem
.dev_id
, FALSE
, FALSE
, FALSE
);
1963 if (c
->devices
[parity1
]->devobj
)
1964 do_xor(context
->parity_scratch
, &context
->stripes
[stripe
].buf
[num
* c
->chunk_item
->stripe_length
], (UINT32
)c
->chunk_item
->stripe_length
);
1966 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1967 stripeoff
= num
* sectors_per_stripe
;
1970 RtlClearAllBits(&context
->stripes
[parity1
].error
);
1972 if (missing_devices
== 0 || (missing_devices
== 1 && !c
->devices
[parity2
]->devobj
)) {
1975 for (i
= 0; i
< sectors_per_stripe
; i
++) {
1978 o
= i
* Vcb
->superblock
.sector_size
;
1979 for (j
= 0; j
< Vcb
->superblock
.sector_size
; j
++) { // FIXME - use SSE
1980 if (context
->parity_scratch
[o
] != 0) {
1981 RtlSetBit(&context
->stripes
[parity1
].error
, i
);
1989 RtlClearAllBits(&context
->stripes
[parity2
].error
);
1991 if (missing_devices
== 0 || (missing_devices
== 1 && !c
->devices
[parity1
]->devobj
)) {
1994 stripe
= parity1
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (parity1
- 1);
1996 while (stripe
!= parity2
) {
1997 galois_double(context
->parity_scratch2
, (UINT32
)c
->chunk_item
->stripe_length
);
1998 do_xor(context
->parity_scratch2
, &context
->stripes
[stripe
].buf
[num
* c
->chunk_item
->stripe_length
], (UINT32
)c
->chunk_item
->stripe_length
);
2000 stripe
= stripe
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (stripe
- 1);
2003 for (i
= 0; i
< sectors_per_stripe
; i
++) {
2004 if (RtlCompareMemory(&context
->stripes
[parity2
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2005 &context
->parity_scratch2
[i
* Vcb
->superblock
.sector_size
], Vcb
->superblock
.sector_size
) != Vcb
->superblock
.sector_size
)
2006 RtlSetBit(&context
->stripes
[parity2
].error
, i
);
2010 if (missing_devices
== 2)
2013 // log and fix errors
2015 for (i
= 0; i
< sectors_per_stripe
; i
++) {
2016 ULONG num_errors
= 0;
2017 UINT64 bad_stripe1
, bad_stripe2
;
2018 ULONG bad_off1
, bad_off2
;
2021 stripe
= (parity1
+ 2) % c
->chunk_item
->num_stripes
;
2022 off
= (ULONG
)((bit_start
+ num
- stripe_start
) * sectors_per_stripe
* (c
->chunk_item
->num_stripes
- 2)) + i
;
2024 while (stripe
!= parity1
) {
2025 if (RtlCheckBit(&context
->alloc
, off
)) {
2028 if (!c
->devices
[stripe
]->devobj
|| RtlCheckBit(&context
->stripes
[stripe
].error
, i
)) {
2029 if (num_errors
== 0) {
2030 bad_stripe1
= stripe
;
2032 } else if (num_errors
== 1) {
2033 bad_stripe2
= stripe
;
2040 off
+= sectors_per_stripe
;
2041 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
2047 if (num_errors
== 0 && !RtlCheckBit(&context
->stripes
[parity1
].error
, i
) && !RtlCheckBit(&context
->stripes
[parity2
].error
, i
)) // everything fine
2050 if (num_errors
== 0) { // parity error
2053 if (RtlCheckBit(&context
->stripes
[parity1
].error
, i
)) {
2054 do_xor(&context
->stripes
[parity1
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2055 &context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
],
2056 Vcb
->superblock
.sector_size
);
2058 bad_off1
= (ULONG
)((bit_start
+ num
- stripe_start
) * sectors_per_stripe
* (c
->chunk_item
->num_stripes
- 2)) + i
;
2059 addr
= c
->offset
+ (stripe_start
* (c
->chunk_item
->num_stripes
- 2) * c
->chunk_item
->stripe_length
) + (bad_off1
* Vcb
->superblock
.sector_size
);
2061 context
->stripes
[parity1
].rewrite
= TRUE
;
2063 log_error(Vcb
, addr
, c
->devices
[parity1
]->devitem
.dev_id
, FALSE
, TRUE
, TRUE
);
2064 log_device_error(Vcb
, c
->devices
[parity1
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
2067 if (RtlCheckBit(&context
->stripes
[parity2
].error
, i
)) {
2068 RtlCopyMemory(&context
->stripes
[parity2
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2069 &context
->parity_scratch2
[i
* Vcb
->superblock
.sector_size
],
2070 Vcb
->superblock
.sector_size
);
2072 bad_off1
= (ULONG
)((bit_start
+ num
- stripe_start
) * sectors_per_stripe
* (c
->chunk_item
->num_stripes
- 2)) + i
;
2073 addr
= c
->offset
+ (stripe_start
* (c
->chunk_item
->num_stripes
- 2) * c
->chunk_item
->stripe_length
) + (bad_off1
* Vcb
->superblock
.sector_size
);
2075 context
->stripes
[parity2
].rewrite
= TRUE
;
2077 log_error(Vcb
, addr
, c
->devices
[parity2
]->devitem
.dev_id
, FALSE
, TRUE
, TRUE
);
2078 log_device_error(Vcb
, c
->devices
[parity2
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
2080 } else if (num_errors
== 1) {
2081 UINT32 crc32a
, crc32b
, len
;
2082 UINT16 stripe_num
, bad_stripe_num
;
2083 UINT64 addr
= c
->offset
+ (stripe_start
* (c
->chunk_item
->num_stripes
- 2) * c
->chunk_item
->stripe_length
) + (bad_off1
* Vcb
->superblock
.sector_size
);
2086 len
= RtlCheckBit(&context
->is_tree
, bad_off1
)? Vcb
->superblock
.node_size
: Vcb
->superblock
.sector_size
;
2088 scratch
= ExAllocatePoolWithTag(PagedPool
, len
, ALLOC_TAG
);
2090 ERR("out of memory\n");
2094 RtlZeroMemory(scratch
, len
);
2096 do_xor(&context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
],
2097 &context
->stripes
[bad_stripe1
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)], len
);
2099 stripe
= parity1
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (parity1
- 1);
2101 if (c
->devices
[parity2
]->devobj
) {
2102 stripe_num
= c
->chunk_item
->num_stripes
- 3;
2103 while (stripe
!= parity2
) {
2104 galois_double(scratch
, len
);
2106 if (stripe
!= bad_stripe1
)
2107 do_xor(scratch
, &context
->stripes
[stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)], len
);
2109 bad_stripe_num
= stripe_num
;
2111 stripe
= stripe
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (stripe
- 1);
2115 do_xor(scratch
, &context
->stripes
[parity2
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)], len
);
2117 if (bad_stripe_num
!= 0)
2118 galois_divpower(scratch
, (UINT8
)bad_stripe_num
, len
);
2121 if (RtlCheckBit(&context
->is_tree
, bad_off1
)) {
2122 tree_header
*th1
= NULL
, *th2
= NULL
;
2124 if (c
->devices
[parity1
]->devobj
) {
2125 th1
= (tree_header
*)&context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
];
2126 crc32a
= ~calc_crc32c(0xffffffff, (UINT8
*)&th1
->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(th1
->csum
));
2129 if (c
->devices
[parity2
]->devobj
) {
2130 th2
= (tree_header
*)scratch
;
2131 crc32b
= ~calc_crc32c(0xffffffff, (UINT8
*)&th2
->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(th2
->csum
));
2134 if ((c
->devices
[parity1
]->devobj
&& crc32a
== *((UINT32
*)th1
->csum
) && th1
->address
== addr
) ||
2135 (c
->devices
[parity2
]->devobj
&& crc32b
== *((UINT32
*)th2
->csum
) && th2
->address
== addr
)) {
2136 if (!c
->devices
[parity1
]->devobj
|| crc32a
!= *((UINT32
*)th1
->csum
) || th1
->address
!= addr
) {
2137 RtlCopyMemory(&context
->stripes
[bad_stripe1
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2138 scratch
, Vcb
->superblock
.node_size
);
2140 if (c
->devices
[parity1
]->devobj
) {
2143 stripe
= (parity1
+ 2) % c
->chunk_item
->num_stripes
;
2145 RtlCopyMemory(&context
->stripes
[parity1
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2146 &context
->stripes
[stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2147 Vcb
->superblock
.node_size
);
2149 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
2151 while (stripe
!= parity1
) {
2152 do_xor(&context
->stripes
[parity1
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2153 &context
->stripes
[stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2154 Vcb
->superblock
.node_size
);
2156 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
2159 context
->stripes
[parity1
].rewrite
= TRUE
;
2161 log_error(Vcb
, addr
, c
->devices
[parity1
]->devitem
.dev_id
, FALSE
, TRUE
, TRUE
);
2162 log_device_error(Vcb
, c
->devices
[parity1
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
2165 RtlCopyMemory(&context
->stripes
[bad_stripe1
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2166 &context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
], Vcb
->superblock
.node_size
);
2168 if (!c
->devices
[parity2
]->devobj
|| crc32b
!= *((UINT32
*)th2
->csum
) || th2
->address
!= addr
) {
2170 stripe
= parity1
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (parity1
- 1);
2172 if (c
->devices
[parity2
]->devobj
) {
2173 RtlCopyMemory(&context
->stripes
[parity2
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2174 &context
->stripes
[stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2175 Vcb
->superblock
.node_size
);
2177 stripe
= stripe
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (stripe
- 1);
2179 while (stripe
!= parity2
) {
2180 galois_double(&context
->stripes
[parity2
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)], Vcb
->superblock
.node_size
);
2182 do_xor(&context
->stripes
[parity2
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2183 &context
->stripes
[stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2184 Vcb
->superblock
.node_size
);
2186 stripe
= stripe
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (stripe
- 1);
2189 context
->stripes
[parity2
].rewrite
= TRUE
;
2191 log_error(Vcb
, addr
, c
->devices
[parity2
]->devitem
.dev_id
, FALSE
, TRUE
, TRUE
);
2192 log_device_error(Vcb
, c
->devices
[parity2
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
2197 context
->stripes
[bad_stripe1
].rewrite
= TRUE
;
2199 RtlClearBits(&context
->stripes
[bad_stripe1
].error
, i
+ 1, (Vcb
->superblock
.node_size
/ Vcb
->superblock
.sector_size
) - 1);
2201 log_error(Vcb
, addr
, c
->devices
[bad_stripe1
]->devitem
.dev_id
, TRUE
, TRUE
, FALSE
);
2203 log_error(Vcb
, addr
, c
->devices
[bad_stripe1
]->devitem
.dev_id
, TRUE
, FALSE
, FALSE
);
2205 if (c
->devices
[parity1
]->devobj
)
2206 crc32a
= ~calc_crc32c(0xffffffff, &context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
], Vcb
->superblock
.sector_size
);
2208 if (c
->devices
[parity2
]->devobj
)
2209 crc32b
= ~calc_crc32c(0xffffffff, scratch
, Vcb
->superblock
.sector_size
);
2211 if ((c
->devices
[parity1
]->devobj
&& crc32a
== context
->csum
[bad_off1
]) || (c
->devices
[parity2
]->devobj
&& crc32b
== context
->csum
[bad_off1
])) {
2212 if (c
->devices
[parity2
]->devobj
&& crc32b
== context
->csum
[bad_off1
]) {
2213 RtlCopyMemory(&context
->stripes
[bad_stripe1
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2214 scratch
, Vcb
->superblock
.sector_size
);
2216 if (c
->devices
[parity1
]->devobj
&& crc32a
!= context
->csum
[bad_off1
]) {
2219 stripe
= (parity1
+ 2) % c
->chunk_item
->num_stripes
;
2221 RtlCopyMemory(&context
->stripes
[parity1
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2222 &context
->stripes
[stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2223 Vcb
->superblock
.sector_size
);
2225 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
2227 while (stripe
!= parity1
) {
2228 do_xor(&context
->stripes
[parity1
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2229 &context
->stripes
[stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2230 Vcb
->superblock
.sector_size
);
2232 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
2235 context
->stripes
[parity1
].rewrite
= TRUE
;
2237 log_error(Vcb
, addr
, c
->devices
[parity1
]->devitem
.dev_id
, FALSE
, TRUE
, TRUE
);
2238 log_device_error(Vcb
, c
->devices
[parity1
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
2241 RtlCopyMemory(&context
->stripes
[bad_stripe1
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2242 &context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
], Vcb
->superblock
.sector_size
);
2244 if (c
->devices
[parity2
]->devobj
&& crc32b
!= context
->csum
[bad_off1
]) {
2246 stripe
= parity1
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (parity1
- 1);
2248 RtlCopyMemory(&context
->stripes
[parity2
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2249 &context
->stripes
[stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2250 Vcb
->superblock
.sector_size
);
2252 stripe
= stripe
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (stripe
- 1);
2254 while (stripe
!= parity2
) {
2255 galois_double(&context
->stripes
[parity2
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)], Vcb
->superblock
.sector_size
);
2257 do_xor(&context
->stripes
[parity2
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2258 &context
->stripes
[stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2259 Vcb
->superblock
.sector_size
);
2261 stripe
= stripe
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (stripe
- 1);
2264 context
->stripes
[parity2
].rewrite
= TRUE
;
2266 log_error(Vcb
, addr
, c
->devices
[parity2
]->devitem
.dev_id
, FALSE
, TRUE
, TRUE
);
2267 log_device_error(Vcb
, c
->devices
[parity2
], BTRFS_DEV_STAT_CORRUPTION_ERRORS
);
2271 context
->stripes
[bad_stripe1
].rewrite
= TRUE
;
2273 log_error(Vcb
, addr
, c
->devices
[bad_stripe1
]->devitem
.dev_id
, FALSE
, TRUE
, FALSE
);
2275 log_error(Vcb
, addr
, c
->devices
[bad_stripe1
]->devitem
.dev_id
, FALSE
, FALSE
, FALSE
);
2278 ExFreePool(scratch
);
2279 } else if (num_errors
== 2 && missing_devices
== 0) {
2282 UINT32 len
= (RtlCheckBit(&context
->is_tree
, bad_off1
) || RtlCheckBit(&context
->is_tree
, bad_off2
)) ? Vcb
->superblock
.node_size
: Vcb
->superblock
.sector_size
;
2283 UINT8 gyx
, gx
, denom
, a
, b
, *p
, *q
, *pxy
, *qxy
;
2286 stripe
= parity1
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (parity1
- 1);
2288 // put qxy in parity_scratch
2289 // put pxy in parity_scratch2
2291 k
= c
->chunk_item
->num_stripes
- 3;
2292 if (stripe
== bad_stripe1
|| stripe
== bad_stripe2
) {
2293 RtlZeroMemory(&context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
], len
);
2294 RtlZeroMemory(&context
->parity_scratch2
[i
* Vcb
->superblock
.sector_size
], len
);
2296 if (stripe
== bad_stripe1
)
2301 RtlCopyMemory(&context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
],
2302 &context
->stripes
[stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)], len
);
2303 RtlCopyMemory(&context
->parity_scratch2
[i
* Vcb
->superblock
.sector_size
],
2304 &context
->stripes
[stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)], len
);
2307 stripe
= stripe
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (stripe
- 1);
2311 galois_double(&context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
], len
);
2313 if (stripe
!= bad_stripe1
&& stripe
!= bad_stripe2
) {
2314 do_xor(&context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
],
2315 &context
->stripes
[stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)], len
);
2316 do_xor(&context
->parity_scratch2
[i
* Vcb
->superblock
.sector_size
],
2317 &context
->stripes
[stripe
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)], len
);
2318 } else if (stripe
== bad_stripe1
)
2320 else if (stripe
== bad_stripe2
)
2323 stripe
= stripe
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (stripe
- 1);
2325 } while (stripe
!= parity2
);
2327 gyx
= gpow2(y
> x
? (y
-x
) : (255-x
+y
));
2330 denom
= gdiv(1, gyx
^ 1);
2331 a
= gmul(gyx
, denom
);
2332 b
= gmul(gx
, denom
);
2334 p
= &context
->stripes
[parity1
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)];
2335 q
= &context
->stripes
[parity2
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)];
2336 pxy
= &context
->parity_scratch2
[i
* Vcb
->superblock
.sector_size
];
2337 qxy
= &context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
];
2339 for (j
= 0; j
< len
; j
++) {
2340 *qxy
= gmul(a
, *p
^ *pxy
) ^ gmul(b
, *q
^ *qxy
);
2348 do_xor(&context
->parity_scratch2
[i
* Vcb
->superblock
.sector_size
], &context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
], len
);
2349 do_xor(&context
->parity_scratch2
[i
* Vcb
->superblock
.sector_size
], &context
->stripes
[parity1
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)], len
);
2351 addr
= c
->offset
+ (stripe_start
* (c
->chunk_item
->num_stripes
- 2) * c
->chunk_item
->stripe_length
) + (bad_off1
* Vcb
->superblock
.sector_size
);
2353 if (RtlCheckBit(&context
->is_tree
, bad_off1
)) {
2354 tree_header
* th
= (tree_header
*)&context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
];
2355 UINT32 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&th
->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(th
->csum
));
2357 if (crc32
== *((UINT32
*)th
->csum
) && th
->address
== addr
) {
2358 RtlCopyMemory(&context
->stripes
[bad_stripe1
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2359 &context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
], Vcb
->superblock
.node_size
);
2361 context
->stripes
[bad_stripe1
].rewrite
= TRUE
;
2363 RtlClearBits(&context
->stripes
[bad_stripe1
].error
, i
+ 1, (Vcb
->superblock
.node_size
/ Vcb
->superblock
.sector_size
) - 1);
2365 log_error(Vcb
, addr
, c
->devices
[bad_stripe1
]->devitem
.dev_id
, TRUE
, TRUE
, FALSE
);
2367 log_error(Vcb
, addr
, c
->devices
[bad_stripe1
]->devitem
.dev_id
, TRUE
, FALSE
, FALSE
);
2369 UINT32 crc32
= ~calc_crc32c(0xffffffff, &context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
], Vcb
->superblock
.sector_size
);
2371 if (crc32
== context
->csum
[bad_off1
]) {
2372 RtlCopyMemory(&context
->stripes
[bad_stripe1
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2373 &context
->parity_scratch
[i
* Vcb
->superblock
.sector_size
], Vcb
->superblock
.sector_size
);
2375 context
->stripes
[bad_stripe1
].rewrite
= TRUE
;
2377 log_error(Vcb
, addr
, c
->devices
[bad_stripe1
]->devitem
.dev_id
, FALSE
, TRUE
, FALSE
);
2379 log_error(Vcb
, addr
, c
->devices
[bad_stripe1
]->devitem
.dev_id
, FALSE
, FALSE
, FALSE
);
2382 addr
= c
->offset
+ (stripe_start
* (c
->chunk_item
->num_stripes
- 2) * c
->chunk_item
->stripe_length
) + (bad_off2
* Vcb
->superblock
.sector_size
);
2384 if (RtlCheckBit(&context
->is_tree
, bad_off2
)) {
2385 tree_header
* th
= (tree_header
*)&context
->parity_scratch2
[i
* Vcb
->superblock
.sector_size
];
2386 UINT32 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&th
->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(th
->csum
));
2388 if (crc32
== *((UINT32
*)th
->csum
) && th
->address
== addr
) {
2389 RtlCopyMemory(&context
->stripes
[bad_stripe2
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2390 &context
->parity_scratch2
[i
* Vcb
->superblock
.sector_size
], Vcb
->superblock
.node_size
);
2392 context
->stripes
[bad_stripe2
].rewrite
= TRUE
;
2394 RtlClearBits(&context
->stripes
[bad_stripe2
].error
, i
+ 1, (Vcb
->superblock
.node_size
/ Vcb
->superblock
.sector_size
) - 1);
2396 log_error(Vcb
, addr
, c
->devices
[bad_stripe2
]->devitem
.dev_id
, TRUE
, TRUE
, FALSE
);
2398 log_error(Vcb
, addr
, c
->devices
[bad_stripe2
]->devitem
.dev_id
, TRUE
, FALSE
, FALSE
);
2400 UINT32 crc32
= ~calc_crc32c(0xffffffff, &context
->parity_scratch2
[i
* Vcb
->superblock
.sector_size
], Vcb
->superblock
.sector_size
);
2402 if (crc32
== context
->csum
[bad_off2
]) {
2403 RtlCopyMemory(&context
->stripes
[bad_stripe2
].buf
[(num
* c
->chunk_item
->stripe_length
) + (i
* Vcb
->superblock
.sector_size
)],
2404 &context
->parity_scratch2
[i
* Vcb
->superblock
.sector_size
], Vcb
->superblock
.sector_size
);
2406 context
->stripes
[bad_stripe2
].rewrite
= TRUE
;
2408 log_error(Vcb
, addr
, c
->devices
[bad_stripe2
]->devitem
.dev_id
, FALSE
, TRUE
, FALSE
);
2410 log_error(Vcb
, addr
, c
->devices
[bad_stripe2
]->devitem
.dev_id
, FALSE
, FALSE
, FALSE
);
2413 stripe
= (parity2
+ 1) % c
->chunk_item
->num_stripes
;
2414 off
= (ULONG
)((bit_start
+ num
- stripe_start
) * sectors_per_stripe
* (c
->chunk_item
->num_stripes
- 2)) + i
;
2416 while (stripe
!= parity1
) {
2417 if (c
->devices
[stripe
]->devobj
&& RtlCheckBit(&context
->alloc
, off
)) {
2418 if (RtlCheckBit(&context
->stripes
[stripe
].error
, i
)) {
2419 UINT64 addr
= c
->offset
+ (stripe_start
* (c
->chunk_item
->num_stripes
- 2) * c
->chunk_item
->stripe_length
) + (off
* Vcb
->superblock
.sector_size
);
2421 log_error(Vcb
, addr
, c
->devices
[stripe
]->devitem
.dev_id
, RtlCheckBit(&context
->is_tree
, off
), FALSE
, FALSE
);
2425 off
+= sectors_per_stripe
;
2426 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
2432 static NTSTATUS
scrub_chunk_raid56_stripe_run(device_extension
* Vcb
, chunk
* c
, UINT64 stripe_start
, UINT64 stripe_end
) {
2437 UINT64 run_start
, run_end
, full_stripe_len
, stripe
;
2438 UINT32 max_read
, num_sectors
;
2439 ULONG arrlen
, *allocarr
, *csumarr
= NULL
, *treearr
, num_parity_stripes
= c
->chunk_item
->type
& BLOCK_FLAG_RAID6
? 2 : 1;
2440 scrub_context_raid56 context
;
2442 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
2444 TRACE("(%p, %p, %llx, %llx)\n", Vcb
, c
, stripe_start
, stripe_end
);
2446 full_stripe_len
= (c
->chunk_item
->num_stripes
- num_parity_stripes
) * c
->chunk_item
->stripe_length
;
2447 run_start
= c
->offset
+ (stripe_start
* full_stripe_len
);
2448 run_end
= c
->offset
+ ((stripe_end
+ 1) * full_stripe_len
);
2450 searchkey
.obj_id
= run_start
;
2451 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
2452 searchkey
.offset
= 0xffffffffffffffff;
2454 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, NULL
);
2455 if (!NT_SUCCESS(Status
)) {
2456 ERR("find_item returned %08x\n", Status
);
2460 num_sectors
= (UINT32
)((stripe_end
- stripe_start
+ 1) * full_stripe_len
/ Vcb
->superblock
.sector_size
);
2461 arrlen
= (ULONG
)sector_align((num_sectors
/ 8) + 1, sizeof(ULONG
));
2463 allocarr
= ExAllocatePoolWithTag(PagedPool
, arrlen
, ALLOC_TAG
);
2465 ERR("out of memory\n");
2466 return STATUS_INSUFFICIENT_RESOURCES
;
2469 treearr
= ExAllocatePoolWithTag(PagedPool
, arrlen
, ALLOC_TAG
);
2471 ERR("out of memory\n");
2472 ExFreePool(allocarr
);
2473 return STATUS_INSUFFICIENT_RESOURCES
;
2476 RtlInitializeBitMap(&context
.alloc
, allocarr
, num_sectors
);
2477 RtlClearAllBits(&context
.alloc
);
2479 RtlInitializeBitMap(&context
.is_tree
, treearr
, num_sectors
);
2480 RtlClearAllBits(&context
.is_tree
);
2482 context
.parity_scratch
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)c
->chunk_item
->stripe_length
, ALLOC_TAG
);
2483 if (!context
.parity_scratch
) {
2484 ERR("out of memory\n");
2485 ExFreePool(allocarr
);
2486 ExFreePool(treearr
);
2487 return STATUS_INSUFFICIENT_RESOURCES
;
2490 if (c
->chunk_item
->type
& BLOCK_FLAG_DATA
) {
2491 csumarr
= ExAllocatePoolWithTag(PagedPool
, arrlen
, ALLOC_TAG
);
2493 ERR("out of memory\n");
2494 ExFreePool(allocarr
);
2495 ExFreePool(treearr
);
2496 ExFreePool(context
.parity_scratch
);
2497 return STATUS_INSUFFICIENT_RESOURCES
;
2500 RtlInitializeBitMap(&context
.has_csum
, csumarr
, num_sectors
);
2501 RtlClearAllBits(&context
.has_csum
);
2503 context
.csum
= ExAllocatePoolWithTag(PagedPool
, num_sectors
* sizeof(UINT32
), ALLOC_TAG
);
2504 if (!context
.csum
) {
2505 ERR("out of memory\n");
2506 ExFreePool(allocarr
);
2507 ExFreePool(treearr
);
2508 ExFreePool(context
.parity_scratch
);
2509 ExFreePool(csumarr
);
2510 return STATUS_INSUFFICIENT_RESOURCES
;
2514 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
2515 context
.parity_scratch2
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)c
->chunk_item
->stripe_length
, ALLOC_TAG
);
2516 if (!context
.parity_scratch2
) {
2517 ERR("out of memory\n");
2518 ExFreePool(allocarr
);
2519 ExFreePool(treearr
);
2520 ExFreePool(context
.parity_scratch
);
2522 if (c
->chunk_item
->type
& BLOCK_FLAG_DATA
) {
2523 ExFreePool(csumarr
);
2524 ExFreePool(context
.csum
);
2527 return STATUS_INSUFFICIENT_RESOURCES
;
2532 traverse_ptr next_tp
;
2534 if (tp
.item
->key
.obj_id
>= run_end
)
2537 if (tp
.item
->key
.obj_type
== TYPE_EXTENT_ITEM
|| tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
) {
2538 UINT64 size
= tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
? Vcb
->superblock
.node_size
: tp
.item
->key
.offset
;
2540 if (tp
.item
->key
.obj_id
+ size
> run_start
) {
2541 UINT64 extent_start
= max(run_start
, tp
.item
->key
.obj_id
);
2542 UINT64 extent_end
= min(tp
.item
->key
.obj_id
+ size
, run_end
);
2543 BOOL extent_is_tree
= FALSE
;
2545 RtlSetBits(&context
.alloc
, (ULONG
)((extent_start
- run_start
) / Vcb
->superblock
.sector_size
), (ULONG
)((extent_end
- extent_start
) / Vcb
->superblock
.sector_size
));
2547 if (tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
)
2548 extent_is_tree
= TRUE
;
2550 EXTENT_ITEM
* ei
= (EXTENT_ITEM
*)tp
.item
->data
;
2552 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
2553 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM
));
2554 Status
= STATUS_INTERNAL_ERROR
;
2558 if (ei
->flags
& EXTENT_ITEM_TREE_BLOCK
)
2559 extent_is_tree
= TRUE
;
2563 RtlSetBits(&context
.is_tree
, (ULONG
)((extent_start
- run_start
) / Vcb
->superblock
.sector_size
), (ULONG
)((extent_end
- extent_start
) / Vcb
->superblock
.sector_size
));
2564 else if (c
->chunk_item
->type
& BLOCK_FLAG_DATA
) {
2568 searchkey
.obj_id
= EXTENT_CSUM_ID
;
2569 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
2570 searchkey
.offset
= extent_start
;
2572 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp2
, &searchkey
, FALSE
, NULL
);
2573 if (!NT_SUCCESS(Status
) && Status
!= STATUS_NOT_FOUND
) {
2574 ERR("find_item returned %08x\n", Status
);
2579 traverse_ptr next_tp2
;
2581 if (tp2
.item
->key
.offset
>= extent_end
)
2584 if (tp2
.item
->key
.offset
>= extent_start
) {
2585 UINT64 csum_start
= max(extent_start
, tp2
.item
->key
.offset
);
2586 UINT64 csum_end
= min(extent_end
, tp2
.item
->key
.offset
+ (tp2
.item
->size
* Vcb
->superblock
.sector_size
/ sizeof(UINT32
)));
2588 RtlSetBits(&context
.has_csum
, (ULONG
)((csum_start
- run_start
) / Vcb
->superblock
.sector_size
), (ULONG
)((csum_end
- csum_start
) / Vcb
->superblock
.sector_size
));
2590 RtlCopyMemory(&context
.csum
[(csum_start
- run_start
) / Vcb
->superblock
.sector_size
],
2591 tp2
.item
->data
+ ((csum_start
- tp2
.item
->key
.offset
) * sizeof(UINT32
) / Vcb
->superblock
.sector_size
),
2592 (ULONG
)((csum_end
- csum_start
) * sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2595 b2
= find_next_item(Vcb
, &tp2
, &next_tp2
, FALSE
, NULL
);
2604 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, NULL
);
2610 context
.stripes
= ExAllocatePoolWithTag(PagedPool
, sizeof(scrub_context_raid56_stripe
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
2611 if (!context
.stripes
) {
2612 ERR("out of memory\n");
2613 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2617 max_read
= (UINT32
)min(1048576 / c
->chunk_item
->stripe_length
, stripe_end
- stripe_start
+ 1); // only process 1 MB of data at a time
2619 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
2620 context
.stripes
[i
].buf
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(max_read
* c
->chunk_item
->stripe_length
), ALLOC_TAG
);
2621 if (!context
.stripes
[i
].buf
) {
2624 ERR("out of memory\n");
2626 for (j
= 0; j
< i
; j
++) {
2627 ExFreePool(context
.stripes
[j
].buf
);
2629 ExFreePool(context
.stripes
);
2631 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2635 context
.stripes
[i
].errorarr
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)sector_align(((c
->chunk_item
->stripe_length
/ Vcb
->superblock
.sector_size
) / 8) + 1, sizeof(ULONG
)), ALLOC_TAG
);
2636 if (!context
.stripes
[i
].errorarr
) {
2639 ERR("out of memory\n");
2641 ExFreePool(context
.stripes
[i
].buf
);
2643 for (j
= 0; j
< i
; j
++) {
2644 ExFreePool(context
.stripes
[j
].buf
);
2646 ExFreePool(context
.stripes
);
2648 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2652 RtlInitializeBitMap(&context
.stripes
[i
].error
, context
.stripes
[i
].errorarr
, (ULONG
)(c
->chunk_item
->stripe_length
/ Vcb
->superblock
.sector_size
));
2654 context
.stripes
[i
].context
= &context
;
2655 context
.stripes
[i
].rewrite
= FALSE
;
2658 stripe
= stripe_start
;
2660 Status
= STATUS_SUCCESS
;
2662 chunk_lock_range(Vcb
, c
, run_start
, run_end
- run_start
);
2666 UINT16 missing_devices
= 0;
2667 BOOL need_wait
= FALSE
;
2669 if (max_read
< stripe_end
+ 1 - stripe
)
2670 read_stripes
= max_read
;
2672 read_stripes
= (ULONG
)(stripe_end
+ 1 - stripe
);
2674 context
.stripes_left
= c
->chunk_item
->num_stripes
;
2676 // read megabyte by megabyte
2677 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
2678 if (c
->devices
[i
]->devobj
) {
2679 PIO_STACK_LOCATION IrpSp
;
2681 context
.stripes
[i
].Irp
= IoAllocateIrp(c
->devices
[i
]->devobj
->StackSize
, FALSE
);
2683 if (!context
.stripes
[i
].Irp
) {
2684 ERR("IoAllocateIrp failed\n");
2685 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2689 context
.stripes
[i
].Irp
->MdlAddress
= NULL
;
2691 IrpSp
= IoGetNextIrpStackLocation(context
.stripes
[i
].Irp
);
2692 IrpSp
->MajorFunction
= IRP_MJ_READ
;
2694 if (c
->devices
[i
]->devobj
->Flags
& DO_BUFFERED_IO
) {
2695 context
.stripes
[i
].Irp
->AssociatedIrp
.SystemBuffer
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)(read_stripes
* c
->chunk_item
->stripe_length
), ALLOC_TAG
);
2696 if (!context
.stripes
[i
].Irp
->AssociatedIrp
.SystemBuffer
) {
2697 ERR("out of memory\n");
2698 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2702 context
.stripes
[i
].Irp
->Flags
|= IRP_BUFFERED_IO
| IRP_DEALLOCATE_BUFFER
| IRP_INPUT_OPERATION
;
2704 context
.stripes
[i
].Irp
->UserBuffer
= context
.stripes
[i
].buf
;
2705 } else if (c
->devices
[i
]->devobj
->Flags
& DO_DIRECT_IO
) {
2706 context
.stripes
[i
].Irp
->MdlAddress
= IoAllocateMdl(context
.stripes
[i
].buf
, (ULONG
)(read_stripes
* c
->chunk_item
->stripe_length
), FALSE
, FALSE
, NULL
);
2707 if (!context
.stripes
[i
].Irp
->MdlAddress
) {
2708 ERR("IoAllocateMdl failed\n");
2709 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2713 Status
= STATUS_SUCCESS
;
2716 MmProbeAndLockPages(context
.stripes
[i
].Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
2717 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2718 Status
= _SEH2_GetExceptionCode();
2721 if (!NT_SUCCESS(Status
)) {
2722 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
2723 IoFreeMdl(context
.stripes
[i
].Irp
->MdlAddress
);
2727 context
.stripes
[i
].Irp
->UserBuffer
= context
.stripes
[i
].buf
;
2729 context
.stripes
[i
].offset
= stripe
* c
->chunk_item
->stripe_length
;
2731 IrpSp
->Parameters
.Read
.Length
= (ULONG
)(read_stripes
* c
->chunk_item
->stripe_length
);
2732 IrpSp
->Parameters
.Read
.ByteOffset
.QuadPart
= cis
[i
].offset
+ context
.stripes
[i
].offset
;
2734 context
.stripes
[i
].Irp
->UserIosb
= &context
.stripes
[i
].iosb
;
2735 context
.stripes
[i
].missing
= FALSE
;
2737 IoSetCompletionRoutine(context
.stripes
[i
].Irp
, scrub_read_completion_raid56
, &context
.stripes
[i
], TRUE
, TRUE
, TRUE
);
2739 Vcb
->scrub
.data_scrubbed
+= read_stripes
* c
->chunk_item
->stripe_length
;
2742 context
.stripes
[i
].Irp
= NULL
;
2743 context
.stripes
[i
].missing
= TRUE
;
2745 InterlockedDecrement(&context
.stripes_left
);
2749 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
&& missing_devices
> 1) {
2750 ERR("too many missing devices (%u, maximum 1)\n", missing_devices
);
2751 Status
= STATUS_UNEXPECTED_IO_ERROR
;
2753 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
&& missing_devices
> 2) {
2754 ERR("too many missing devices (%u, maximum 2)\n", missing_devices
);
2755 Status
= STATUS_UNEXPECTED_IO_ERROR
;
2760 KeInitializeEvent(&context
.Event
, NotificationEvent
, FALSE
);
2762 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
2763 if (c
->devices
[i
]->devobj
)
2764 IoCallDriver(c
->devices
[i
]->devobj
, context
.stripes
[i
].Irp
);
2767 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
2770 // return an error if any of the stripes returned an error
2771 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
2772 if (!context
.stripes
[i
].missing
&& !NT_SUCCESS(context
.stripes
[i
].iosb
.Status
)) {
2773 Status
= context
.stripes
[i
].iosb
.Status
;
2774 log_device_error(Vcb
, c
->devices
[i
], BTRFS_DEV_STAT_READ_ERRORS
);
2779 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
2780 for (i
= 0; i
< read_stripes
; i
++) {
2781 scrub_raid6_stripe(Vcb
, c
, &context
, stripe_start
, stripe
, i
, missing_devices
);
2784 for (i
= 0; i
< read_stripes
; i
++) {
2785 scrub_raid5_stripe(Vcb
, c
, &context
, stripe_start
, stripe
, i
, missing_devices
);
2788 stripe
+= read_stripes
;
2791 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
2792 if (context
.stripes
[i
].Irp
) {
2793 if (c
->devices
[i
]->devobj
->Flags
& DO_DIRECT_IO
&& context
.stripes
[i
].Irp
->MdlAddress
) {
2794 MmUnlockPages(context
.stripes
[i
].Irp
->MdlAddress
);
2795 IoFreeMdl(context
.stripes
[i
].Irp
->MdlAddress
);
2797 IoFreeIrp(context
.stripes
[i
].Irp
);
2798 context
.stripes
[i
].Irp
= NULL
;
2800 if (context
.stripes
[i
].rewrite
) {
2801 Status
= write_data_phys(c
->devices
[i
]->devobj
, cis
[i
].offset
+ context
.stripes
[i
].offset
,
2802 context
.stripes
[i
].buf
, (UINT32
)(read_stripes
* c
->chunk_item
->stripe_length
));
2804 if (!NT_SUCCESS(Status
)) {
2805 ERR("write_data_phys returned %08x\n", Status
);
2806 log_device_error(Vcb
, c
->devices
[i
], BTRFS_DEV_STAT_WRITE_ERRORS
);
2813 if (!NT_SUCCESS(Status
))
2815 } while (stripe
< stripe_end
);
2818 chunk_unlock_range(Vcb
, c
, run_start
, run_end
- run_start
);
2820 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
2821 ExFreePool(context
.stripes
[i
].buf
);
2822 ExFreePool(context
.stripes
[i
].errorarr
);
2824 ExFreePool(context
.stripes
);
2827 ExFreePool(treearr
);
2828 ExFreePool(allocarr
);
2829 ExFreePool(context
.parity_scratch
);
2831 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
2832 ExFreePool(context
.parity_scratch2
);
2834 if (c
->chunk_item
->type
& BLOCK_FLAG_DATA
) {
2835 ExFreePool(csumarr
);
2836 ExFreePool(context
.csum
);
2842 static NTSTATUS
scrub_chunk_raid56(device_extension
* Vcb
, chunk
* c
, UINT64
* offset
, BOOL
* changed
) {
2847 UINT64 full_stripe_len
, stripe
, stripe_start
, stripe_end
, total_data
= 0;
2848 ULONG num_extents
= 0, num_parity_stripes
= c
->chunk_item
->type
& BLOCK_FLAG_RAID6
? 2 : 1;
2850 full_stripe_len
= (c
->chunk_item
->num_stripes
- num_parity_stripes
) * c
->chunk_item
->stripe_length
;
2851 stripe
= (*offset
- c
->offset
) / full_stripe_len
;
2853 *offset
= c
->offset
+ (stripe
* full_stripe_len
);
2855 searchkey
.obj_id
= *offset
;
2856 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
2857 searchkey
.offset
= 0xffffffffffffffff;
2859 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, NULL
);
2860 if (!NT_SUCCESS(Status
)) {
2861 ERR("find_item returned %08x\n", Status
);
2868 traverse_ptr next_tp
;
2870 if (tp
.item
->key
.obj_id
>= c
->offset
+ c
->chunk_item
->size
)
2873 if (tp
.item
->key
.obj_id
>= *offset
&& (tp
.item
->key
.obj_type
== TYPE_EXTENT_ITEM
|| tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
)) {
2874 UINT64 size
= tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
? Vcb
->superblock
.node_size
: tp
.item
->key
.offset
;
2876 TRACE("%llx\n", tp
.item
->key
.obj_id
);
2878 if (size
< Vcb
->superblock
.sector_size
) {
2879 ERR("extent %llx has size less than sector_size (%llx < %x)\n", tp
.item
->key
.obj_id
, Vcb
->superblock
.sector_size
);
2880 return STATUS_INTERNAL_ERROR
;
2883 stripe
= (tp
.item
->key
.obj_id
- c
->offset
) / full_stripe_len
;
2886 if (stripe
> stripe_end
+ 1) {
2887 Status
= scrub_chunk_raid56_stripe_run(Vcb
, c
, stripe_start
, stripe_end
);
2888 if (!NT_SUCCESS(Status
)) {
2889 ERR("scrub_chunk_raid56_stripe_run returned %08x\n", Status
);
2893 stripe_start
= stripe
;
2896 stripe_start
= stripe
;
2898 stripe_end
= (tp
.item
->key
.obj_id
+ size
- 1 - c
->offset
) / full_stripe_len
;
2905 // only do so much at a time
2906 if (num_extents
>= 64 || total_data
>= 0x8000000) // 128 MB
2910 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, NULL
);
2917 Status
= scrub_chunk_raid56_stripe_run(Vcb
, c
, stripe_start
, stripe_end
);
2918 if (!NT_SUCCESS(Status
)) {
2919 ERR("scrub_chunk_raid56_stripe_run returned %08x\n", Status
);
2923 *offset
= c
->offset
+ ((stripe_end
+ 1) * full_stripe_len
);
2926 return STATUS_SUCCESS
;
2929 static NTSTATUS
scrub_chunk(device_extension
* Vcb
, chunk
* c
, UINT64
* offset
, BOOL
* changed
) {
2933 BOOL b
= FALSE
, tree_run
= FALSE
;
2934 ULONG type
, num_extents
= 0;
2935 UINT64 total_data
= 0, tree_run_start
, tree_run_end
;
2937 TRACE("chunk %llx\n", c
->offset
);
2939 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
2941 if (c
->chunk_item
->type
& BLOCK_FLAG_DUPLICATE
)
2942 type
= BLOCK_FLAG_DUPLICATE
;
2943 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
)
2944 type
= BLOCK_FLAG_RAID0
;
2945 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID1
)
2946 type
= BLOCK_FLAG_DUPLICATE
;
2947 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
)
2948 type
= BLOCK_FLAG_RAID10
;
2949 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
2950 Status
= scrub_chunk_raid56(Vcb
, c
, offset
, changed
);
2952 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
2953 Status
= scrub_chunk_raid56(Vcb
, c
, offset
, changed
);
2956 type
= BLOCK_FLAG_DUPLICATE
;
2958 searchkey
.obj_id
= *offset
;
2959 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
2960 searchkey
.offset
= 0xffffffffffffffff;
2962 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, NULL
);
2963 if (!NT_SUCCESS(Status
)) {
2964 ERR("error - find_item returned %08x\n", Status
);
2969 traverse_ptr next_tp
;
2971 if (tp
.item
->key
.obj_id
>= c
->offset
+ c
->chunk_item
->size
)
2974 if (tp
.item
->key
.obj_id
>= *offset
&& (tp
.item
->key
.obj_type
== TYPE_EXTENT_ITEM
|| tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
)) {
2975 UINT64 size
= tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
? Vcb
->superblock
.node_size
: tp
.item
->key
.offset
;
2977 UINT32
* csum
= NULL
;
2979 ULONG
* bmparr
= NULL
;
2981 TRACE("%llx\n", tp
.item
->key
.obj_id
);
2985 if (tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
)
2988 EXTENT_ITEM
* ei
= (EXTENT_ITEM
*)tp
.item
->data
;
2990 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
2991 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM
));
2992 Status
= STATUS_INTERNAL_ERROR
;
2996 if (ei
->flags
& EXTENT_ITEM_TREE_BLOCK
)
3000 if (size
< Vcb
->superblock
.sector_size
) {
3001 ERR("extent %llx has size less than sector_size (%llx < %x)\n", tp
.item
->key
.obj_id
, Vcb
->superblock
.sector_size
);
3002 Status
= STATUS_INTERNAL_ERROR
;
3010 csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(sizeof(UINT32
) * size
/ Vcb
->superblock
.sector_size
), ALLOC_TAG
);
3012 ERR("out of memory\n");
3013 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3017 bmparr
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(sector_align(((size
/ Vcb
->superblock
.sector_size
) >> 3) + 1, sizeof(ULONG
))), ALLOC_TAG
);
3019 ERR("out of memory\n");
3021 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3025 RtlInitializeBitMap(&bmp
, bmparr
, (ULONG
)(size
/ Vcb
->superblock
.sector_size
));
3026 RtlSetAllBits(&bmp
); // 1 = no csum, 0 = csum
3028 searchkey
.obj_id
= EXTENT_CSUM_ID
;
3029 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
3030 searchkey
.offset
= tp
.item
->key
.obj_id
;
3032 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp2
, &searchkey
, FALSE
, NULL
);
3033 if (!NT_SUCCESS(Status
) && Status
!= STATUS_NOT_FOUND
) {
3034 ERR("find_item returned %08x\n", Status
);
3040 if (Status
!= STATUS_NOT_FOUND
) {
3042 traverse_ptr next_tp2
;
3044 if (tp2
.item
->key
.obj_type
== TYPE_EXTENT_CSUM
) {
3045 if (tp2
.item
->key
.offset
>= tp
.item
->key
.obj_id
+ size
)
3047 else if (tp2
.item
->size
>= sizeof(UINT32
) && tp2
.item
->key
.offset
+ (tp2
.item
->size
* Vcb
->superblock
.sector_size
/ sizeof(UINT32
)) >= tp
.item
->key
.obj_id
) {
3048 UINT64 cs
= max(tp
.item
->key
.obj_id
, tp2
.item
->key
.offset
);
3049 UINT64 ce
= min(tp
.item
->key
.obj_id
+ size
, tp2
.item
->key
.offset
+ (tp2
.item
->size
* Vcb
->superblock
.sector_size
/ sizeof(UINT32
)));
3051 RtlCopyMemory(csum
+ ((cs
- tp
.item
->key
.obj_id
) / Vcb
->superblock
.sector_size
),
3052 tp2
.item
->data
+ ((cs
- tp2
.item
->key
.offset
) * sizeof(UINT32
) / Vcb
->superblock
.sector_size
),
3053 (ULONG
)((ce
- cs
) * sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
3055 RtlClearBits(&bmp
, (ULONG
)((cs
- tp
.item
->key
.obj_id
) / Vcb
->superblock
.sector_size
), (ULONG
)((ce
- cs
) / Vcb
->superblock
.sector_size
));
3057 if (ce
== tp
.item
->key
.obj_id
+ size
)
3062 if (find_next_item(Vcb
, &tp2
, &next_tp2
, FALSE
, NULL
))
3071 if (!is_tree
|| tp
.item
->key
.obj_id
> tree_run_end
) {
3072 Status
= scrub_extent(Vcb
, c
, type
, tree_run_start
, (UINT32
)(tree_run_end
- tree_run_start
), NULL
);
3073 if (!NT_SUCCESS(Status
)) {
3074 ERR("scrub_extent returned %08x\n", Status
);
3081 tree_run_start
= tp
.item
->key
.obj_id
;
3082 tree_run_end
= tp
.item
->key
.obj_id
+ Vcb
->superblock
.node_size
;
3085 tree_run_end
= tp
.item
->key
.obj_id
+ Vcb
->superblock
.node_size
;
3086 } else if (is_tree
) {
3088 tree_run_start
= tp
.item
->key
.obj_id
;
3089 tree_run_end
= tp
.item
->key
.obj_id
+ Vcb
->superblock
.node_size
;
3093 Status
= scrub_data_extent(Vcb
, c
, tp
.item
->key
.obj_id
, type
, csum
, &bmp
);
3094 if (!NT_SUCCESS(Status
)) {
3095 ERR("scrub_data_extent returned %08x\n", Status
);
3105 *offset
= tp
.item
->key
.obj_id
+ size
;
3111 // only do so much at a time
3112 if (num_extents
>= 64 || total_data
>= 0x8000000) // 128 MB
3116 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, NULL
);
3123 Status
= scrub_extent(Vcb
, c
, type
, tree_run_start
, (UINT32
)(tree_run_end
- tree_run_start
), NULL
);
3124 if (!NT_SUCCESS(Status
)) {
3125 ERR("scrub_extent returned %08x\n", Status
);
3130 Status
= STATUS_SUCCESS
;
3133 ExReleaseResourceLite(&Vcb
->tree_lock
);
3138 _Function_class_(KSTART_ROUTINE
)
3140 static void NTAPI
scrub_thread(void* context
) {
3142 static void scrub_thread(void* context
) {
3144 device_extension
* Vcb
= context
;
3145 LIST_ENTRY chunks
, *le
;
3149 KeInitializeEvent(&Vcb
->scrub
.finished
, NotificationEvent
, FALSE
);
3151 InitializeListHead(&chunks
);
3153 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
3155 if (Vcb
->need_write
&& !Vcb
->readonly
)
3156 Status
= do_write(Vcb
, NULL
);
3158 Status
= STATUS_SUCCESS
;
3162 if (!NT_SUCCESS(Status
)) {
3163 ExReleaseResourceLite(&Vcb
->tree_lock
);
3164 ERR("do_write returned %08x\n", Status
);
3165 Vcb
->scrub
.error
= Status
;
3169 ExConvertExclusiveToSharedLite(&Vcb
->tree_lock
);
3171 ExAcquireResourceExclusiveLite(&Vcb
->scrub
.stats_lock
, TRUE
);
3173 KeQuerySystemTime(&Vcb
->scrub
.start_time
);
3174 Vcb
->scrub
.finish_time
.QuadPart
= 0;
3175 Vcb
->scrub
.resume_time
.QuadPart
= Vcb
->scrub
.start_time
.QuadPart
;
3176 Vcb
->scrub
.duration
.QuadPart
= 0;
3177 Vcb
->scrub
.total_chunks
= 0;
3178 Vcb
->scrub
.chunks_left
= 0;
3179 Vcb
->scrub
.data_scrubbed
= 0;
3180 Vcb
->scrub
.num_errors
= 0;
3182 while (!IsListEmpty(&Vcb
->scrub
.errors
)) {
3183 scrub_error
* err
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->scrub
.errors
), scrub_error
, list_entry
);
3187 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
3189 le
= Vcb
->chunks
.Flink
;
3190 while (le
!= &Vcb
->chunks
) {
3191 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3193 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
3196 InsertTailList(&chunks
, &c
->list_entry_balance
);
3197 Vcb
->scrub
.total_chunks
++;
3198 Vcb
->scrub
.chunks_left
++;
3201 ExReleaseResourceLite(&c
->lock
);
3206 ExReleaseResourceLite(&Vcb
->chunk_lock
);
3208 ExReleaseResource(&Vcb
->scrub
.stats_lock
);
3210 ExReleaseResourceLite(&Vcb
->tree_lock
);
3212 while (!IsListEmpty(&chunks
)) {
3213 chunk
* c
= CONTAINING_RECORD(RemoveHeadList(&chunks
), chunk
, list_entry_balance
);
3214 UINT64 offset
= c
->offset
;
3219 KeWaitForSingleObject(&Vcb
->scrub
.event
, Executive
, KernelMode
, FALSE
, NULL
);
3221 if (!Vcb
->scrub
.stopping
) {
3225 Status
= scrub_chunk(Vcb
, c
, &offset
, &changed
);
3226 if (!NT_SUCCESS(Status
)) {
3227 ERR("scrub_chunk returned %08x\n", Status
);
3228 Vcb
->scrub
.stopping
= TRUE
;
3229 Vcb
->scrub
.error
= Status
;
3233 if (offset
== c
->offset
+ c
->chunk_item
->size
|| Vcb
->scrub
.stopping
)
3236 KeWaitForSingleObject(&Vcb
->scrub
.event
, Executive
, KernelMode
, FALSE
, NULL
);
3240 ExAcquireResourceExclusiveLite(&Vcb
->scrub
.stats_lock
, TRUE
);
3242 if (!Vcb
->scrub
.stopping
)
3243 Vcb
->scrub
.chunks_left
--;
3245 if (IsListEmpty(&chunks
))
3246 KeQuerySystemTime(&Vcb
->scrub
.finish_time
);
3248 ExReleaseResource(&Vcb
->scrub
.stats_lock
);
3251 c
->list_entry_balance
.Flink
= NULL
;
3254 KeQuerySystemTime(&time
);
3255 Vcb
->scrub
.duration
.QuadPart
+= time
.QuadPart
- Vcb
->scrub
.resume_time
.QuadPart
;
3258 ZwClose(Vcb
->scrub
.thread
);
3259 Vcb
->scrub
.thread
= NULL
;
3261 KeSetEvent(&Vcb
->scrub
.finished
, 0, FALSE
);
3264 NTSTATUS
start_scrub(device_extension
* Vcb
, KPROCESSOR_MODE processor_mode
) {
3267 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
3268 return STATUS_PRIVILEGE_NOT_HELD
;
3271 WARN("cannot start scrub while locked\n");
3272 return STATUS_DEVICE_NOT_READY
;
3275 if (Vcb
->balance
.thread
) {
3276 WARN("cannot start scrub while balance running\n");
3277 return STATUS_DEVICE_NOT_READY
;
3280 if (Vcb
->scrub
.thread
) {
3281 WARN("scrub already running\n");
3282 return STATUS_DEVICE_NOT_READY
;
3286 return STATUS_MEDIA_WRITE_PROTECTED
;
3288 Vcb
->scrub
.stopping
= FALSE
;
3289 Vcb
->scrub
.paused
= FALSE
;
3290 Vcb
->scrub
.error
= STATUS_SUCCESS
;
3291 KeInitializeEvent(&Vcb
->scrub
.event
, NotificationEvent
, !Vcb
->scrub
.paused
);
3293 Status
= PsCreateSystemThread(&Vcb
->scrub
.thread
, 0, NULL
, NULL
, NULL
, scrub_thread
, Vcb
);
3294 if (!NT_SUCCESS(Status
)) {
3295 ERR("PsCreateSystemThread returned %08x\n", Status
);
3299 return STATUS_SUCCESS
;
3302 NTSTATUS
query_scrub(device_extension
* Vcb
, KPROCESSOR_MODE processor_mode
, void* data
, ULONG length
) {
3303 btrfs_query_scrub
* bqs
= (btrfs_query_scrub
*)data
;
3307 btrfs_scrub_error
* bse
= NULL
;
3309 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
3310 return STATUS_PRIVILEGE_NOT_HELD
;
3312 if (length
< offsetof(btrfs_query_scrub
, errors
))
3313 return STATUS_BUFFER_TOO_SMALL
;
3315 ExAcquireResourceSharedLite(&Vcb
->scrub
.stats_lock
, TRUE
);
3317 if (Vcb
->scrub
.thread
&& Vcb
->scrub
.chunks_left
> 0)
3318 bqs
->status
= Vcb
->scrub
.paused
? BTRFS_SCRUB_PAUSED
: BTRFS_SCRUB_RUNNING
;
3320 bqs
->status
= BTRFS_SCRUB_STOPPED
;
3322 bqs
->start_time
.QuadPart
= Vcb
->scrub
.start_time
.QuadPart
;
3323 bqs
->finish_time
.QuadPart
= Vcb
->scrub
.finish_time
.QuadPart
;
3324 bqs
->chunks_left
= Vcb
->scrub
.chunks_left
;
3325 bqs
->total_chunks
= Vcb
->scrub
.total_chunks
;
3326 bqs
->data_scrubbed
= Vcb
->scrub
.data_scrubbed
;
3328 bqs
->duration
= Vcb
->scrub
.duration
.QuadPart
;
3330 if (bqs
->status
== BTRFS_SCRUB_RUNNING
) {
3333 KeQuerySystemTime(&time
);
3334 bqs
->duration
+= time
.QuadPart
- Vcb
->scrub
.resume_time
.QuadPart
;
3337 bqs
->error
= Vcb
->scrub
.error
;
3339 bqs
->num_errors
= Vcb
->scrub
.num_errors
;
3341 len
= length
- offsetof(btrfs_query_scrub
, errors
);
3343 le
= Vcb
->scrub
.errors
.Flink
;
3344 while (le
!= &Vcb
->scrub
.errors
) {
3345 scrub_error
* err
= CONTAINING_RECORD(le
, scrub_error
, list_entry
);
3348 if (err
->is_metadata
)
3349 errlen
= offsetof(btrfs_scrub_error
, metadata
.firstitem
) + sizeof(KEY
);
3351 errlen
= offsetof(btrfs_scrub_error
, data
.filename
) + err
->data
.filename_length
;
3354 Status
= STATUS_BUFFER_OVERFLOW
;
3363 if (bse
->is_metadata
)
3364 lastlen
= offsetof(btrfs_scrub_error
, metadata
.firstitem
) + sizeof(KEY
);
3366 lastlen
= offsetof(btrfs_scrub_error
, data
.filename
) + bse
->data
.filename_length
;
3368 bse
->next_entry
= lastlen
;
3369 bse
= (btrfs_scrub_error
*)(((UINT8
*)bse
) + lastlen
);
3372 bse
->next_entry
= 0;
3373 bse
->address
= err
->address
;
3374 bse
->device
= err
->device
;
3375 bse
->recovered
= err
->recovered
;
3376 bse
->is_metadata
= err
->is_metadata
;
3377 bse
->parity
= err
->parity
;
3379 if (err
->is_metadata
) {
3380 bse
->metadata
.root
= err
->metadata
.root
;
3381 bse
->metadata
.level
= err
->metadata
.level
;
3382 bse
->metadata
.firstitem
= err
->metadata
.firstitem
;
3384 bse
->data
.subvol
= err
->data
.subvol
;
3385 bse
->data
.offset
= err
->data
.offset
;
3386 bse
->data
.filename_length
= err
->data
.filename_length
;
3387 RtlCopyMemory(bse
->data
.filename
, err
->data
.filename
, err
->data
.filename_length
);
3394 Status
= STATUS_SUCCESS
;
3397 ExReleaseResourceLite(&Vcb
->scrub
.stats_lock
);
3402 NTSTATUS
pause_scrub(device_extension
* Vcb
, KPROCESSOR_MODE processor_mode
) {
3405 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
3406 return STATUS_PRIVILEGE_NOT_HELD
;
3408 if (!Vcb
->scrub
.thread
)
3409 return STATUS_DEVICE_NOT_READY
;
3411 if (Vcb
->scrub
.paused
)
3412 return STATUS_DEVICE_NOT_READY
;
3414 Vcb
->scrub
.paused
= TRUE
;
3415 KeClearEvent(&Vcb
->scrub
.event
);
3417 KeQuerySystemTime(&time
);
3418 Vcb
->scrub
.duration
.QuadPart
+= time
.QuadPart
- Vcb
->scrub
.resume_time
.QuadPart
;
3420 return STATUS_SUCCESS
;
3423 NTSTATUS
resume_scrub(device_extension
* Vcb
, KPROCESSOR_MODE processor_mode
) {
3424 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
3425 return STATUS_PRIVILEGE_NOT_HELD
;
3427 if (!Vcb
->scrub
.thread
)
3428 return STATUS_DEVICE_NOT_READY
;
3430 if (!Vcb
->scrub
.paused
)
3431 return STATUS_DEVICE_NOT_READY
;
3433 Vcb
->scrub
.paused
= FALSE
;
3434 KeSetEvent(&Vcb
->scrub
.event
, 0, FALSE
);
3436 KeQuerySystemTime(&Vcb
->scrub
.resume_time
);
3438 return STATUS_SUCCESS
;
3441 NTSTATUS
stop_scrub(device_extension
* Vcb
, KPROCESSOR_MODE processor_mode
) {
3442 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
3443 return STATUS_PRIVILEGE_NOT_HELD
;
3445 if (!Vcb
->scrub
.thread
)
3446 return STATUS_DEVICE_NOT_READY
;
3448 Vcb
->scrub
.paused
= FALSE
;
3449 Vcb
->scrub
.stopping
= TRUE
;
3450 KeSetEvent(&Vcb
->scrub
.event
, 0, FALSE
);
3452 return STATUS_SUCCESS
;