浅谈Linux磁盘修复e2fsck命令

linux下磁盘检查修复命令e2fsck

-a: 检查 partition,如发现问题会自动修复。

-b: 设定 superblock 位置。

-B size: 指定 size 作为区块大小。

-c: 检查 partition 是否有坏轨。

-C file: 将检查结果储存到 file。

-d: 输出 e2fsck debug 结果。

-f: e2fsck 预设只会对错误的档案系统检查,加上 -f 是强制检查。

-F: 在检查前将硬盘的 buffer cache 清空,避免发生错误。

-l list: 记录了坏轨区块加入 list 中。

-d : 打印 e2fsck 的 debug 结果。

-f : 强制检查。

-n: 以 (read-only) 开启档案系统

-p: 关闭互动模式,如有问题自动修复,等同 -a。

-v: 显示详细报告。

-y: 启用使用者互动模式。

使用例子

检查 /dev/mapper/VolGroup00-LogVol02 是否有问题,如发现问题便自动修复:

e2fsck -a /dev/mapper/VolGroup00-LogVol02

执行 e2fsck 或 fsck 前请先 umount partition,否则有机会令档案系统毁损。

分区忙的情况,需要将所有涉及该分区的进程杀掉,有个快速的方法是执行 fuser -k /home 。

如果需要对根目录 (/) 进行检查及修复,便需要进入 singal user mode 执行。

最后别忘了将分区mount上。

#!/usr/bin/env python2.7

# coding: utf-8

"""

It only handle the following two situations:

1. There is only one primary partiion in the disk with a format of ext2/3/4 or xfs;

2. The disk is raw with a file system whose format is ext2/3/4 or xfs.

"""

import struct

import array

import fcntl

import time

import sys

import os

import glob

import logging

import commands

import argparse

import atexit

import re

BLKSSZGET = 0x1268

BLKGETSIZE = 0x1260

BLKRRPART = 0x125f

BLKGETSIZE64 = 0x80041272

NORMAL_DEVICE_NAME = r"\/dev\/\D+$"

SPECITIAL_DEVICE_NAME = r"\/dev\/\D+\d$"

logger = None

def read_ub(data):

"""read little-endian unsigned byte"""

return struct.unpack('B', data[0])[0]

def read_us(data):

"""read little-endian unsigned short(2 bytes)"""

return struct.unpack('<H', data[0:2])[0]

def read_ui(data):

"""read little-endian unsigned int(4 bytes)"""

return struct.unpack('<I', data[0:4])[0]

def read_ul(data):

"""read little-endian unsigned long(8 bytes)"""

return struct.unpack('<Q', data[0:8])[0]

def init_log():

"""初始化日志"""

global logger

log_file = 'devresize.log'

fmt_file = '%(asctime)s - [%(levelname)-5.5s]- %(filename)s:%(lineno)s - %(message)s'

fmt_stream = '[%(levelname)s] - %(message)s'

logger = logging.getLogger('devresize')

logger.setLevel(logging.DEBUG)

file_handler = logging.FileHandler(log_file)

file_handler.setLevel(logging.DEBUG)

file_handler.setFormatter(logging.Formatter(fmt_file))

logger.addHandler(file_handler)

stream_handler = logging.StreamHandler()

stream_handler.setLevel(logging.INFO)

stream_handler.setFormatter(logging.Formatter(fmt_stream))

logger.addHandler(stream_handler)

class PartitionEntry(object):

"""表示一个磁盘分区"""

PartitionTypes = {

0x05: "Microsoft Extended",

0x83: "Linux",

0x85: "Linux Extended"

}

def __init__(self, data):

self.data = data

self.boot_sig = data[0]

self.start_head, self.start_sector, self.start_cylinder = (

PartitionEntry.get_hsc(data[1:1 + 3]))

self.partition_type = read_ub(data[4])

self.end_head, self.end_sector, self.end_cylinder = (

PartitionEntry.get_hsc(data[5:5 + 3]))

