@@ -471,8 +471,144 @@ static int change_csum_objectids(struct btrfs_fs_info *fs_info)
471
471
return ret ;
472
472
}
473
473
474
+ static int rewrite_tree_block_csum (struct btrfs_fs_info * fs_info , u64 logical ,
475
+ u16 new_csum_type )
476
+ {
477
+ struct extent_buffer * eb ;
478
+ u8 result_old [BTRFS_CSUM_SIZE ];
479
+ u8 result_new [BTRFS_CSUM_SIZE ];
480
+ int ret ;
481
+
482
+ eb = alloc_dummy_extent_buffer (fs_info , logical , fs_info -> nodesize );
483
+ if (!eb )
484
+ return - ENOMEM ;
485
+
486
+ ret = btrfs_read_extent_buffer (eb , 0 , 0 , NULL );
487
+ if (ret < 0 ) {
488
+ errno = - ret ;
489
+ error ("failed to read tree block at logical %llu: %m" , logical );
490
+ goto out ;
491
+ }
492
+
493
+ /* Verify the csum first. */
494
+ btrfs_csum_data (fs_info , fs_info -> csum_type , (u8 * )eb -> data + BTRFS_CSUM_SIZE ,
495
+ result_old , fs_info -> nodesize - BTRFS_CSUM_SIZE );
496
+ btrfs_csum_data (fs_info , new_csum_type , (u8 * )eb -> data + BTRFS_CSUM_SIZE ,
497
+ result_new , fs_info -> nodesize - BTRFS_CSUM_SIZE );
498
+
499
+ /* Matches old csum, rewrite. */
500
+ if (memcmp_extent_buffer (eb , result_old , 0 , fs_info -> csum_size ) == 0 ) {
501
+ write_extent_buffer (eb , result_new , 0 ,
502
+ btrfs_csum_type_size (new_csum_type ));
503
+ ret = write_data_to_disk (fs_info , eb -> data , eb -> start ,
504
+ fs_info -> nodesize );
505
+ if (ret < 0 ) {
506
+ errno = - ret ;
507
+ error ("failed to write tree block at logical %llu: %m" ,
508
+ logical );
509
+ }
510
+ goto out ;
511
+ }
512
+
513
+ /* Already new csum. */
514
+ if (memcmp_extent_buffer (eb , result_new , 0 , fs_info -> csum_size ) == 0 )
515
+ goto out ;
516
+
517
+ /* Csum doesn't match either old or new csum type, bad tree block. */
518
+ ret = - EIO ;
519
+ error ("tree block csum mismatch at logical %llu" , logical );
520
+ out :
521
+ free_extent_buffer (eb );
522
+ return ret ;
523
+ }
524
+
525
+ static int change_meta_csums (struct btrfs_fs_info * fs_info , u32 new_csum_type )
526
+ {
527
+ struct btrfs_root * extent_root = btrfs_extent_root (fs_info , 0 );
528
+ struct btrfs_path path = { 0 };
529
+ struct btrfs_key key ;
530
+ int ret ;
531
+
532
+ /*
533
+ * Disable metadata csum checks first, as we may hit tree blocks with
534
+ * either old or new csums.
535
+ * We will manually check the meta csums here.
536
+ */
537
+ fs_info -> skip_csum_check = true;
538
+
539
+ key .objectid = 0 ;
540
+ key .type = 0 ;
541
+ key .offset = 0 ;
542
+
543
+ ret = btrfs_search_slot (NULL , extent_root , & key , & path , 0 , 0 );
544
+ if (ret < 0 ) {
545
+ errno = - ret ;
546
+ error ("failed to get the first tree block of extent tree: %m" );
547
+ return ret ;
548
+ }
549
+ assert (ret > 0 );
550
+ while (true) {
551
+ btrfs_item_key_to_cpu (path .nodes [0 ], & key , path .slots [0 ]);
552
+ if (key .type != BTRFS_EXTENT_ITEM_KEY &&
553
+ key .type != BTRFS_METADATA_ITEM_KEY )
554
+ goto next ;
555
+
556
+ if (key .type == BTRFS_EXTENT_ITEM_KEY ) {
557
+ struct btrfs_extent_item * ei ;
558
+ ei = btrfs_item_ptr (path .nodes [0 ], path .slots [0 ],
559
+ struct btrfs_extent_item );
560
+ if (btrfs_extent_flags (path .nodes [0 ], ei ) &
561
+ BTRFS_EXTENT_FLAG_DATA )
562
+ goto next ;
563
+ }
564
+ ret = rewrite_tree_block_csum (fs_info , key .objectid , new_csum_type );
565
+ if (ret < 0 ) {
566
+ errno = - ret ;
567
+ error ("failed to rewrite csum for tree block %llu: %m" ,
568
+ key .offset );
569
+ goto out ;
570
+ }
571
+ next :
572
+ ret = btrfs_next_extent_item (extent_root , & path , U64_MAX );
573
+ if (ret < 0 ) {
574
+ errno = - ret ;
575
+ error ("failed to get next extent item: %m" );
576
+ }
577
+ if (ret > 0 ) {
578
+ ret = 0 ;
579
+ goto out ;
580
+ }
581
+ }
582
+ out :
583
+ btrfs_release_path (& path );
584
+
585
+ /*
586
+ * Finish the change by clearing the csum change flag and update the superblock
587
+ * csum type.
588
+ */
589
+ if (ret == 0 ) {
590
+ u64 super_flags = btrfs_super_flags (fs_info -> super_copy );
591
+
592
+ btrfs_set_super_csum_type (fs_info -> super_copy , new_csum_type );
593
+ super_flags &= ~(BTRFS_SUPER_FLAG_CHANGING_DATA_CSUM |
594
+ BTRFS_SUPER_FLAG_CHANGING_META_CSUM );
595
+ btrfs_set_super_flags (fs_info -> super_copy , super_flags );
596
+
597
+ fs_info -> csum_type = new_csum_type ;
598
+ fs_info -> csum_size = btrfs_csum_type_size (new_csum_type );
599
+
600
+ ret = write_all_supers (fs_info );
601
+ if (ret < 0 ) {
602
+ errno = - ret ;
603
+ error ("failed to write super blocks: %m" );
604
+ }
605
+ }
606
+ return ret ;
607
+ }
608
+
474
609
int btrfs_change_csum_type (struct btrfs_fs_info * fs_info , u16 new_csum_type )
475
610
{
611
+ u16 old_csum_type = fs_info -> csum_type ;
476
612
int ret ;
477
613
478
614
/* Phase 0, check conflicting features. */
@@ -511,5 +647,10 @@ int btrfs_change_csum_type(struct btrfs_fs_info *fs_info, u16 new_csum_type)
511
647
* like relocation in progs.
512
648
* Thus we have to support reading a tree block with either csum.
513
649
*/
514
- return - EOPNOTSUPP ;
650
+ ret = change_meta_csums (fs_info , new_csum_type );
651
+ if (ret == 0 )
652
+ printf ("converted csum type from %s (%u) to %s (%u)\n" ,
653
+ btrfs_super_csum_name (old_csum_type ), old_csum_type ,
654
+ btrfs_super_csum_name (new_csum_type ), new_csum_type );
655
+ return ret ;
515
656
}
0 commit comments