2011-10-28 02:02:19 +00:00
/*
* Copyright ( C ) 2009 Oracle . All rights reserved .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public
* License along with this program ; if not , write to the
* Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
* Boston , MA 021110 - 1307 , USA .
*/
2022-09-14 15:06:52 +00:00
# include "kerncompat.h"
2011-10-28 02:02:19 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <getopt.h>
2016-01-10 03:04:23 +00:00
# include <limits.h>
2022-09-15 11:10:53 +00:00
# include <errno.h>
# include <string.h>
# include "kernel-lib/radix-tree.h"
2020-08-18 13:56:04 +00:00
# include "kernel-shared/ctree.h"
2020-08-18 13:56:04 +00:00
# include "kernel-shared/volumes.h"
2020-08-18 13:56:04 +00:00
# include "kernel-shared/disk-io.h"
2020-08-18 13:56:04 +00:00
# include "kernel-shared/transaction.h"
2022-09-15 11:10:53 +00:00
# include "kernel-shared/extent_io.h"
2019-06-19 23:46:21 +00:00
# include "common/utils.h"
2019-06-19 23:46:21 +00:00
# include "common/help.h"
2022-09-15 11:10:53 +00:00
# include "common/extent-cache.h"
# include "common/messages.h"
2022-09-15 21:15:17 +00:00
# include "common/string-utils.h"
2011-10-28 02:02:19 +00:00
2013-09-09 20:41:54 +00:00
# define FIELD_BUF_LEN 80
2016-07-15 14:46:08 +00:00
static int debug_corrupt_block ( struct extent_buffer * eb ,
2016-07-15 14:38:07 +00:00
struct btrfs_root * root , u64 bytenr , u32 blocksize , u64 copy )
2011-10-28 02:02:19 +00:00
{
int ret ;
int num_copies ;
int mirror_num = 1 ;
while ( 1 ) {
if ( ! copy | | mirror_num = = copy ) {
2022-04-05 12:48:27 +00:00
u64 read_len = eb - > len ;
ret = read_data_from_disk ( eb - > fs_info , eb - > data ,
eb - > start , & read_len ,
mirror_num ) ;
if ( read_len < eb - > len )
ret = - EIO ;
2016-07-15 15:00:56 +00:00
if ( ret < 0 ) {
2018-10-25 12:10:54 +00:00
errno = - ret ;
error ( " cannot read eb bytenr %llu: %m " ,
2022-04-05 12:48:28 +00:00
( unsigned long long ) eb - > start ) ;
2016-07-15 15:00:56 +00:00
return ret ;
}
2011-10-28 02:02:19 +00:00
printf ( " corrupting %llu copy %d \n " , eb - > start ,
mirror_num ) ;
memset ( eb - > data , 0 , eb - > len ) ;
2022-04-05 12:48:25 +00:00
ret = write_and_map_eb ( eb - > fs_info , eb ) ;
2016-07-15 15:00:56 +00:00
if ( ret < 0 ) {
2018-10-25 12:10:54 +00:00
errno = - ret ;
error ( " cannot write eb bytenr %llu: %m " ,
2022-04-05 12:48:28 +00:00
( unsigned long long ) eb - > start ) ;
2016-07-15 15:00:56 +00:00
return ret ;
}
2011-10-28 02:02:19 +00:00
}
2017-06-13 09:19:18 +00:00
num_copies = btrfs_num_copies ( root - > fs_info , eb - > start ,
eb - > len ) ;
2011-10-28 02:02:19 +00:00
if ( num_copies = = 1 )
break ;
mirror_num + + ;
if ( mirror_num > num_copies )
break ;
}
2016-07-15 14:46:08 +00:00
return 0 ;
2011-10-28 02:02:19 +00:00
}
2015-06-10 22:04:19 +00:00
static void print_usage ( int ret )
2011-10-28 02:02:19 +00:00
{
2016-09-03 18:30:50 +00:00
printf ( " usage: btrfs-corrupt-block [options] device \n " ) ;
2022-07-27 21:30:24 +00:00
printf ( " \n " ) ;
printf ( " Corrupt data structures on a btrfs filesystem. For testing only! \n " ) ;
printf ( " \n " ) ;
printf ( " -l EXTENT \n " ) ;
printf ( " --logical EXTENT logical extent to be corrupted \n " ) ;
printf ( " -c COPY \n " ) ;
printf ( " --copy COPY copy of the extent to be corrupted (usually 1 or 2, default: 0) \n " ) ;
printf ( " -b COUNT \n " ) ;
printf ( " --bytes COUNT number of bytes to be corrupted \n " ) ;
printf ( " -e \n " ) ;
printf ( " --extent-record corrupt the extent \n " ) ;
printf ( " -E \n " ) ;
printf ( " --extent-tree corrupt the whole extent tree \n " ) ;
printf ( " -u \n " ) ;
printf ( " --chunk-record corrupt the given chunk \n " ) ;
printf ( " -U \n " ) ;
printf ( " --chunk-tree corrupt the whole whole chunk tree \n " ) ;
printf ( " -i INODE \n " ) ;
printf ( " --inode INODE inode number to corrupt (must also specify the field to corrupt) \n " ) ;
printf ( " -x EXTENT \n " ) ;
printf ( " --file-extent EXTENT file extent item to corrupt (must also specify -i for the inode and -f for the field to corrupt) \n " ) ;
printf ( " -m BLOCK \n " ) ;
printf ( " --metadata-block BLOCK \n " ) ;
printf ( " metadata block to corrupt (must also specify -f for the field to corrupt) \n " ) ;
printf ( " -k \n " ) ;
printf ( " --keys corrupt block keys (set by --logical) \n " ) ;
printf ( " -K <u64,u8,u64> \n " ) ;
printf ( " --key <u64,u8,u64> corrupt the given key (must also specify -f for the field and optionally -r for the root) \n " ) ;
printf ( " -f FIELD \n " ) ;
printf ( " --field FIELD field name in the item to corrupt \n " ) ;
printf ( " -I \n " ) ;
printf ( " --item corrupt an item corresponding to the passed key triplet (must also specify the field, or a (bytes, offset, value) tuple to corrupt and root for the item) \n " ) ;
printf ( " -D \n " ) ;
printf ( " --dir-item corrupt a dir item corresponding to the passed key triplet, must also specify a field \n " ) ;
printf ( " -d \n " ) ;
printf ( " --delete delete item corresponding to passed key triplet \n " ) ;
printf ( " -r \n " ) ;
printf ( " --root operate on this root \n " ) ;
printf ( " -C BYTENR \n " ) ;
printf ( " --csum BYTENR delete a csum for the specified bytenr. When used with -b it'll delete that many bytes, otherwise it's just sectorsize \n " ) ;
printf ( " --block-group OFFSET corrupt the given block group \n " ) ;
printf ( " --value VALUE value to use for corrupting item data \n " ) ;
printf ( " --offset OFFSET offset to use for corrupting item data \n " ) ;
2015-06-10 22:04:19 +00:00
exit ( ret ) ;
2011-10-28 02:02:19 +00:00
}
2012-02-21 19:37:21 +00:00
static void corrupt_keys ( struct btrfs_trans_handle * trans ,
2017-05-18 02:36:19 +00:00
struct btrfs_fs_info * fs_info ,
2012-02-21 19:37:21 +00:00
struct extent_buffer * eb )
{
int slot ;
int bad_slot ;
int nr ;
struct btrfs_disk_key bad_key ; ;
nr = btrfs_header_nritems ( eb ) ;
if ( nr = = 0 )
return ;
2016-05-26 02:56:51 +00:00
slot = rand_range ( nr ) ;
bad_slot = rand_range ( nr ) ;
2012-02-21 19:37:21 +00:00
if ( bad_slot = = slot )
return ;
2013-06-07 02:25:59 +00:00
fprintf ( stderr ,
" corrupting keys in block %llu slot %d swapping with %d \n " ,
2012-02-21 19:37:21 +00:00
( unsigned long long ) eb - > start , slot , bad_slot ) ;
if ( btrfs_header_level ( eb ) = = 0 ) {
btrfs_item_key ( eb , & bad_key , bad_slot ) ;
btrfs_set_item_key ( eb , & bad_key , slot ) ;
} else {
btrfs_node_key ( eb , & bad_key , bad_slot ) ;
btrfs_set_node_key ( eb , & bad_key , slot ) ;
}
btrfs_mark_buffer_dirty ( eb ) ;
if ( ! trans ) {
2021-10-21 01:40:20 +00:00
u16 csum_size = fs_info - > csum_size ;
u16 csum_type = fs_info - > csum_type ;
2019-09-03 15:00:37 +00:00
csum_tree_block_size ( eb , csum_size , 0 , csum_type ) ;
2022-04-05 12:48:25 +00:00
write_and_map_eb ( eb - > fs_info , eb ) ;
2012-02-21 19:37:21 +00:00
}
}
2017-05-18 02:36:19 +00:00
static int corrupt_keys_in_block ( struct btrfs_fs_info * fs_info , u64 bytenr )
2012-02-21 19:37:21 +00:00
{
struct extent_buffer * eb ;
2017-08-25 14:54:16 +00:00
eb = read_tree_block ( fs_info , bytenr , 0 ) ;
2015-01-28 02:12:55 +00:00
if ( ! extent_buffer_uptodate ( eb ) )
2012-02-21 19:37:21 +00:00
return - EIO ; ;
2017-05-18 02:36:19 +00:00
corrupt_keys ( NULL , fs_info , eb ) ;
2012-02-21 19:37:21 +00:00
free_extent_buffer ( eb ) ;
return 0 ;
}
2012-02-07 13:36:38 +00:00
static int corrupt_extent ( struct btrfs_trans_handle * trans ,
2017-02-09 16:42:02 +00:00
struct btrfs_root * root , u64 bytenr )
2012-02-06 13:54:05 +00:00
{
2021-11-08 19:26:41 +00:00
struct btrfs_root * extent_root ;
2012-02-06 13:54:05 +00:00
struct btrfs_key key ;
struct extent_buffer * leaf ;
u32 item_size ;
unsigned long ptr ;
struct btrfs_path * path ;
int ret ;
int slot ;
2016-05-26 02:56:51 +00:00
int should_del = rand_range ( 3 ) ;
2012-02-06 13:54:05 +00:00
path = btrfs_alloc_path ( ) ;
2013-07-30 11:09:55 +00:00
if ( ! path )
return - ENOMEM ;
2012-02-06 13:54:05 +00:00
key . objectid = bytenr ;
key . type = ( u8 ) - 1 ;
key . offset = ( u64 ) - 1 ;
2021-11-08 19:26:41 +00:00
extent_root = btrfs_extent_root ( trans - > fs_info , bytenr ) ;
2012-02-06 13:54:05 +00:00
while ( 1 ) {
2021-11-08 19:26:41 +00:00
ret = btrfs_search_slot ( trans , extent_root , & key , path , - 1 , 1 ) ;
2012-02-06 13:54:05 +00:00
if ( ret < 0 )
break ;
if ( ret > 0 ) {
if ( path - > slots [ 0 ] = = 0 )
break ;
path - > slots [ 0 ] - - ;
2012-02-07 13:36:38 +00:00
ret = 0 ;
2012-02-06 13:54:05 +00:00
}
leaf = path - > nodes [ 0 ] ;
slot = path - > slots [ 0 ] ;
btrfs_item_key_to_cpu ( leaf , & key , slot ) ;
if ( key . objectid ! = bytenr )
break ;
if ( key . type ! = BTRFS_EXTENT_ITEM_KEY & &
2016-05-26 02:36:02 +00:00
key . type ! = BTRFS_METADATA_ITEM_KEY & &
2012-02-06 13:54:05 +00:00
key . type ! = BTRFS_TREE_BLOCK_REF_KEY & &
key . type ! = BTRFS_EXTENT_DATA_REF_KEY & &
key . type ! = BTRFS_EXTENT_REF_V0_KEY & &
key . type ! = BTRFS_SHARED_BLOCK_REF_KEY & &
key . type ! = BTRFS_SHARED_DATA_REF_KEY )
goto next ;
2012-02-07 13:36:38 +00:00
if ( should_del ) {
2013-06-07 02:25:59 +00:00
fprintf ( stderr ,
" deleting extent record: key %llu %u %llu \n " ,
2012-02-07 13:36:38 +00:00
key . objectid , key . type , key . offset ) ;
2012-02-06 13:54:05 +00:00
2012-02-07 13:36:38 +00:00
if ( key . type = = BTRFS_EXTENT_ITEM_KEY ) {
/* make sure this extent doesn't get
* reused for other purposes */
btrfs_pin_extent ( root - > fs_info ,
key . objectid , key . offset ) ;
}
btrfs_del_item ( trans , root , path ) ;
} else {
2013-06-07 02:25:59 +00:00
fprintf ( stderr ,
" corrupting extent record: key %llu %u %llu \n " ,
2012-02-07 13:36:38 +00:00
key . objectid , key . type , key . offset ) ;
ptr = btrfs_item_ptr_offset ( leaf , slot ) ;
2022-02-22 22:26:20 +00:00
item_size = btrfs_item_size ( leaf , slot ) ;
2012-02-07 13:36:38 +00:00
memset_extent_buffer ( leaf , 0 , ptr , item_size ) ;
btrfs_mark_buffer_dirty ( leaf ) ;
}
2012-02-06 13:54:05 +00:00
next :
2013-08-03 00:52:43 +00:00
btrfs_release_path ( path ) ;
2012-02-06 13:54:05 +00:00
if ( key . offset > 0 )
key . offset - - ;
if ( key . offset = = 0 )
break ;
}
btrfs_free_path ( path ) ;
return 0 ;
}
2012-02-07 13:36:38 +00:00
static void btrfs_corrupt_extent_leaf ( struct btrfs_trans_handle * trans ,
2013-06-07 02:25:59 +00:00
struct btrfs_root * root ,
struct extent_buffer * eb )
2012-02-07 13:36:38 +00:00
{
u32 nr = btrfs_header_nritems ( eb ) ;
2016-05-26 02:56:51 +00:00
u32 victim = rand_range ( nr ) ;
2012-02-07 13:36:38 +00:00
u64 objectid ;
struct btrfs_key key ;
btrfs_item_key_to_cpu ( eb , & key , victim ) ;
objectid = key . objectid ;
2017-02-09 16:42:02 +00:00
corrupt_extent ( trans , root , objectid ) ;
2012-02-07 13:36:38 +00:00
}
static void btrfs_corrupt_extent_tree ( struct btrfs_trans_handle * trans ,
2013-06-07 02:25:59 +00:00
struct btrfs_root * root ,
struct extent_buffer * eb )
2012-02-07 13:36:38 +00:00
{
2017-05-18 02:51:08 +00:00
struct btrfs_fs_info * fs_info = root - > fs_info ;
2012-02-07 13:36:38 +00:00
int i ;
if ( ! eb )
return ;
if ( btrfs_is_leaf ( eb ) ) {
btrfs_corrupt_extent_leaf ( trans , root , eb ) ;
return ;
}
if ( btrfs_header_level ( eb ) = = 1 & & eb ! = root - > node ) {
2016-05-26 02:56:51 +00:00
if ( rand_range ( 5 ) )
2012-02-07 13:36:38 +00:00
return ;
}
2014-08-22 03:42:49 +00:00
for ( i = 0 ; i < btrfs_header_nritems ( eb ) ; i + + ) {
2012-02-07 13:36:38 +00:00
struct extent_buffer * next ;
2017-05-18 02:51:08 +00:00
next = read_tree_block ( fs_info , btrfs_node_blockptr ( eb , i ) ,
2013-06-07 02:25:59 +00:00
btrfs_node_ptr_generation ( eb , i ) ) ;
2015-01-28 02:12:55 +00:00
if ( ! extent_buffer_uptodate ( next ) )
2012-02-07 13:36:38 +00:00
continue ;
btrfs_corrupt_extent_tree ( trans , root , next ) ;
free_extent_buffer ( next ) ;
}
}
2013-09-09 20:41:54 +00:00
enum btrfs_inode_field {
BTRFS_INODE_FIELD_ISIZE ,
2015-07-03 16:07:17 +00:00
BTRFS_INODE_FIELD_NBYTES ,
2017-01-05 10:08:32 +00:00
BTRFS_INODE_FIELD_NLINK ,
2017-01-05 17:03:58 +00:00
BTRFS_INODE_FIELD_GENERATION ,
BTRFS_INODE_FIELD_TRANSID ,
BTRFS_INODE_FIELD_BLOCK_GROUP ,
BTRFS_INODE_FIELD_MODE ,
BTRFS_INODE_FIELD_UID ,
BTRFS_INODE_FIELD_GID ,
2013-09-09 20:41:54 +00:00
BTRFS_INODE_FIELD_BAD ,
} ;
2013-09-09 20:41:57 +00:00
enum btrfs_file_extent_field {
BTRFS_FILE_EXTENT_DISK_BYTENR ,
2022-07-26 20:43:24 +00:00
BTRFS_FILE_EXTENT_TYPE ,
2013-09-09 20:41:57 +00:00
BTRFS_FILE_EXTENT_BAD ,
} ;
2014-10-10 20:57:16 +00:00
enum btrfs_dir_item_field {
BTRFS_DIR_ITEM_NAME ,
BTRFS_DIR_ITEM_LOCATION_OBJECTID ,
BTRFS_DIR_ITEM_BAD ,
} ;
2013-10-01 12:54:58 +00:00
enum btrfs_metadata_block_field {
BTRFS_METADATA_BLOCK_GENERATION ,
2014-10-03 14:38:41 +00:00
BTRFS_METADATA_BLOCK_SHIFT_ITEMS ,
2013-10-01 12:54:58 +00:00
BTRFS_METADATA_BLOCK_BAD ,
} ;
2014-10-02 19:12:20 +00:00
enum btrfs_item_field {
BTRFS_ITEM_OFFSET ,
BTRFS_ITEM_BAD ,
} ;
2014-01-07 20:19:35 +00:00
enum btrfs_key_field {
BTRFS_KEY_OBJECTID ,
BTRFS_KEY_TYPE ,
BTRFS_KEY_OFFSET ,
BTRFS_KEY_BAD ,
} ;
2021-08-18 04:39:20 +00:00
enum btrfs_block_group_field {
BTRFS_BLOCK_GROUP_ITEM_USED ,
BTRFS_BLOCK_GROUP_ITEM_FLAGS ,
BTRFS_BLOCK_GROUP_ITEM_CHUNK_OBJECTID ,
BTRFS_BLOCK_GROUP_ITEM_BAD ,
} ;
static enum btrfs_block_group_field convert_block_group_field ( char * field )
{
if ( ! strncmp ( field , " used " , FIELD_BUF_LEN ) )
return BTRFS_BLOCK_GROUP_ITEM_USED ;
if ( ! strncmp ( field , " flags " , FIELD_BUF_LEN ) )
return BTRFS_BLOCK_GROUP_ITEM_FLAGS ;
if ( ! strncmp ( field , " chunk_objectid " , FIELD_BUF_LEN ) )
return BTRFS_BLOCK_GROUP_ITEM_CHUNK_OBJECTID ;
return BTRFS_BLOCK_GROUP_ITEM_BAD ;
}
2013-09-09 20:41:57 +00:00
static enum btrfs_inode_field convert_inode_field ( char * field )
2013-09-09 20:41:54 +00:00
{
if ( ! strncmp ( field , " isize " , FIELD_BUF_LEN ) )
return BTRFS_INODE_FIELD_ISIZE ;
2015-07-03 16:07:17 +00:00
if ( ! strncmp ( field , " nbytes " , FIELD_BUF_LEN ) )
return BTRFS_INODE_FIELD_NBYTES ;
2017-01-05 10:08:32 +00:00
if ( ! strncmp ( field , " nlink " , FIELD_BUF_LEN ) )
return BTRFS_INODE_FIELD_NLINK ;
2017-01-05 17:03:58 +00:00
if ( ! strncmp ( field , " generation " , FIELD_BUF_LEN ) )
return BTRFS_INODE_FIELD_GENERATION ;
if ( ! strncmp ( field , " transid " , FIELD_BUF_LEN ) )
return BTRFS_INODE_FIELD_TRANSID ;
if ( ! strncmp ( field , " block_group " , FIELD_BUF_LEN ) )
return BTRFS_INODE_FIELD_BLOCK_GROUP ;
if ( ! strncmp ( field , " mode " , FIELD_BUF_LEN ) )
return BTRFS_INODE_FIELD_MODE ;
if ( ! strncmp ( field , " uid " , FIELD_BUF_LEN ) )
return BTRFS_INODE_FIELD_UID ;
if ( ! strncmp ( field , " gid " , FIELD_BUF_LEN ) )
return BTRFS_INODE_FIELD_GID ;
2013-09-09 20:41:54 +00:00
return BTRFS_INODE_FIELD_BAD ;
}
2013-09-09 20:41:57 +00:00
static enum btrfs_file_extent_field convert_file_extent_field ( char * field )
{
if ( ! strncmp ( field , " disk_bytenr " , FIELD_BUF_LEN ) )
return BTRFS_FILE_EXTENT_DISK_BYTENR ;
2022-07-26 20:43:24 +00:00
if ( ! strncmp ( field , " type " , FIELD_BUF_LEN ) )
return BTRFS_FILE_EXTENT_TYPE ;
2013-09-09 20:41:57 +00:00
return BTRFS_FILE_EXTENT_BAD ;
}
2013-10-01 12:54:58 +00:00
static enum btrfs_metadata_block_field
convert_metadata_block_field ( char * field )
{
if ( ! strncmp ( field , " generation " , FIELD_BUF_LEN ) )
return BTRFS_METADATA_BLOCK_GENERATION ;
2014-10-03 14:38:41 +00:00
if ( ! strncmp ( field , " shift_items " , FIELD_BUF_LEN ) )
return BTRFS_METADATA_BLOCK_SHIFT_ITEMS ;
2013-10-01 12:54:58 +00:00
return BTRFS_METADATA_BLOCK_BAD ;
}
2014-01-07 20:19:35 +00:00
static enum btrfs_key_field convert_key_field ( char * field )
{
if ( ! strncmp ( field , " objectid " , FIELD_BUF_LEN ) )
return BTRFS_KEY_OBJECTID ;
if ( ! strncmp ( field , " type " , FIELD_BUF_LEN ) )
return BTRFS_KEY_TYPE ;
if ( ! strncmp ( field , " offset " , FIELD_BUF_LEN ) )
return BTRFS_KEY_OFFSET ;
return BTRFS_KEY_BAD ;
}
2014-10-02 19:12:20 +00:00
static enum btrfs_item_field convert_item_field ( char * field )
{
if ( ! strncmp ( field , " offset " , FIELD_BUF_LEN ) )
return BTRFS_ITEM_OFFSET ;
return BTRFS_ITEM_BAD ;
}
2014-10-10 20:57:16 +00:00
static enum btrfs_dir_item_field convert_dir_item_field ( char * field )
{
if ( ! strncmp ( field , " name " , FIELD_BUF_LEN ) )
return BTRFS_DIR_ITEM_NAME ;
if ( ! strncmp ( field , " location_objectid " , FIELD_BUF_LEN ) )
return BTRFS_DIR_ITEM_LOCATION_OBJECTID ;
return BTRFS_DIR_ITEM_BAD ;
}
2013-09-09 20:41:57 +00:00
static u64 generate_u64 ( u64 orig )
{
u64 ret ;
do {
2016-05-26 02:56:51 +00:00
ret = rand_u64 ( ) ;
2013-09-09 20:41:57 +00:00
} while ( ret = = orig ) ;
return ret ;
}
2014-10-02 19:12:20 +00:00
static u32 generate_u32 ( u32 orig )
{
u32 ret ;
do {
2016-05-26 02:56:51 +00:00
ret = rand_u32 ( ) ;
2014-10-02 19:12:20 +00:00
} while ( ret = = orig ) ;
return ret ;
}
2014-01-07 20:19:35 +00:00
static u8 generate_u8 ( u8 orig )
{
u8 ret ;
do {
2016-05-26 02:56:51 +00:00
ret = rand_u8 ( ) ;
2014-01-07 20:19:35 +00:00
} while ( ret = = orig ) ;
return ret ;
}
2021-08-18 04:39:20 +00:00
static int corrupt_block_group ( struct btrfs_root * root , u64 bg , char * field )
{
struct btrfs_trans_handle * trans ;
struct btrfs_path * path ;
struct btrfs_block_group_item * bgi ;
struct btrfs_key key ;
enum btrfs_block_group_field corrupt_field ;
u64 orig , bogus ;
int ret = 0 ;
2021-11-08 19:26:41 +00:00
root = btrfs_extent_root ( root - > fs_info , 0 ) ;
2021-08-18 04:39:20 +00:00
corrupt_field = convert_block_group_field ( field ) ;
if ( corrupt_field = = BTRFS_BLOCK_GROUP_ITEM_BAD ) {
fprintf ( stderr , " Invalid field %s \n " , field ) ;
return - EINVAL ;
}
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
trans = btrfs_start_transaction ( root , 1 ) ;
if ( IS_ERR ( trans ) ) {
btrfs_free_path ( path ) ;
fprintf ( stderr , " Couldn't start transaction %ld \n " ,
PTR_ERR ( trans ) ) ;
return PTR_ERR ( trans ) ;
}
key . objectid = bg ;
key . type = BTRFS_BLOCK_GROUP_ITEM_KEY ;
key . offset = 0 ;
ret = btrfs_search_slot ( trans , root , & key , path , 0 , 1 ) ;
if ( ret < 0 ) {
fprintf ( stderr , " Error searching for bg %llu %d \n " , bg , ret ) ;
goto out ;
}
ret = 0 ;
btrfs_item_key_to_cpu ( path - > nodes [ 0 ] , & key , path - > slots [ 0 ] ) ;
if ( key . type ! = BTRFS_BLOCK_GROUP_ITEM_KEY ) {
fprintf ( stderr , " Couldn't find the bg %llu \n " , bg ) ;
goto out ;
}
bgi = btrfs_item_ptr ( path - > nodes [ 0 ] , path - > slots [ 0 ] ,
struct btrfs_block_group_item ) ;
switch ( corrupt_field ) {
case BTRFS_BLOCK_GROUP_ITEM_USED :
orig = btrfs_block_group_used ( path - > nodes [ 0 ] , bgi ) ;
bogus = generate_u64 ( orig ) ;
btrfs_set_block_group_used ( path - > nodes [ 0 ] , bgi , bogus ) ;
break ;
case BTRFS_BLOCK_GROUP_ITEM_CHUNK_OBJECTID :
orig = btrfs_block_group_chunk_objectid ( path - > nodes [ 0 ] , bgi ) ;
bogus = generate_u64 ( orig ) ;
btrfs_set_block_group_chunk_objectid ( path - > nodes [ 0 ] , bgi ,
bogus ) ;
break ;
case BTRFS_BLOCK_GROUP_ITEM_FLAGS :
orig = btrfs_block_group_flags ( path - > nodes [ 0 ] , bgi ) ;
bogus = generate_u64 ( orig ) ;
btrfs_set_block_group_flags ( path - > nodes [ 0 ] , bgi , bogus ) ;
break ;
default :
ret = - EINVAL ;
goto out ;
}
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
out :
btrfs_commit_transaction ( trans , root ) ;
btrfs_free_path ( path ) ;
return ret ;
}
2014-01-07 20:19:35 +00:00
static int corrupt_key ( struct btrfs_root * root , struct btrfs_key * key ,
char * field )
{
enum btrfs_key_field corrupt_field = convert_key_field ( field ) ;
struct btrfs_path * path ;
struct btrfs_trans_handle * trans ;
int ret ;
if ( corrupt_field = = BTRFS_KEY_BAD ) {
fprintf ( stderr , " Invalid field %s \n " , field ) ;
return - EINVAL ;
}
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
trans = btrfs_start_transaction ( root , 1 ) ;
if ( IS_ERR ( trans ) ) {
btrfs_free_path ( path ) ;
return PTR_ERR ( trans ) ;
}
ret = btrfs_search_slot ( trans , root , key , path , 0 , 1 ) ;
if ( ret < 0 )
goto out ;
if ( ret > 0 ) {
fprintf ( stderr , " Couldn't find the key to corrupt \n " ) ;
ret = - ENOENT ;
goto out ;
}
switch ( corrupt_field ) {
case BTRFS_KEY_OBJECTID :
key - > objectid = generate_u64 ( key - > objectid ) ;
break ;
case BTRFS_KEY_TYPE :
key - > type = generate_u8 ( key - > type ) ;
break ;
case BTRFS_KEY_OFFSET :
key - > offset = generate_u64 ( key - > objectid ) ;
break ;
default :
fprintf ( stderr , " Invalid field %s, %d \n " , field ,
corrupt_field ) ;
ret = - EINVAL ;
goto out ;
}
btrfs_set_item_key_unsafe ( root , path , key ) ;
out :
btrfs_free_path ( path ) ;
btrfs_commit_transaction ( trans , root ) ;
return ret ;
}
2014-10-10 20:57:16 +00:00
static int corrupt_dir_item ( struct btrfs_root * root , struct btrfs_key * key ,
char * field )
{
struct btrfs_trans_handle * trans ;
struct btrfs_dir_item * di ;
struct btrfs_path * path ;
2015-11-06 17:27:55 +00:00
char name [ PATH_MAX ] ;
2014-10-10 20:57:16 +00:00
struct btrfs_key location ;
struct btrfs_disk_key disk_key ;
unsigned long name_ptr ;
enum btrfs_dir_item_field corrupt_field =
convert_dir_item_field ( field ) ;
u64 bogus ;
u16 name_len ;
int ret ;
if ( corrupt_field = = BTRFS_DIR_ITEM_BAD ) {
fprintf ( stderr , " Invalid field %s \n " , field ) ;
return - EINVAL ;
}
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
trans = btrfs_start_transaction ( root , 1 ) ;
if ( IS_ERR ( trans ) ) {
btrfs_free_path ( path ) ;
return PTR_ERR ( trans ) ;
}
ret = btrfs_search_slot ( trans , root , key , path , 0 , 1 ) ;
if ( ret ) {
if ( ret > 0 )
ret = - ENOENT ;
fprintf ( stderr , " Error searching for dir item %d \n " , ret ) ;
goto out ;
}
di = btrfs_item_ptr ( path - > nodes [ 0 ] , path - > slots [ 0 ] ,
struct btrfs_dir_item ) ;
switch ( corrupt_field ) {
case BTRFS_DIR_ITEM_NAME :
name_len = btrfs_dir_name_len ( path - > nodes [ 0 ] , di ) ;
name_ptr = ( unsigned long ) ( di + 1 ) ;
read_extent_buffer ( path - > nodes [ 0 ] , name , name_ptr , name_len ) ;
name [ 0 ] + + ;
write_extent_buffer ( path - > nodes [ 0 ] , name , name_ptr , name_len ) ;
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
goto out ;
case BTRFS_DIR_ITEM_LOCATION_OBJECTID :
btrfs_dir_item_key_to_cpu ( path - > nodes [ 0 ] , di , & location ) ;
bogus = generate_u64 ( location . objectid ) ;
location . objectid = bogus ;
btrfs_cpu_key_to_disk ( & disk_key , & location ) ;
btrfs_set_dir_item_key ( path - > nodes [ 0 ] , di , & disk_key ) ;
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
goto out ;
default :
ret = - EINVAL ;
goto out ;
}
out :
btrfs_commit_transaction ( trans , root ) ;
btrfs_free_path ( path ) ;
return ret ;
}
2014-01-07 20:19:35 +00:00
2013-09-09 20:41:54 +00:00
static int corrupt_inode ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root , u64 inode , char * field )
{
struct btrfs_inode_item * ei ;
struct btrfs_path * path ;
struct btrfs_key key ;
2013-09-09 20:41:57 +00:00
enum btrfs_inode_field corrupt_field = convert_inode_field ( field ) ;
2013-09-09 20:41:54 +00:00
u64 bogus ;
u64 orig ;
int ret ;
if ( corrupt_field = = BTRFS_INODE_FIELD_BAD ) {
fprintf ( stderr , " Invalid field %s \n " , field ) ;
return - EINVAL ;
}
key . objectid = inode ;
key . type = BTRFS_INODE_ITEM_KEY ;
key . offset = ( u64 ) - 1 ;
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
ret = btrfs_search_slot ( trans , root , & key , path , 0 , 1 ) ;
if ( ret < 0 )
goto out ;
if ( ret ) {
if ( ! path - > slots [ 0 ] ) {
2021-06-11 21:07:50 +00:00
fprintf ( stderr , " Couldn't find inode %llu \n " , inode ) ;
2013-09-09 20:41:54 +00:00
ret = - ENOENT ;
goto out ;
}
path - > slots [ 0 ] - - ;
ret = 0 ;
}
btrfs_item_key_to_cpu ( path - > nodes [ 0 ] , & key , path - > slots [ 0 ] ) ;
if ( key . objectid ! = inode ) {
2021-06-11 21:07:50 +00:00
fprintf ( stderr , " Couldn't find inode %llu \n " , inode ) ;
2013-09-09 20:41:54 +00:00
ret = - ENOENT ;
goto out ;
}
ei = btrfs_item_ptr ( path - > nodes [ 0 ] , path - > slots [ 0 ] ,
struct btrfs_inode_item ) ;
switch ( corrupt_field ) {
case BTRFS_INODE_FIELD_ISIZE :
orig = btrfs_inode_size ( path - > nodes [ 0 ] , ei ) ;
2013-09-09 20:41:57 +00:00
bogus = generate_u64 ( orig ) ;
2013-09-09 20:41:54 +00:00
btrfs_set_inode_size ( path - > nodes [ 0 ] , ei , bogus ) ;
break ;
2015-07-03 16:07:17 +00:00
case BTRFS_INODE_FIELD_NBYTES :
orig = btrfs_inode_nbytes ( path - > nodes [ 0 ] , ei ) ;
bogus = generate_u64 ( orig ) ;
btrfs_set_inode_nbytes ( path - > nodes [ 0 ] , ei , bogus ) ;
break ;
2017-01-05 10:08:32 +00:00
case BTRFS_INODE_FIELD_NLINK :
orig = btrfs_inode_nlink ( path - > nodes [ 0 ] , ei ) ;
bogus = generate_u32 ( orig ) ;
btrfs_set_inode_nlink ( path - > nodes [ 0 ] , ei , bogus ) ;
break ;
2017-01-05 17:03:58 +00:00
case BTRFS_INODE_FIELD_GENERATION :
orig = btrfs_inode_generation ( path - > nodes [ 0 ] , ei ) ;
bogus = generate_u64 ( orig ) ;
btrfs_set_inode_generation ( path - > nodes [ 0 ] , ei , bogus ) ;
break ;
case BTRFS_INODE_FIELD_TRANSID :
orig = btrfs_inode_transid ( path - > nodes [ 0 ] , ei ) ;
bogus = generate_u64 ( orig ) ;
btrfs_set_inode_transid ( path - > nodes [ 0 ] , ei , bogus ) ;
break ;
case BTRFS_INODE_FIELD_BLOCK_GROUP :
orig = btrfs_inode_block_group ( path - > nodes [ 0 ] , ei ) ;
bogus = generate_u64 ( orig ) ;
btrfs_set_inode_block_group ( path - > nodes [ 0 ] , ei , bogus ) ;
break ;
case BTRFS_INODE_FIELD_MODE :
orig = btrfs_inode_mode ( path - > nodes [ 0 ] , ei ) ;
bogus = generate_u32 ( orig ) ;
btrfs_set_inode_mode ( path - > nodes [ 0 ] , ei , bogus ) ;
break ;
case BTRFS_INODE_FIELD_UID :
orig = btrfs_inode_uid ( path - > nodes [ 0 ] , ei ) ;
bogus = generate_u32 ( orig ) ;
btrfs_set_inode_uid ( path - > nodes [ 0 ] , ei , bogus ) ;
break ;
case BTRFS_INODE_FIELD_GID :
orig = btrfs_inode_gid ( path - > nodes [ 0 ] , ei ) ;
bogus = generate_u32 ( orig ) ;
btrfs_set_inode_gid ( path - > nodes [ 0 ] , ei , bogus ) ;
break ;
2013-09-09 20:41:54 +00:00
default :
ret = - EINVAL ;
break ;
}
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
out :
btrfs_free_path ( path ) ;
return ret ;
}
2013-09-09 20:41:57 +00:00
static int corrupt_file_extent ( struct btrfs_trans_handle * trans ,
struct btrfs_root * root , u64 inode , u64 extent ,
2022-07-26 20:43:24 +00:00
char * field , u64 bogus )
2013-09-09 20:41:57 +00:00
{
struct btrfs_file_extent_item * fi ;
struct btrfs_path * path ;
struct btrfs_key key ;
enum btrfs_file_extent_field corrupt_field ;
u64 orig ;
int ret = 0 ;
corrupt_field = convert_file_extent_field ( field ) ;
if ( corrupt_field = = BTRFS_FILE_EXTENT_BAD ) {
fprintf ( stderr , " Invalid field %s \n " , field ) ;
return - EINVAL ;
}
key . objectid = inode ;
key . type = BTRFS_EXTENT_DATA_KEY ;
key . offset = extent ;
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
ret = btrfs_search_slot ( trans , root , & key , path , 0 , 1 ) ;
if ( ret < 0 )
goto out ;
if ( ret ) {
fprintf ( stderr , " Couldn't find extent %llu for inode %llu \n " ,
extent , inode ) ;
ret = - ENOENT ;
goto out ;
}
fi = btrfs_item_ptr ( path - > nodes [ 0 ] , path - > slots [ 0 ] ,
struct btrfs_file_extent_item ) ;
switch ( corrupt_field ) {
case BTRFS_FILE_EXTENT_DISK_BYTENR :
orig = btrfs_file_extent_disk_bytenr ( path - > nodes [ 0 ] , fi ) ;
2022-07-26 20:43:24 +00:00
bogus = ( bogus = = ( u64 ) - 1 ) ? generate_u64 ( orig ) : bogus ;
2013-09-09 20:41:57 +00:00
btrfs_set_file_extent_disk_bytenr ( path - > nodes [ 0 ] , fi , bogus ) ;
break ;
2022-07-26 20:43:24 +00:00
case BTRFS_FILE_EXTENT_TYPE :
if ( bogus = = ( u64 ) - 1 ) {
fprintf ( stderr , " Specify a new extent type value (-v) \n " ) ;
ret = - EINVAL ;
goto out ;
}
btrfs_set_file_extent_type ( path - > nodes [ 0 ] , fi , ( u8 ) bogus ) ;
break ;
2013-09-09 20:41:57 +00:00
default :
ret = - EINVAL ;
break ;
}
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
out :
btrfs_free_path ( path ) ;
return ret ;
}
2014-10-03 14:38:41 +00:00
static void shift_items ( struct btrfs_root * root , struct extent_buffer * eb )
{
int nritems = btrfs_header_nritems ( eb ) ;
2018-04-30 03:15:43 +00:00
int shift_space = btrfs_leaf_free_space ( eb ) / 2 ;
2014-10-03 14:38:41 +00:00
int slot = nritems / 2 ;
int i = 0 ;
2022-02-22 22:26:20 +00:00
unsigned int data_end = btrfs_item_offset ( eb , nritems - 1 ) ;
2014-10-03 14:38:41 +00:00
/* Shift the item data up to and including slot back by shift space */
memmove_extent_buffer ( eb , btrfs_leaf_data ( eb ) + data_end - shift_space ,
btrfs_leaf_data ( eb ) + data_end ,
2022-02-22 22:26:20 +00:00
btrfs_item_offset ( eb , slot - 1 ) - data_end ) ;
2014-10-03 14:38:41 +00:00
/* Now update the item pointers. */
for ( i = nritems - 1 ; i > = slot ; i - - ) {
2022-02-22 22:26:20 +00:00
u32 offset = btrfs_item_offset ( eb , i ) ;
2014-10-03 14:38:41 +00:00
offset - = shift_space ;
2022-02-22 22:26:20 +00:00
btrfs_set_item_offset ( eb , i , offset ) ;
2014-10-03 14:38:41 +00:00
}
}
2017-05-18 02:36:19 +00:00
static int corrupt_metadata_block ( struct btrfs_fs_info * fs_info , u64 block ,
2013-10-01 12:54:58 +00:00
char * field )
{
struct extent_buffer * eb ;
enum btrfs_metadata_block_field corrupt_field ;
int ret ;
corrupt_field = convert_metadata_block_field ( field ) ;
if ( corrupt_field = = BTRFS_METADATA_BLOCK_BAD ) {
fprintf ( stderr , " Invalid field %s \n " , field ) ;
return - EINVAL ;
}
2017-08-25 14:54:16 +00:00
eb = read_tree_block ( fs_info , block , 0 ) ;
2015-01-28 02:12:55 +00:00
if ( ! extent_buffer_uptodate ( eb ) ) {
2013-10-01 12:54:58 +00:00
fprintf ( stderr , " Couldn't read in tree block %s \n " , field ) ;
return - EINVAL ;
}
ret = 0 ;
switch ( corrupt_field ) {
case BTRFS_METADATA_BLOCK_GENERATION :
2019-10-15 15:42:48 +00:00
{
u64 orig = btrfs_header_generation ( eb ) ;
u64 bogus = generate_u64 ( orig ) ;
2013-10-01 12:54:58 +00:00
btrfs_set_header_generation ( eb , bogus ) ;
2022-09-13 07:19:26 +00:00
csum_tree_block_size ( eb , fs_info - > csum_size , 0 ,
fs_info - > csum_type ) ;
2020-03-02 04:55:09 +00:00
ret = write_and_map_eb ( fs_info , eb ) ;
2019-10-15 15:42:48 +00:00
free_extent_buffer ( eb ) ;
2020-03-02 04:55:09 +00:00
if ( ret < 0 ) {
errno = - ret ;
fprintf ( stderr ,
" failed to write extent buffer at %llu: %m " ,
eb - > start ) ;
return ret ;
}
2013-10-01 12:54:58 +00:00
break ;
2019-10-15 15:42:48 +00:00
}
2014-10-03 14:38:41 +00:00
case BTRFS_METADATA_BLOCK_SHIFT_ITEMS :
2019-10-15 15:42:48 +00:00
{
struct btrfs_trans_handle * trans ;
struct btrfs_root * root ;
struct btrfs_path * path ;
struct btrfs_key key , root_key ;
u64 root_objectid ;
u8 level ;
root_objectid = btrfs_header_owner ( eb ) ;
level = btrfs_header_level ( eb ) ;
if ( level )
btrfs_node_key_to_cpu ( eb , & key , 0 ) ;
else
btrfs_item_key_to_cpu ( eb , & key , 0 ) ;
free_extent_buffer ( eb ) ;
root_key . objectid = root_objectid ;
root_key . type = BTRFS_ROOT_ITEM_KEY ;
root_key . offset = ( u64 ) - 1 ;
root = btrfs_read_fs_root ( fs_info , & root_key ) ;
if ( IS_ERR ( root ) ) {
fprintf ( stderr , " Couldn't find owner root %llu \n " ,
key . objectid ) ;
return PTR_ERR ( root ) ;
}
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
trans = btrfs_start_transaction ( root , 1 ) ;
if ( IS_ERR ( trans ) ) {
btrfs_free_path ( path ) ;
fprintf ( stderr , " Couldn't start transaction %ld \n " ,
PTR_ERR ( trans ) ) ;
return PTR_ERR ( trans ) ;
}
path - > lowest_level = level ;
ret = btrfs_search_slot ( trans , root , & key , path , 0 , 1 ) ;
if ( ret < 0 ) {
fprintf ( stderr , " Error searching to node %d \n " , ret ) ;
btrfs_free_path ( path ) ;
btrfs_abort_transaction ( trans , ret ) ;
return ret ;
}
eb = path - > nodes [ level ] ;
2014-10-03 14:38:41 +00:00
shift_items ( root , path - > nodes [ level ] ) ;
2019-10-15 15:42:48 +00:00
btrfs_mark_buffer_dirty ( path - > nodes [ level ] ) ;
btrfs_commit_transaction ( trans , root ) ;
2014-10-03 14:38:41 +00:00
break ;
2019-10-15 15:42:48 +00:00
}
2013-10-01 12:54:58 +00:00
default :
ret = - EINVAL ;
break ;
}
2019-10-15 15:42:48 +00:00
2013-10-01 12:54:58 +00:00
return ret ;
}
2014-10-02 19:12:20 +00:00
static int corrupt_btrfs_item ( struct btrfs_root * root , struct btrfs_key * key ,
char * field )
{
struct btrfs_trans_handle * trans ;
struct btrfs_path * path ;
enum btrfs_item_field corrupt_field ;
u32 orig , bogus ;
int ret ;
corrupt_field = convert_item_field ( field ) ;
if ( corrupt_field = = BTRFS_ITEM_BAD ) {
fprintf ( stderr , " Invalid field %s \n " , field ) ;
return - EINVAL ;
}
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
trans = btrfs_start_transaction ( root , 1 ) ;
if ( IS_ERR ( trans ) ) {
btrfs_free_path ( path ) ;
fprintf ( stderr , " Couldn't start transaction %ld \n " ,
PTR_ERR ( trans ) ) ;
return PTR_ERR ( trans ) ;
}
ret = btrfs_search_slot ( trans , root , key , path , 0 , 1 ) ;
if ( ret ! = 0 ) {
fprintf ( stderr , " Error searching to node %d \n " , ret ) ;
goto out ;
}
ret = 0 ;
switch ( corrupt_field ) {
case BTRFS_ITEM_OFFSET :
2022-02-22 22:26:20 +00:00
orig = btrfs_item_offset ( path - > nodes [ 0 ] , path - > slots [ 0 ] ) ;
2014-10-02 19:12:20 +00:00
bogus = generate_u32 ( orig ) ;
2022-02-22 22:26:20 +00:00
btrfs_set_item_offset ( path - > nodes [ 0 ] , path - > slots [ 0 ] , bogus ) ;
2014-10-02 19:12:20 +00:00
break ;
default :
ret = - EINVAL ;
break ;
}
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
out :
btrfs_commit_transaction ( trans , root ) ;
btrfs_free_path ( path ) ;
return ret ;
}
2022-07-26 20:43:23 +00:00
static int corrupt_btrfs_item_data ( struct btrfs_root * root ,
struct btrfs_key * key ,
u64 bogus_offset , u64 bogus_size ,
char bogus_value )
{
struct btrfs_trans_handle * trans ;
struct btrfs_path * path ;
int ret ;
void * data ;
struct extent_buffer * leaf ;
int slot ;
u32 item_size ;
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
trans = btrfs_start_transaction ( root , 1 ) ;
if ( IS_ERR ( trans ) ) {
fprintf ( stderr , " Couldn't start transaction %ld \n " ,
PTR_ERR ( trans ) ) ;
ret = PTR_ERR ( trans ) ;
goto free_path ;
}
ret = btrfs_search_slot ( trans , root , key , path , 0 , 1 ) ;
if ( ret ! = 0 ) {
fprintf ( stderr , " Error searching to node %d \n " , ret ) ;
goto commit_txn ;
}
leaf = path - > nodes [ 0 ] ;
slot = path - > slots [ 0 ] ;
data = btrfs_item_ptr ( leaf , slot , void ) ;
item_size = btrfs_item_size ( leaf , slot ) ;
if ( bogus_offset + bogus_size > item_size ) {
fprintf ( stderr , " Item corruption past end of item: %llu > %u \n " , bogus_offset + bogus_size , item_size ) ;
ret = - EINVAL ;
goto commit_txn ;
}
data + = bogus_offset ;
memset_extent_buffer ( leaf , bogus_value , ( unsigned long ) data , bogus_size ) ;
btrfs_mark_buffer_dirty ( leaf ) ;
commit_txn :
btrfs_commit_transaction ( trans , root ) ;
free_path :
btrfs_free_path ( path ) ;
return ret ;
}
2014-10-31 18:01:19 +00:00
static int delete_item ( struct btrfs_root * root , struct btrfs_key * key )
{
struct btrfs_trans_handle * trans ;
struct btrfs_path * path ;
int ret ;
path = btrfs_alloc_path ( ) ;
if ( ! path )
return - ENOMEM ;
trans = btrfs_start_transaction ( root , 1 ) ;
if ( IS_ERR ( trans ) ) {
btrfs_free_path ( path ) ;
fprintf ( stderr , " Couldn't start transaction %ld \n " ,
PTR_ERR ( trans ) ) ;
return PTR_ERR ( trans ) ;
}
ret = btrfs_search_slot ( trans , root , key , path , - 1 , 1 ) ;
if ( ret ) {
if ( ret > 0 )
ret = - ENOENT ;
fprintf ( stderr , " Error searching to node %d \n " , ret ) ;
goto out ;
}
ret = btrfs_del_item ( trans , root , path ) ;
btrfs_mark_buffer_dirty ( path - > nodes [ 0 ] ) ;
out :
btrfs_commit_transaction ( trans , root ) ;
btrfs_free_path ( path ) ;
return ret ;
}
2015-05-19 14:21:04 +00:00
static int delete_csum ( struct btrfs_root * root , u64 bytenr , u64 bytes )
{
struct btrfs_trans_handle * trans ;
int ret ;
2021-11-10 20:07:59 +00:00
root = btrfs_csum_root ( root - > fs_info , bytenr ) ;
2015-05-19 14:21:04 +00:00
trans = btrfs_start_transaction ( root , 1 ) ;
if ( IS_ERR ( trans ) ) {
fprintf ( stderr , " Couldn't start transaction %ld \n " ,
PTR_ERR ( trans ) ) ;
return PTR_ERR ( trans ) ;
}
2018-06-08 12:47:45 +00:00
ret = btrfs_del_csums ( trans , bytenr , bytes ) ;
2015-05-19 14:21:04 +00:00
if ( ret )
fprintf ( stderr , " Error deleting csums %d \n " , ret ) ;
btrfs_commit_transaction ( trans , root ) ;
return ret ;
}
2013-06-07 02:25:59 +00:00
/* corrupt item using NO cow.
2016-05-11 23:50:36 +00:00
* Because chunk recover will recover based on whole partition scanning ,
2013-06-07 02:25:59 +00:00
* If using COW , chunk recover will use the old item to recover ,
* which is still OK but we want to check the ability to rebuild chunk
* not only restore the old ones */
2015-09-14 21:39:28 +00:00
static int corrupt_item_nocow ( struct btrfs_trans_handle * trans ,
2013-06-07 02:25:59 +00:00
struct btrfs_root * root , struct btrfs_path * path ,
int del )
{
int ret = 0 ;
struct btrfs_key key ;
struct extent_buffer * leaf ;
unsigned long ptr ;
int slot ;
u32 item_size ;
2012-02-07 13:36:38 +00:00
2013-06-07 02:25:59 +00:00
leaf = path - > nodes [ 0 ] ;
slot = path - > slots [ 0 ] ;
/* Not deleting the first item of a leaf to keep leaf structure */
if ( slot = = 0 )
del = 0 ;
/* Only accept valid eb */
2016-09-09 11:37:35 +00:00
if ( slot > = btrfs_header_nritems ( leaf ) ) {
2016-09-07 14:09:11 +00:00
error ( " invalid eb: no data or slot out of range: %d >= %d " ,
slot , btrfs_header_nritems ( leaf ) ) ;
return - EINVAL ;
}
2013-06-07 02:25:59 +00:00
btrfs_item_key_to_cpu ( leaf , & key , slot ) ;
if ( del ) {
fprintf ( stdout , " Deleting key and data [%llu, %u, %llu]. \n " ,
key . objectid , key . type , key . offset ) ;
btrfs_del_item ( trans , root , path ) ;
} else {
fprintf ( stdout , " Corrupting key and data [%llu, %u, %llu]. \n " ,
key . objectid , key . type , key . offset ) ;
ptr = btrfs_item_ptr_offset ( leaf , slot ) ;
2022-02-22 22:26:20 +00:00
item_size = btrfs_item_size ( leaf , slot ) ;
2013-06-07 02:25:59 +00:00
memset_extent_buffer ( leaf , 0 , ptr , item_size ) ;
btrfs_mark_buffer_dirty ( leaf ) ;
}
return ret ;
}
2015-09-14 21:39:28 +00:00
static int corrupt_chunk_tree ( struct btrfs_trans_handle * trans ,
2013-06-07 02:25:59 +00:00
struct btrfs_root * root )
{
int ret ;
int del ;
int slot ;
struct btrfs_path * path ;
struct btrfs_key key ;
struct btrfs_key found_key ;
struct extent_buffer * leaf ;
path = btrfs_alloc_path ( ) ;
2013-10-07 21:42:54 +00:00
if ( ! path )
return - ENOMEM ;
2013-06-07 02:25:59 +00:00
key . objectid = ( u64 ) - 1 ;
key . offset = ( u64 ) - 1 ;
key . type = ( u8 ) - 1 ;
/* Here, cow and ins_len must equals 0 for the following reasons:
* 1 ) chunk recover is based on disk scanning , so COW should be
* disabled in case the original chunk being scanned and
* recovered using the old chunk .
* 2 ) if cow = 0 , ins_len must also be set to 0 , or BUG_ON will be
* triggered .
*/
ret = btrfs_search_slot ( trans , root , & key , path , 0 , 0 ) ;
BUG_ON ( ret = = 0 ) ;
if ( ret < 0 ) {
fprintf ( stderr , " Error searching tree \n " ) ;
goto free_out ;
}
/* corrupt/del dev_item first */
while ( ! btrfs_previous_item ( root , path , 0 , BTRFS_DEV_ITEM_KEY ) ) {
slot = path - > slots [ 0 ] ;
leaf = path - > nodes [ 0 ] ;
2016-05-26 02:56:51 +00:00
del = rand_range ( 3 ) ;
2013-06-07 02:25:59 +00:00
/* Never delete the first item to keep the leaf structure */
if ( path - > slots [ 0 ] = = 0 )
del = 0 ;
ret = corrupt_item_nocow ( trans , root , path , del ) ;
if ( ret )
goto free_out ;
}
2013-10-07 21:42:54 +00:00
btrfs_release_path ( path ) ;
2013-06-07 02:25:59 +00:00
/* Here, cow and ins_len must equals 0 for the following reasons:
* 1 ) chunk recover is based on disk scanning , so COW should be
* disabled in case the original chunk being scanned and
* recovered using the old chunk .
* 2 ) if cow = 0 , ins_len must also be set to 0 , or BUG_ON will be
* triggered .
*/
ret = btrfs_search_slot ( trans , root , & key , path , 0 , 0 ) ;
BUG_ON ( ret = = 0 ) ;
if ( ret < 0 ) {
fprintf ( stderr , " Error searching tree \n " ) ;
goto free_out ;
}
/* corrupt/del chunk then*/
while ( ! btrfs_previous_item ( root , path , 0 , BTRFS_CHUNK_ITEM_KEY ) ) {
slot = path - > slots [ 0 ] ;
leaf = path - > nodes [ 0 ] ;
2016-05-26 02:56:51 +00:00
del = rand_range ( 3 ) ;
2013-06-07 02:25:59 +00:00
btrfs_item_key_to_cpu ( leaf , & found_key , slot ) ;
ret = corrupt_item_nocow ( trans , root , path , del ) ;
if ( ret )
goto free_out ;
}
free_out :
btrfs_free_path ( path ) ;
return ret ;
}
2015-09-14 21:39:28 +00:00
static int find_chunk_offset ( struct btrfs_root * root ,
2013-06-07 02:25:59 +00:00
struct btrfs_path * path , u64 offset )
{
struct btrfs_key key ;
int ret ;
key . objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID ;
key . type = BTRFS_CHUNK_ITEM_KEY ;
key . offset = offset ;
/* Here, cow and ins_len must equals 0 for following reasons:
* 1 ) chunk recover is based on disk scanning , so COW should
* be disabled in case the original chunk being scanned
* and recovered using the old chunk .
* 2 ) if cow = 0 , ins_len must also be set to 0 , or BUG_ON
* will be triggered .
*/
ret = btrfs_search_slot ( NULL , root , & key , path , 0 , 0 ) ;
if ( ret > 0 ) {
fprintf ( stderr , " Can't find chunk with given offset %llu \n " ,
offset ) ;
goto out ;
}
if ( ret < 0 ) {
2015-08-07 00:20:25 +00:00
fprintf ( stderr , " Error searching chunk \n " ) ;
2013-06-07 02:25:59 +00:00
goto out ;
}
out :
return ret ;
}
2018-05-14 11:13:26 +00:00
2018-05-14 11:13:28 +00:00
static void parse_key ( u64 * objectid , u8 * type , u64 * offset )
{
int ret = sscanf ( optarg , " %llu,%hhu,%llu " , objectid , type , offset ) ;
if ( ret ! = 3 ) {
fprintf ( stderr , " error parsing key '%s': %d \n " , optarg , errno ) ;
print_usage ( 1 ) ;
}
}
2018-05-14 11:13:26 +00:00
static struct btrfs_root * open_root ( struct btrfs_fs_info * fs_info ,
u64 root_objectid )
{
struct btrfs_key root_key ;
struct btrfs_root * root ;
root_key . objectid = root_objectid ;
root_key . type = BTRFS_ROOT_ITEM_KEY ;
root_key . offset = ( u64 ) - 1 ;
root = btrfs_read_fs_root ( fs_info , & root_key ) ;
if ( IS_ERR ( root ) ) {
fprintf ( stderr , " couldn't find root %llu \n " , root_objectid ) ;
print_usage ( 1 ) ;
}
return root ;
}
2016-03-01 15:28:11 +00:00
int main ( int argc , char * * argv )
2011-10-28 02:02:19 +00:00
{
struct cache_tree root_cache ;
2014-01-07 20:19:35 +00:00
struct btrfs_key key ;
2018-05-14 11:13:31 +00:00
struct btrfs_root * root , * target_root ;
2011-10-28 02:02:19 +00:00
char * dev ;
2013-06-07 02:25:59 +00:00
/* chunk offset can be 0,so change to (u64)-1 */
u64 logical = ( u64 ) - 1 ;
2011-10-28 02:02:19 +00:00
int ret = 0 ;
2014-02-20 01:30:51 +00:00
u64 copy = 0 ;
2011-10-28 02:02:19 +00:00
u64 bytes = 4096 ;
2012-02-07 13:36:38 +00:00
int extent_rec = 0 ;
int extent_tree = 0 ;
2012-02-21 19:37:21 +00:00
int corrupt_block_keys = 0 ;
2013-06-07 02:25:59 +00:00
int chunk_rec = 0 ;
int chunk_tree = 0 ;
2014-10-02 19:12:20 +00:00
int corrupt_item = 0 ;
2014-10-10 20:57:16 +00:00
int corrupt_di = 0 ;
2014-10-31 18:01:19 +00:00
int delete = 0 ;
2018-05-14 11:13:30 +00:00
int should_corrupt_key = 0 ;
2013-10-01 12:54:58 +00:00
u64 metadata_block = 0 ;
2013-09-09 20:41:54 +00:00
u64 inode = 0 ;
2013-09-09 20:41:57 +00:00
u64 file_extent = ( u64 ) - 1 ;
2015-01-13 20:23:01 +00:00
u64 root_objectid = 0 ;
2015-05-19 14:21:04 +00:00
u64 csum_bytenr = 0 ;
2021-08-18 04:39:20 +00:00
u64 block_group = 0 ;
2013-09-09 20:41:54 +00:00
char field [ FIELD_BUF_LEN ] ;
2022-07-26 20:43:23 +00:00
u64 bogus_value = ( u64 ) - 1 ;
u64 bogus_offset = ( u64 ) - 1 ;
2012-02-07 13:36:38 +00:00
2013-09-09 20:41:54 +00:00
field [ 0 ] = ' \0 ' ;
2014-01-07 20:19:35 +00:00
memset ( & key , 0 , sizeof ( key ) ) ;
2011-10-28 02:02:19 +00:00
while ( 1 ) {
int c ;
2022-07-27 18:47:57 +00:00
enum { GETOPT_VAL_BLOCK_GROUP = GETOPT_VAL_FIRST ,
GETOPT_VAL_VALUE , GETOPT_VAL_OFFSET ,
} ;
2015-01-19 12:44:49 +00:00
static const struct option long_options [ ] = {
2015-01-19 12:30:06 +00:00
/* { "byte-count", 1, NULL, 'b' }, */
2015-04-08 15:39:51 +00:00
{ " logical " , required_argument , NULL , ' l ' } ,
{ " copy " , required_argument , NULL , ' c ' } ,
{ " bytes " , required_argument , NULL , ' b ' } ,
{ " extent-record " , no_argument , NULL , ' e ' } ,
{ " extent-tree " , no_argument , NULL , ' E ' } ,
{ " keys " , no_argument , NULL , ' k ' } ,
{ " chunk-record " , no_argument , NULL , ' u ' } ,
{ " chunk-tree " , no_argument , NULL , ' U ' } ,
{ " inode " , required_argument , NULL , ' i ' } ,
{ " file-extent " , required_argument , NULL , ' x ' } ,
{ " metadata-block " , required_argument , NULL , ' m ' } ,
{ " field " , required_argument , NULL , ' f ' } ,
{ " key " , required_argument , NULL , ' K ' } ,
{ " item " , no_argument , NULL , ' I ' } ,
{ " dir-item " , no_argument , NULL , ' D ' } ,
{ " delete " , no_argument , NULL , ' d ' } ,
{ " root " , no_argument , NULL , ' r ' } ,
2015-05-19 14:21:04 +00:00
{ " csum " , required_argument , NULL , ' C ' } ,
2021-08-20 13:01:32 +00:00
{ " block-group " , required_argument , NULL , GETOPT_VAL_BLOCK_GROUP } ,
2022-07-27 18:47:57 +00:00
{ " value " , required_argument , NULL , GETOPT_VAL_VALUE } ,
{ " offset " , required_argument , NULL , GETOPT_VAL_OFFSET } ,
2015-06-10 22:04:19 +00:00
{ " help " , no_argument , NULL , GETOPT_VAL_HELP } ,
2015-01-21 17:53:02 +00:00
{ NULL , 0 , NULL , 0 }
2015-01-19 12:30:06 +00:00
} ;
2022-07-27 18:47:57 +00:00
c = getopt_long ( argc , argv , " l:c:b:eEkuUi:f:x:m:K:I:D:d:r:C: " ,
2015-04-08 15:33:55 +00:00
long_options , NULL ) ;
2011-10-28 02:02:19 +00:00
if ( c < 0 )
break ;
switch ( c ) {
case ' l ' :
2014-02-20 01:30:51 +00:00
logical = arg_strtou64 ( optarg ) ;
2011-10-28 02:02:19 +00:00
break ;
case ' c ' :
2014-02-20 01:30:51 +00:00
copy = arg_strtou64 ( optarg ) ;
2011-10-28 02:02:19 +00:00
break ;
case ' b ' :
2014-02-20 01:30:51 +00:00
bytes = arg_strtou64 ( optarg ) ;
2011-10-28 02:02:19 +00:00
break ;
2012-02-06 13:54:05 +00:00
case ' e ' :
extent_rec = 1 ;
break ;
2012-02-07 13:36:38 +00:00
case ' E ' :
extent_tree = 1 ;
break ;
2012-02-21 19:37:21 +00:00
case ' k ' :
corrupt_block_keys = 1 ;
break ;
2013-06-07 02:25:59 +00:00
case ' u ' :
chunk_rec = 1 ;
break ;
case ' U ' :
chunk_tree = 1 ;
2014-12-19 15:11:59 +00:00
break ;
2013-09-09 20:41:54 +00:00
case ' i ' :
2014-02-20 01:30:51 +00:00
inode = arg_strtou64 ( optarg ) ;
2013-09-09 20:41:54 +00:00
break ;
case ' f ' :
strncpy ( field , optarg , FIELD_BUF_LEN ) ;
2013-06-07 02:25:59 +00:00
break ;
2013-09-09 20:41:57 +00:00
case ' x ' :
2014-02-20 01:30:51 +00:00
file_extent = arg_strtou64 ( optarg ) ;
2013-09-09 20:41:57 +00:00
break ;
2013-10-01 12:54:58 +00:00
case ' m ' :
2014-02-20 01:30:51 +00:00
metadata_block = arg_strtou64 ( optarg ) ;
2013-10-01 12:54:58 +00:00
break ;
2014-01-07 20:19:35 +00:00
case ' K ' :
2018-05-14 11:13:30 +00:00
should_corrupt_key = 1 ;
parse_key ( & key . objectid , & key . type , & key . offset ) ;
2014-01-07 20:19:35 +00:00
break ;
2014-10-10 20:57:16 +00:00
case ' D ' :
corrupt_di = 1 ;
2018-05-14 11:13:34 +00:00
parse_key ( & key . objectid , & key . type , & key . offset ) ;
2014-10-10 20:57:16 +00:00
break ;
2014-10-02 19:12:20 +00:00
case ' I ' :
corrupt_item = 1 ;
2018-05-14 11:13:29 +00:00
parse_key ( & key . objectid , & key . type , & key . offset ) ;
2014-12-19 15:08:48 +00:00
break ;
2014-10-31 18:01:19 +00:00
case ' d ' :
delete = 1 ;
2018-05-14 11:13:33 +00:00
parse_key ( & key . objectid , & key . type , & key . offset ) ;
2014-10-02 19:12:20 +00:00
break ;
2015-01-13 20:23:01 +00:00
case ' r ' :
root_objectid = arg_strtou64 ( optarg ) ;
break ;
2015-05-19 14:21:04 +00:00
case ' C ' :
csum_bytenr = arg_strtou64 ( optarg ) ;
break ;
2021-08-20 13:01:32 +00:00
case GETOPT_VAL_BLOCK_GROUP :
2021-08-18 04:39:20 +00:00
block_group = arg_strtou64 ( optarg ) ;
break ;
2022-07-27 18:47:57 +00:00
case GETOPT_VAL_VALUE :
2022-07-26 20:43:23 +00:00
bogus_value = arg_strtou64 ( optarg ) ;
break ;
2022-07-27 18:47:57 +00:00
case GETOPT_VAL_OFFSET :
2022-07-26 20:43:23 +00:00
bogus_offset = arg_strtou64 ( optarg ) ;
break ;
2015-06-10 22:04:19 +00:00
case GETOPT_VAL_HELP :
2011-10-28 02:02:19 +00:00
default :
2015-06-10 22:04:19 +00:00
print_usage ( c ! = GETOPT_VAL_HELP ) ;
2011-10-28 02:02:19 +00:00
}
}
2016-03-01 15:28:11 +00:00
set_argv0 ( argv ) ;
2016-03-01 15:29:16 +00:00
if ( check_argc_min ( argc - optind , 1 ) )
2019-03-04 13:49:15 +00:00
return 1 ;
2016-03-01 15:28:11 +00:00
dev = argv [ optind ] ;
2011-10-28 02:02:19 +00:00
radix_tree_init ( ) ;
cache_tree_init ( & root_cache ) ;
2013-10-28 18:28:43 +00:00
root = open_ctree ( dev , 0 , OPEN_CTREE_WRITES ) ;
2011-10-28 02:02:19 +00:00
if ( ! root ) {
fprintf ( stderr , " Open ctree failed \n " ) ;
exit ( 1 ) ;
}
2018-05-14 11:13:31 +00:00
target_root = root ;
if ( root_objectid )
target_root = open_root ( root - > fs_info , root_objectid ) ;
2012-02-06 13:54:05 +00:00
if ( extent_rec ) {
2012-02-07 13:36:38 +00:00
struct btrfs_trans_handle * trans ;
2013-09-09 20:41:54 +00:00
if ( logical = = ( u64 ) - 1 )
2015-06-10 22:04:19 +00:00
print_usage ( 1 ) ;
2012-02-07 13:36:38 +00:00
trans = btrfs_start_transaction ( root , 1 ) ;
2017-08-28 14:48:16 +00:00
BUG_ON ( IS_ERR ( trans ) ) ;
2017-02-09 16:42:02 +00:00
ret = corrupt_extent ( trans , root , logical ) ;
2012-02-07 13:36:38 +00:00
btrfs_commit_transaction ( trans , root ) ;
goto out_close ;
}
if ( extent_tree ) {
struct btrfs_trans_handle * trans ;
2021-11-08 19:26:41 +00:00
struct btrfs_root * extent_root ;
2017-08-28 14:48:16 +00:00
2021-11-08 19:26:41 +00:00
extent_root = btrfs_extent_root ( root - > fs_info , 0 ) ;
2012-02-07 13:36:38 +00:00
trans = btrfs_start_transaction ( root , 1 ) ;
2017-08-28 14:48:16 +00:00
BUG_ON ( IS_ERR ( trans ) ) ;
2021-11-08 19:26:41 +00:00
btrfs_corrupt_extent_tree ( trans , extent_root ,
extent_root - > node ) ;
2012-02-07 13:36:38 +00:00
btrfs_commit_transaction ( trans , root ) ;
goto out_close ;
2011-10-28 02:02:19 +00:00
}
2013-06-07 02:25:59 +00:00
if ( chunk_rec ) {
struct btrfs_trans_handle * trans ;
struct btrfs_path * path ;
int del ;
2013-09-09 20:41:54 +00:00
if ( logical = = ( u64 ) - 1 )
2015-06-10 22:04:19 +00:00
print_usage ( 1 ) ;
2016-05-26 02:56:51 +00:00
del = rand_range ( 3 ) ;
2013-06-07 02:25:59 +00:00
path = btrfs_alloc_path ( ) ;
2013-10-07 21:42:54 +00:00
if ( ! path ) {
fprintf ( stderr , " path allocation failed \n " ) ;
goto out_close ;
}
2013-06-07 02:25:59 +00:00
if ( find_chunk_offset ( root - > fs_info - > chunk_root , path ,
logical ) ! = 0 ) {
btrfs_free_path ( path ) ;
goto out_close ;
}
trans = btrfs_start_transaction ( root , 1 ) ;
2017-08-28 14:48:16 +00:00
BUG_ON ( IS_ERR ( trans ) ) ;
2013-06-07 02:25:59 +00:00
ret = corrupt_item_nocow ( trans , root - > fs_info - > chunk_root ,
path , del ) ;
if ( ret < 0 )
fprintf ( stderr , " Failed to corrupt chunk record \n " ) ;
btrfs_commit_transaction ( trans , root ) ;
goto out_close ;
}
if ( chunk_tree ) {
struct btrfs_trans_handle * trans ;
2017-08-28 14:48:16 +00:00
2013-06-07 02:25:59 +00:00
trans = btrfs_start_transaction ( root , 1 ) ;
2017-08-28 14:48:16 +00:00
BUG_ON ( IS_ERR ( trans ) ) ;
2013-06-07 02:25:59 +00:00
ret = corrupt_chunk_tree ( trans , root - > fs_info - > chunk_root ) ;
if ( ret < 0 )
fprintf ( stderr , " Failed to corrupt chunk tree \n " ) ;
btrfs_commit_transaction ( trans , root ) ;
goto out_close ;
}
2013-09-09 20:41:54 +00:00
if ( inode ) {
struct btrfs_trans_handle * trans ;
2015-11-06 15:53:55 +00:00
if ( * field = = 0 )
2015-06-10 22:04:19 +00:00
print_usage ( 1 ) ;
2013-09-09 20:41:57 +00:00
2013-09-09 20:41:54 +00:00
trans = btrfs_start_transaction ( root , 1 ) ;
2017-08-28 14:48:16 +00:00
BUG_ON ( IS_ERR ( trans ) ) ;
2013-09-09 20:41:57 +00:00
if ( file_extent = = ( u64 ) - 1 ) {
printf ( " corrupting inode \n " ) ;
ret = corrupt_inode ( trans , root , inode , field ) ;
} else {
ret = corrupt_file_extent ( trans , root , inode ,
2022-07-26 20:43:24 +00:00
file_extent , field , bogus_value ) ;
2013-09-09 20:41:57 +00:00
}
2013-09-09 20:41:54 +00:00
btrfs_commit_transaction ( trans , root ) ;
goto out_close ;
}
2013-10-01 12:54:58 +00:00
if ( metadata_block ) {
2015-11-06 15:53:55 +00:00
if ( * field = = 0 )
2015-06-10 22:04:19 +00:00
print_usage ( 1 ) ;
2017-05-18 02:36:19 +00:00
ret = corrupt_metadata_block ( root - > fs_info , metadata_block ,
field ) ;
2013-10-01 12:54:58 +00:00
goto out_close ;
}
2014-10-10 20:57:16 +00:00
if ( corrupt_di ) {
2015-11-06 15:53:55 +00:00
if ( ! key . objectid | | * field = = 0 )
2015-06-10 22:04:19 +00:00
print_usage ( 1 ) ;
2018-05-14 11:13:34 +00:00
ret = corrupt_dir_item ( target_root , & key , field ) ;
2014-10-10 20:57:16 +00:00
goto out_close ;
}
2015-05-19 14:21:04 +00:00
if ( csum_bytenr ) {
ret = delete_csum ( root , csum_bytenr , bytes ) ;
goto out_close ;
}
2014-10-02 19:12:20 +00:00
if ( corrupt_item ) {
if ( ! key . objectid )
2015-06-10 22:04:19 +00:00
print_usage ( 1 ) ;
2018-05-14 11:13:27 +00:00
if ( ! root_objectid )
print_usage ( 1 ) ;
2022-07-26 20:43:23 +00:00
if ( * field ! = 0 )
ret = corrupt_btrfs_item ( target_root , & key , field ) ;
else if ( bogus_offset ! = ( u64 ) - 1 & &
bytes ! = ( u64 ) - 1 & &
bogus_value ! = ( u64 ) - 1 )
ret = corrupt_btrfs_item_data ( target_root , & key ,
bogus_offset , bytes ,
bogus_value ) ;
else
print_usage ( 1 ) ;
2018-05-14 11:13:29 +00:00
goto out_close ;
2014-10-31 18:01:19 +00:00
}
if ( delete ) {
if ( ! key . objectid )
2015-06-10 22:04:19 +00:00
print_usage ( 1 ) ;
2018-05-14 11:13:26 +00:00
2018-05-14 11:13:31 +00:00
ret = delete_item ( target_root , & key ) ;
2014-10-02 19:12:20 +00:00
goto out_close ;
}
2018-05-14 11:13:30 +00:00
if ( should_corrupt_key ) {
2015-11-06 15:53:55 +00:00
if ( * field = = 0 )
2015-06-10 22:04:19 +00:00
print_usage ( 1 ) ;
2018-05-14 11:13:32 +00:00
ret = corrupt_key ( target_root , & key , field ) ;
2014-01-07 20:19:35 +00:00
goto out_close ;
}
2021-08-18 04:39:20 +00:00
if ( block_group ) {
if ( * field = = 0 )
print_usage ( 1 ) ;
ret = corrupt_block_group ( root , block_group , field ) ;
goto out_close ;
}
2013-09-09 20:41:57 +00:00
/*
* If we made it here and we have extent set then we didn ' t specify
* inode and we ' re screwed .
*/
if ( file_extent ! = ( u64 ) - 1 )
2015-06-10 22:04:19 +00:00
print_usage ( 1 ) ;
2013-09-09 20:41:57 +00:00
2013-09-09 20:41:54 +00:00
if ( logical = = ( u64 ) - 1 )
2015-06-10 22:04:19 +00:00
print_usage ( 1 ) ;
2011-10-28 02:02:19 +00:00
if ( bytes = = 0 )
2017-05-18 01:11:20 +00:00
bytes = root - > fs_info - > sectorsize ;
2011-10-28 02:02:19 +00:00
2017-05-18 01:11:20 +00:00
bytes = round_up ( bytes , root - > fs_info - > sectorsize ) ;
2011-10-28 02:02:19 +00:00
while ( bytes > 0 ) {
2012-02-21 19:37:21 +00:00
if ( corrupt_block_keys ) {
2017-05-18 02:36:19 +00:00
corrupt_keys_in_block ( root - > fs_info , logical ) ;
2012-02-21 19:37:21 +00:00
} else {
2016-07-15 14:38:07 +00:00
struct extent_buffer * eb ;
eb = btrfs_find_create_tree_block ( root - > fs_info ,
2017-08-25 15:44:22 +00:00
logical ) ;
2016-07-15 14:38:07 +00:00
if ( ! eb ) {
error (
" not enough memory to allocate extent buffer for bytenr %llu " ,
( unsigned long long ) logical ) ;
ret = 1 ;
goto out_close ;
}
2017-05-18 01:11:20 +00:00
debug_corrupt_block ( eb , root , logical ,
root - > fs_info - > sectorsize , copy ) ;
2012-02-21 19:37:21 +00:00
free_extent_buffer ( eb ) ;
}
2017-05-18 01:11:20 +00:00
logical + = root - > fs_info - > sectorsize ;
bytes - = root - > fs_info - > sectorsize ;
2011-10-28 02:02:19 +00:00
}
2012-02-07 13:36:38 +00:00
return ret ;
out_close :
close_ctree ( root ) ;
2011-10-28 02:02:19 +00:00
return ret ;
}