self.start_lba = read_ui(data[8:8 + 4])

self.sector_num = read_ui(data[12:12 + 4])

self.partition_type_name = PartitionEntry.PartitionTypes.get(self.partition_type, "other")

@staticmethod

def get_hsc(data):

"""获取(head, sector, cylindar)"""

h, s, c = struct.unpack('BBB', data[0:3])

c = (c | ((s & 0xC0) << 2))

s = (s & 0x3F)

return h, s, c

@staticmethod

def cal_hsc(sector, hh, ss):

"""计算(head, sector, cylindar)"""

s = sector % ss + 1

sector /= ss

h = sector % hh

sector /= hh

c = sector & 0xFF

s |= (sector >> 2) & 0xC0

return h, s, c

def vaild_type(self):

"""校验分区类型是否在可处理的名单里"""

return self.partition_type in self.PartitionTypes

def isprimary(self):

"""是否为主分区"""

return self.partition_type == 0x83

def __str__(self):

if not self.vaild_type():

logger.info("%x" % self.partition_type)

return "This isn't a Linux Partition!"

return """

Start h,s,c: %u %u %u

End h,s,c: %u %u %u

Partition Type Name:%s

Start LBA: %u

Sector Number: %u

""" % (self.start_head, self.start_sector, self.start_cylinder,

self.end_head, self.end_sector, self.end_cylinder,

self.partition_type_name, self.start_lba, self.sector_num)

class MBR(object):

def __init__(self, data):

self.data = data

self.boot_code = data[:446]

self.mbr_sig = data[510:512]

if self.check_mbr_sig(): # 如果存在分区

self.partitions = ([PartitionEntry(data[446 + 16 * i:446 + 16 * (i + 1)])

for i in range(0, 4)])

else: # 否则为裸盘文件系统

self.partitions = None

if self.partitions is not None:

self.vaild_part_num = len(filter(lambda x: x.vaild_type(), self.partitions))

else:

self.vaild_part_num = 0

self.device_heads = 0

self.device_sectors = 0

self.cal_device_hs()

def cal_device_hs(self):

"""计算设备的heads和sectors"""

if self.partitions is not None and self.vaild_part_num == 1:

self.device_heads = self.partitions[0].end_head + 1

self.device_sectors = self.partitions[0].end_sector & 0x3F

def check_mbr_sig(self):

"""检查MBR签名"""

mbr_sig = read_us(self.mbr_sig)

if mbr_sig == 0xAA55:

return True

else:

return False

def get_device_size(fd):

"""获取块设备大小"""

buf = array.array('c', [chr(0)] * 8)

fcntl.ioctl(fd, BLKSSZGET, buf, True)

logical_sector_size = read_ul(buf)

buf = array.array('c', [chr(0)] * 8)

try:

fcntl.ioctl(fd, BLKGETSIZE, buf, True)

device_size = read_ul(buf) * 512

except IOError:

fcntl.ioctl(fd, BLKGETSIZE64, buf, True)

device_size = read_ul(buf)

device_sector_number = device_size / logical_sector_size

logger.debug(

"""device_size:%d

device_sector_number:%d

logical_sector_size:%d""" % (device_size, device_sector_number, logical_sector_size))

return device_size, device_sector_number, logical_sector_size

def is_ext_fs(fstype):

return 'ext' in fstype

def check_fs_block_size(part, fstype, mount_dir):

"""获取文件系统块大小和块数"""

if is_ext_fs(fstype):

block_size = commands.getoutput("tune2fs -l %s | grep 'Block size' | awk '{print $3}'" % part)

# block_count = commands.getoutput("tune2fs -l %s | grep 'Block count' | awk '{print $3}'" % part)

else:

mount_fs(part, mount_dir)

output = commands.getoutput("xfs_info %s | grep '^data' | awk -F '[= ,]+' '{print $3, $5}'" % part).split()

umount_fs(part)

