/* * Copyright (C) 2013 SUSE. 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. */#include"kerncompat.h"#include<getopt.h>#include"ctree.h"#include"volumes.h"#include"transaction.h"#include"disk-io.h"#include"commands.h"#include"utils.h"#include"help.h"staticconstchar*constrescue_cmd_group_usage[]={"btrfs rescue <command> [options] <path>",NULL};intbtrfs_recover_chunk_tree(constchar*path,intverbose,intyes);intbtrfs_recover_superblocks(constchar*path,intverbose,intyes);staticconstchar*constcmd_rescue_chunk_recover_usage[]={"btrfs rescue chunk-recover [options] <device>","Recover the chunk tree by scanning the devices one by one.","","-y Assume an answer of `yes' to all questions","-v Verbose mode","-h Help",NULL};staticintcmd_rescue_chunk_recover(intargc,char*argv[]){intret=0;char*file;intyes=0;intverbose=0;optind=0;while(1){intc=getopt(argc,argv,"yvh");if(c<0)break;switch(c){case'y':yes=1;break;case'v':verbose=1;break;case'h':default:usage(cmd_rescue_chunk_recover_usage);}}if(check_argc_exact(argc-optind,1))usage(cmd_rescue_chunk_recover_usage);file=argv[optind];ret=check_mounted(file);if(ret<0){errno=-ret;error("could not check mount status: %m");return1;}elseif(ret){error("the device is busy");return1;}ret=btrfs_recover_chunk_tree(file,verbose,yes);if(!ret){fprintf(stdout,"Chunk tree recovered successfully\n");}elseif(ret>0){ret=0;fprintf(stdout,"Chunk tree recovery aborted\n");}else{fprintf(stdout,"Chunk tree recovery failed\n");}returnret;}staticconstchar*constcmd_rescue_super_recover_usage[]={"btrfs rescue super-recover [options] <device>","Recover bad superblocks from good copies","","-y Assume an answer of `yes' to all questions","-v Verbose mode",NULL};/* * return codes: * 0 : All superblocks are valid, no need to recover * 1 : Usage or syntax error * 2 : Recover all bad superblocks successfully * 3 : Fail to Recover bad superblocks * 4 : Abort to recover bad superblocks */staticintcmd_rescue_super_recover(intargc,char**argv){intret;intverbose=0;intyes=0;char*dname;optind=0;while(1){intc=getopt(argc,argv,"vy");if(c<0)break;switch(c){case'v':verbose=1;break;case'y':yes=1;break;default:usage(cmd_rescue_super_recover_usage);}}if(check_argc_exact(argc-optind,1))usage(cmd_rescue_super_recover_usage);dname=argv[optind];ret=check_mounted(dname);if(ret<0){errno=-ret;error("could not check mount status: %m");return1;}elseif(ret){error("the device is busy");return1;}ret=btrfs_recover_superblocks(dname,verbose,yes);returnret;}staticconstchar*constcmd_rescue_zero_log_usage[]={"btrfs rescue zero-log <device>","Clear the tree log. Usable if it's corrupted and prevents mount.","",NULL};staticintcmd_rescue_zero_log(intargc,char**argv){structbtrfs_root*root;structbtrfs_trans_handle*trans;structbtrfs_super_block*sb;char*devname;intret;clean_args_no_options(argc,argv,cmd_rescue_zero_log_usage);if(check_argc_exact(argc,2))usage(cmd_rescue_zero_log_usage);devname=argv[optind];ret=check_mounted(devname);if(ret<0){errno=-ret;error("could not check mount status: %m");gotoout;}elseif(ret){error("%s is currently mounted",devname);ret=-EBUSY;gotoout;}root=open_ctree(devname,0,OPEN_CTREE_WRITES|OPEN_CTREE_PARTIAL);if(!root){error("could not open ctree");return1;}sb=root->fs_info->super_copy;printf("Clearing log on %s, previous log_root %llu, level %u\n",devname,(unsignedlonglong)btrfs_super_log_root(sb),(unsigned)btrfs_super_log_root_level(sb));trans=btrfs_start_transaction(root,1);BUG_ON(IS_ERR(trans));btrfs_set_super_log_root(sb,0);btrfs_set_super_log_root_level(sb,0);btrfs_commit_transaction(trans,root);close_ctree(root);out:return!!ret;}staticconstchar*constcmd_rescue_fix_device_size_usage[]={"btrfs rescue fix-device-size <device>","Re-align device and super block sizes. Usable if newer kernel refuse to mount it due to mismatch super size","",NULL};staticintcmd_rescue_fix_device_size(intargc,char**argv){structbtrfs_fs_info*fs_info;char*devname;intret;clean_args_no_options(argc,argv,cmd_rescue_fix_device_size_usage);if(check_argc_exact(argc,2))usage(cmd_rescue_fix_device_size_usage);devname=argv[optind];ret=check_mounted(devname);if(ret<0){errno=-ret;error("could not check mount status: %m");gotoout;}elseif(ret){error("%s is currently mounted",devname);ret=-EBUSY;gotoout;}fs_info=open_ctree_fs_info(devname,0,0,0,OPEN_CTREE_WRITES|OPEN_CTREE_PARTIAL);if(!fs_info){error("could not open btrfs");ret=-EIO;gotoout;}ret=btrfs_fix_device_and_super_size(fs_info);if(ret>0)ret=0;close_ctree(fs_info->tree_root);out:return!!ret;}staticconstcharrescue_cmd_group_info[]="toolbox for specific rescue operations";conststructcmd_grouprescue_cmd_group={rescue_cmd_group_usage,rescue_cmd_group_info,{{"chunk-recover",cmd_rescue_chunk_recover,cmd_rescue_chunk_recover_usage,NULL,0},{"super-recover",cmd_rescue_super_recover,cmd_rescue_super_recover_usage,NULL,0},{"zero-log",cmd_rescue_zero_log,cmd_rescue_zero_log_usage,NULL,0},{"fix-device-size",cmd_rescue_fix_device_size,cmd_rescue_fix_device_size_usage,NULL,0},NULL_CMD_STRUCT}};intcmd_rescue(intargc,char**argv){returnhandle_command_group(&rescue_cmd_group,argc,argv);}