block_size = output[0]

# block_count = output[1]

if not block_size:

logger.error("Check filesystem %s block size error, cannot get block size." % part)

sys.exit(1)

if int(block_size) != 4096:

logger.error("Only can process filesystem with block size 4KB (actual block size is %s bytes)" % block_size)

sys.exit(1)

return mount_dir

def backup_mbr(part, data):

"""备份MBR元数据"""

bak_name = '/tmp/MBR_%s_%s_bak' % (os.path.basename(part), time.strftime("%Y-%m-%d_%X", time.localtime()))

bak_file = open(bak_name, 'w')

bak_file.write(data)

bak_file.close()

logger.info("Backup MBR to %s" % bak_name)

return bak_name

def cal_new_part(part_data, mbr, start_lab, new_end):

"""计算新的MBR分区"""

device_heads, device_sectors = mbr.device_heads, mbr.device_sectors

new_partition_sector_num = new_end - start_lab + 1

begin_h, begin_s, begin_c = PartitionEntry.cal_hsc(start_lab, device_heads, device_sectors)

end_h, end_s, end_c = PartitionEntry.cal_hsc(new_end, device_heads, device_sectors)

new_part_data = list(part_data[:])

new_part_data[1:1 + 3] = list(struct.pack('BBB', begin_h, begin_s, begin_c))

new_part_data[5:5 + 3] = list(struct.pack('BBB', end_h, end_s, end_c))

new_part_data[0xc:] = list(

struct.pack('BBBB', (new_partition_sector_num & 0xff), ((new_partition_sector_num >> 8) & 0xff),

((new_partition_sector_num >> 16) & 0xff), ((new_partition_sector_num >> 24) & 0xff)))

logger.debug("""

Start h,s,c: %u %u %u

End h,s,c: %u %u %u

Partition Type Name:%s

Start LBA: %u

Sector Number: %u

""" % (begin_h, begin_s, begin_c,

end_h, end_s, end_c,

mbr.partitions[0].partition_type_name,

mbr.partitions[0].start_lba,

new_partition_sector_num))

return new_part_data

def check_partition(dev, mbr):

"""检查磁盘分区"""

resize_part_flag = True

target_partition = ''

part_count = int(commands.getoutput("ls %s* | wc -w" % dev)) - 1

if part_count > 0 and part_count != mbr.vaild_part_num:

logger.debug(commands.getoutput('ls %s*' % dev))

logger.debug("%s != %s", part_count, mbr.vaild_part_num)

logger.error("Disk %s has invalid partition" % dev)

sys.exit(1)

if mbr.vaild_part_num > 1:

logger.error("Disk %s has multiple partitions." % dev)

sys.exit(1)

elif mbr.vaild_part_num == 1: # only one partition, which is the primary partition

if not mbr.partitions[0].isprimary(): # and the filesystem type is ext2/3/4.

logger.error("Must be primary partition.")

sys.exit(1)

resize_part_flag = True

if dev[-1].isdigit():

target_partition = dev + 'p1' # ex: /dev/nbd0 -> /dev/nbd0p1

else:

target_partition = dev + '1' # ex: /dev/vdb -> /dev/vdb1

logger.debug('target_partition:%s' % target_partition)

elif mbr.vaild_part_num == 0: # no partition but whole disk is ext2/3/4

resize_part_flag = False

target_partition = dev

return target_partition, resize_part_flag

def check_format(part):

"""检查是否为支持的分区类型"""

output = commands.getoutput('blkid %s' % part)

if not output:

logger.error("check filesystem format error, please ensure %s is a valid filesystem" % part)

sys.exit(1)

for fmt in ['ext2', 'ext3', 'ext4', 'xfs']:

if fmt in output:

return fmt

logger.error("Only can process ext2/3/4 and xfs.")

sys.exit(1)

def check_fs_healthy(part, fstype = 'ext'):

"""检查文件系统完整性"""

logger.info("checking filesystem healthy")

if is_ext_fs(fstype):

ret = os.system('e2fsck -af %s' % part)

logger.debug('e2fsck ret is %d' % ret)

if ret == 1:

logger.info('File system errors have been corrected')

ret = ret not in [0, 1]

else:

ret = os.system('xfs_repair %s' % part)

logger.debug('xfs_repair ret is %d' % ret)

if ret:

logger.error('File system %s error!' % part)

sys.exit(1)

def mount_fs(part, mount_dir):

"""挂载块设备"""

# first need to mount fs

if not os.path.exists(mount_dir):

os.mkdir(mount_dir)

ret = os.system('mount %s %s' % (part, mount_dir))

if ret != 0:

raise RuntimeError('mount failed! (return code %s)' % ret)

logger.info('mount %s %s' % (part, mount_dir))

def umount_fs(part):

"""解挂块设备"""

mount_dir = commands.getoutput("mount | grep '%s ' | awk '{print $3}'" % part)

if not mount_dir: # if not mounted

return

else:

ret = os.system('umount %s' % part)

logger.info('umount %s' % part)

if ret != 0:

raise RuntimeError('umount failed! (return code %s)' % ret)

def resize2fs(part):

"""使用resize2fs扩容ext文件系统"""

logger.info("resize filesystem")

ret = os.system('resize2fs -f %s' % part)

logger.debug('resize2fs ret is %d' % ret)

if ret != 0:

raise RuntimeError('resize2fs failed! (return code %s)' % ret)

def resize_xfs(mount_dir):

"""扩容xfs文件系统"""

logger.info("resize filesystem")

ret = os.system('xfs_growfs %s' % mount_dir)

logger.debug('xfs_growfs ret is %d' % ret)

if ret != 0:

raise RuntimeError('xfs_growfs failed! (return code %s)' % ret)

def check_mount(target_dev): # target_dev is mounted!

"""确认要扩容的块设备未挂载"""

output = commands.getoutput('mount | grep "%s "' % target_dev)

if output:

logger.error("Target partition %s must be unmounted." % target_dev)

sys.exit(1)

def part_probe(fd):

"""将写入文件的数据落到磁盘上"""

if logger:

logger.debug('part_probe')

fd.flush()

time.sleep(1)

ret = os.system("partprobe %s" % (fd.name))

if ret != 0:

logger.error("partprobe %s returned non-zero value %s" % (fd.name, ret))

sys.exit(1)

# fcntl.ioctl(fd, BLKRRPART)

def write_mbr(fd, mbr_data):

"""将mbr数据写入文件"""

fd.seek(0)

fd.write(mbr_data)

time.sleep(1)

part_probe(fd)

time.sleep(1)

def check_permission(device):

"""检查设备访问权限"""

if not os.access(device, os.W_OK):

logger.error("Permission denied")

sys.exit(1)

def check_args(device):

"""检查传入的参数是否为设备名而不是分区名"""

exclude_devices = ["/dev/loop", "/dev/nbd"]

normal_device = True

for name in exclude_devices:

if device.startswith(name):

normal_device = False

if normal_device and not re.match(NORMAL_DEVICE_NAME, device):

logger.error("The argument should be a whole disk, not a partition! Example: /dev/vdb")

sys.exit(1)

elif not normal_device and not re.match(SPECITIAL_DEVICE_NAME, device):

logger.error("The argument should be a whole disk, not a partition! Example: /dev/loop1")

sys.exit(1)

def check_partition_need_resize(target_partition):

"""检查分区是否可扩容"""

output = commands.getoutput("parted %s unit MiB print free | tail -n 2 | head -n 1" % target_partition)

return "Free Space" in output

def check_mbr(device):

"""检查是否为mbr分区"""

output = commands.getoutput("parted %s print | grep 'Partition Table'" % device)

if 'gpt' in output:

logger.error("Not support GPT disk currently")

sys.exit(1)

def check_commands(command_list=[]):

"""检查运行环境和工具是否支持"""

for cmd in command_list:

ret, _ = commands.getstatusoutput("which %s" % cmd)

if ret:

logger.error("%s: command not found" % cmd)

sys.exit(1)

# def get_disk_path(partation_name):

# """从*分区名*解析出块设备名"""

# for i, ch in enumerate(os.path.basename(partation_name)[::-1]):

# if not ch.isdigit():

# return partation_name[::-1][i::][::-1]

# logger.error("invalid para %s" % partation_name)

# raise Exception("invalid para %s" % partation_name)

def closefd(fd):

if not fd.closed:

logger.debug("close fd")

fd.close()

def main():

"""

Steps:

1. check filesystem format

2. check unmounted

3. check filesystem healthy

4. check filesystem block size

5. backup MBR

6. rewrite MBR(resize partition)

7. resize filesystem

"""

init_log()

logger.debug("user input:%s" % ' '.join(sys.argv))

parser = argparse.ArgumentParser()

parser.add_argument("device", help="your device path (not a partition)")

parser.add_argument("-f", "--force", help="ignore all prompts", action="store_true")

args = parser.parse_args()

device = args.device

check_args(device)

check_permission(device)

check_commands(["parted", "partprobe", "blkid"])

check_mbr(device)

fd = open(device, 'r+')

data = fd.read(512)

mbr = MBR(data)

bak_mbr_data = ''

mount_dir = '/tmp/mount_point_%s_%s' % \

(os.path.basename(device), time.strftime("%Y-%m-%d_%X", time.localtime()))

atexit.register(closefd, fd)

device_size, device_sector_number, logical_sector_size = get_device_size(fd)

target_partition, resize_part_flag = check_partition(device, mbr)

fstype = check_format(target_partition)

if is_ext_fs(fstype):

check_commands(["resize2fs", "e2fsck", "tune2fs"])

else:

check_commands(["xfs_growfs", "xfs_repair", "xfs_info"])

check_mount(target_partition)

check_fs_healthy(target_partition, fstype)

check_fs_block_size(target_partition, fstype, mount_dir)

if not args.force:

user_input = raw_input("This operation will extend %s to the last sector of device. \n"

"To ensure the security of your valuable data, \n"

"please create a snapshot of this volume before resize its file system, continue? [Y/n]\n" % target_partition)

if user_input.lower() != 'y' and user_input != '':

logger.warn("User input neither 'y' nor '[Enter]',exit.")

sys.exit(1)

if not args.force:

user_input = raw_input("It will resize (%s).\n"

"This operation may take from several minutes to several hours, continue? [Y/n]\n" % target_partition)

if user_input.lower() != 'y' and user_input != '':

logger.warn("User input neither 'y' nor '[Enter]',exit.")

sys.exit(1)

if resize_part_flag and check_partition_need_resize(device): # if need to resize partition

logger.debug("Begin to change the partation")

if (mbr.partitions[0].start_lba + mbr.partitions[0].sector_num) == device_sector_number:

logger.error("No free sectors available.")

sys.exit(1)

if mbr.partitions[0].sector_num > 0xFFFFFFFF * 512 / logical_sector_size:

logger.error("Can't process the partition which have exceeded 2TB.")

sys.exit(1)

new_start_sector = mbr.partitions[0].start_lba

new_end_sector = device_sector_number - 1

if (new_end_sector - new_start_sector + 1) * logical_sector_size > 0xFFFFFFFF * 512:

if not args.force:

user_input = raw_input("The size of this disk is %.2fTB (%d bytes).\n"

"But DOS partition table format can not be used on drives for volumes "

"larger than 2TB (2199023255040 bytes).\n"

"Do you want to resize (%s) to 2TB? [Y/n]\n"

% (round(device_size / 1024.0 / 1024 / 1024 / 1024, 2), device_size,

target_partition))

if user_input.lower() != 'y' and user_input != '':

logger.warn("User input neither 'y' nor '[Enter]',exit.")

sys.exit(1)

new_end_sector = 0xFFFFFFFF * 512 / logical_sector_size + new_start_sector - 1

new_mbr_data = list(data)[:]

new_mbr_data[446:446 + 16] = cal_new_part(data[446:446 + 16], mbr,

new_start_sector, new_end_sector)

backup_mbr(target_partition, data)

bak_mbr_data = data

else:

logger.info("No need to resize partition, try to resize filesystem")

resize_part_flag = False

time.sleep(1)

# rewrite MBR(if necessary), resize file system

try:

if resize_part_flag:

umount_fs(target_partition)

write_mbr(fd, ''.join(new_mbr_data))

umount_fs(target_partition)

if is_ext_fs(fstype):

resize2fs(target_partition)

else:

mount_fs(target_partition, mount_dir)

resize_xfs(mount_dir)

umount_fs(target_partition)

except Exception, e:

umount_fs(target_partition)

logger.error(e)

# logger.error('Some error occurred! Please make sure the e2fsprogs version is above 1.42.13.')

logger.error('Some error occurred! Maybe you should call the customer service staff.')

if resize_part_flag:

logger.error('Resize filesystem aborted, restore MBR')

write_mbr(fd, bak_mbr_data)

sys.exit(1)

logger.info("Finished")

if __name__ == '__main__':

main()

原文发布于微信公众号 - 竹清助手(zhuqing_help)

原文发表时间:2018-11-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大魏分享(微信公众号:david-share)

JavaEE中资源注入松耦合的实现 | 从开发角度看应用架构13

上下文和依赖注入(CDI)规范是Java EE规范中的许多从属规范之一。虽然CDI是在Java EE 6中引入的,但CDI背后的概念已经出现在各种框架中,包括S...

1052
来自专栏向治洪

svn错误对照表

#, c-format msgid "Destination '%s' is not a directory" msgstr "目的 “%s” 不是目录" ...

3585
来自专栏java思维导图

Spring思维导图,让Spring不再难懂(ioc篇)

写在前面 写过java的都知道:所有的对象都必须创建;或者说:使用对象之前必须先创建。而使用ioc之后,你就可以不再手动创建对象,而是从ioc容器中直接获取对象...

4127
来自专栏JAVA高级架构

Java面试分享(题目+答案)

3303
来自专栏JAVA高级架构

spring和springMVC的面试问题总结

1.Spring中AOP的应用场景、Aop原理、好处? 答:AOP--Aspect Oriented Programming面向切面编程;用来封装横切关注点,具...

3719
来自专栏深度学习计算机视觉

Spring常见面试题整理

1.什么是Spring? Spring是一个开源的Java EE开发框架。Spring框架的核心功能可以应用在任何Java应用程序中,但对Java EE平台上的...

1.4K4
来自专栏JMCui

Netty 系列四(ChannelHandler 和 ChannelPipeline).

    先来整体的介绍一下这篇博文要介绍的几个概念(Channel、ChannelHandler、ChannelPipeline、ChannelHandlerC...

1242
来自专栏你不就像风一样

史上最全面的Spring Boot配置文件详解

Spring Boot在工作中是用到的越来越广泛了,简单方便,有了它,效率提高不知道多少倍。Spring Boot配置文件对Spring Boot来说就是入门和...

1.7K2
来自专栏美团技术团队

Spring MVC注解故障追踪记

Spring MVC是美团点评很多团队使用的Web框架。在基于Spring MVC的项目里,注解的使用几乎遍布在项目中的各个模块,有Java提供的注解,如:@O...

4237
来自专栏微服务生态

玩转EhCache之最简单的缓存框架

Ehcache是一个用Java实现的使用简单,高速,实现线程安全的缓存管理类库,ehcache提供了用内存,磁盘文件存储,以及分布式存储方式等多种灵活的cach...

7094

扫码关注云+社区

领取腾讯云代金券