在一开始的时候

在一开始的时候

最后更新:

我已经知道要做什么才能解决这个问题;我只是不知道如何来做到这一点。我希望有一些现成的工具可以自动完成这项工作 - 但找不到。我接受 Rod 的回答,因为尽管它没有直接解决我的问题,但它提供了有关扇区大小问题的非常好的背景信息,并让我相信问题确实是分区对齐和寻址。对于那些遇到同样问题的人,请在做任何事情之前仔细阅读它,包括评论。


在一开始的时候

我有一台电脑,需要更多空间,所以我买了一个新的 500GB 驱动器和一个 USB 外壳。很快我就注意到,如果我在外壳上对驱动器进行分区并将其移动到电脑上,它就不会识别分区(反之亦然)。我以为是外壳的问题,所以没太担心。

然后,悲剧发生了

美好的一天,我的电脑突然无法开机了。原来是主板(没有品牌,上面只印着一个大大的“中国制造”)坏了。我一直用它做文件服务器,现在那个 500GB 的硬盘里装满了我不能丢失的数据。我现在破产了,买不起新电脑,所以我唯一的希望就是那个“有缺陷的”USB 外壳。

调查

借助多个 Linux 发行版、一台笔记本电脑、VirtualBox 和机箱,我对这个问题进行了取证分析。dmesg 报告分区大小超出了驱动器末端。因此,我查看了硬盘数据表,从头开始计算扇区数,使用 dd 手动测试驱动器边界,一切看起来都正常,直到我启动 fdisk 时,它显示:

    Note: Sector size is 4096 (not 512).

fdisk 真是谦虚。这个“注释”是所有问题的根源。经过一番折腾后,得出了以下结论:

  • USB 外壳没有缺陷。

  • 至少现在坏掉的主板上的 SATA 控制器是“奇怪的”。它没有向操作系统报告 4096 字节扇区,因此操作系统很乐意使用 512 字节扇区地址创建 MBR。

  • 现在,当我尝试访问该分区时,操作系统会尝试使用 4096 字节扇区驱动器上的基于 512 字节的地址,当然,这是行不通的。

问题

  • 那么,除了在十六进制编辑器上手动编辑 MBR 之外,我怎样才能更正 MBR 中的地址,使它们在 4096 字节扇区大小上有效,以及

  • 分区未针对 4096 字节扇区进行对齐。除了从另一个驱动器复制数据外,还有其他工具可以对齐它们吗?(我没有备用驱动器),还是我需要创建一些工具来将数据一点一点地“移”到一边?分区是 ext3。

谢谢!

更新:

我发现在这个问题中有一个巧妙的方法可以使用 dd 来就地移动分区:如何在 GNU/Linux 中移动分区? 但我不知道它是否能在一个扇区的切片上工作。我现在无法测试它,但有时间我会测试的。

更新 2:

因此,我已使用上述方法成功对齐分区,并在十六进制编辑器上手动编辑了 MBR。当我重新插入硬盘时,繁荣分区自动挂载!但我不推荐这样做,因为在此过程中出现了 I/O 错误,我可能会丢失所有内容,请参阅 Rod 的回答中的评论。对于其他分区,我不会冒险,而是使用旧硬盘,通过复制数据然后将其粘贴回其他位置来一次对齐块。

答案1

扇区大小问题变得相当复杂。直到 2009 年末,绝大多数硬盘都使用 512 字节扇区,仅此而已。2009 年末,磁盘制造商开始引入所谓的高级格式(AF)磁盘,使用 4096 字节扇区。这些第一批 AF 磁盘(据我所知,目前所有 AF 磁盘)向计算机提供了一个界面,显示每个 4096 字节身体的扇区被分成八个512字节逻辑扇区。这种转换使旧工具(包括许多 BIOS)能够继续工作,这些工具都是以 512 字节假设构建的。我不知道您的磁盘是否使用 AF,但无论哪种情况,它几乎肯定使用 512 字节逻辑扇区大小,这意味着操作系统的接口应该使用 512 字节扇区。

某些 USB 磁盘盒使问题变得更加复杂。其中一些磁盘盒执行的操作与 AF 的操作相反:它们将八个磁盘扇区捆绑成一个新的 4096 字节扇区。我不确定此举背后的原因是什么,但一个实际优势是大于 2TiB 的磁盘可以与旧的 MBR 分区系统一起使用。一个主要缺点是,在这些磁盘盒之一中分区的磁盘不能直接使用,也不能在没有进行此类转换的磁盘盒中使用。同样,未经此转换准备的磁盘在转移到这样的磁盘盒中时也无法使用。请注意,这个问题远远超出了 MBR 本身;您的磁盘可能会将第一个分区标识为从(512 字节)扇区 2048 开始,但如果您的操作系统要查找(4096 字节)扇区 2048,它将不是找到该分区的开头!您遇到了这个问题。因此,您最初认为这是 USB 外壳的故障,这比您最近认为主板搞砸了的想法更接近事实。我已经绝不听说过主板以这种方式转换扇区大小。(但有些硬件 RAID 设备确实这样做。)

我不知道如何强制 Linux 调整其扇区大小的概念,但如果您有足够的磁盘空间,执行低级磁盘复制到另一个磁盘可能会有所帮助。例如:

dd if=/dev/sdb of=~/image.img

这会将您的磁盘从/dev/sdb(USB 磁盘;根据需要调整)复制到文件~/image.img。然后,您可以使用以下脚本挂载映像的分区:

#!/bin/bash
gdisk -l $1 > /tmp/mount_image.tmp
let StartSector=`egrep "^   $2|^  $2" /tmp/mount_image.tmp | fmt -u -s | sed -e 's/^[ \t]*//' | head -1 | cut -d " " -f 2`

let StartByte=($StartSector*512)

echo "Mounting partition $2, which begins at sector $StartSector"

mount -o loop,offset=$StartByte $1 $3

rm /tmp/mount_image.tmp

将脚本另存为,然后mount_image像这样使用它:

./mount_image ~/image.img 2 /mnt

这会将 的分区 2 挂载image.img/mnt。请注意,该脚本依赖于GPT fdisk ( gdisk)gptfdisk,大多数发行版都将其包含在名为或 的软件包中gdisk

从长远来看,更好的解决方案是找到一种不进行扇区大小转换的磁盘连接方法。直接连接到新主板应该可以解决问题;或者您可能可以找到不进行转换的外部机箱。事实上,有些机箱在 USB 端口上进行转换,但不在 eSATA 端口上进行转换,因此如果您的机箱有 eSATA 端口,您可以尝试使用它。我意识到这些解决方案都可能要花钱,而您说您没有钱,但也许您可以将您的转换机箱换成不进行转换的机箱。

我想到的另一种选择是尝试使用 VirtualBox 之类的虚拟机。此类工具在访问磁盘设备时可能会假设扇区大小为 512 字节,从而有效地撤消转换;或者您可能能够dd if=/dev/sdc of=/dev/sdb在虚拟机中复制磁盘的原始内容(如 ),这可能会对内容进行压缩复制,从而使映像占用的磁盘空间比原始映像占用的磁盘空间要小。

答案2

当您有突袭或加密时,此脚本概括了 Rod Smith 的提议。不作保证。请随意改进它!(使用有关 mdadm 的最新发现进行更新)

#!/bin/sh
#
# This script solve the following problem:
#
# 1. create a GPT partition on a large disk while attached directly via SATA
#    when the device present itself with 512 bytes of block size:
#    sd 3:0:0:0: [sda] 5860533168 512-byte logical blocks: (3.00 TB/2.72 TiB)
#
# 2. try to use a SATA to USB adapter like ID 067b:2773 Prolific Technology, Inc.
#    this present the device with 4096 bytes of block size:
#    sd 19:0:0:0: [sdc] 732566646 4096-byte logical blocks: (3.00 TB/2.72 TiB)
#
# 3. The kernel is unable to read correctly the partition table with
#    the USB adaper.
#
#
# With the current tools (kernel and gdisk) in debian wheezy is
# possible to use losetup to remap the partitions to loop devices so
# you can use them as usual with any filesystem, raid or crypto
#
# I still do not know if this issue is originated by the adapter or by
# the disk and if there are any others workarounds.
#
# Known version of the software:
# $ apt-show-versions linux-image-3.2.0-4-amd64
# linux-image-3.2.0-4-amd64/wheezy uptodate 3.2.54-2
# $ apt-show-versions gdisk
# gdisk/wheezy uptodate 0.8.5-1


attach_device() {

    device="$1";

    MYTMPDIR=`mktemp -d`
    trap "rm -rf $MYTMPDIR" EXIT

    # gdisk on the device use the 4096 sector size
    # but we need to force it to 512
    # this is a knwon workaround from http://superuser.com/a/679800
    # basically we make a copy of the gpt partition table on a file
    dd if="/dev/$device" bs=16384 count=1 of="$MYTMPDIR/gpt" 2> /dev/null

    # we extract the offset and the size of each partition
    #
    # FIXME: the "+ 1" seems strange, but it is needed to get the same
    #        size value from:
    #
    #        blockdev --getsize64
    #
    #        without the "+ 1" some funny things happens, for example
    #        you will not be able to start a recognized md device:
    #
    #        md: loop1 does not have a valid v1.2 superblock, not importing!
    #        md: md_import_device returned -22
    #
    #        even if
    #
    #        mdadm --examine /dev/loop1
    #
    #        does not complaint

    gdisk -l \
     "$MYTMPDIR/gpt" 2> /dev/null | \
     awk '/^ *[0-9]/ {printf "%.0f %.0f\n", $2 * 512, ($3 - $2 + 1) * 512}' > $MYTMPDIR/offset-size

    # we create a loop device with the give offset and size
    while read line;
    do
        offset=$(printf "$line" | cut -d ' ' -f 1);
        size=$(printf "$line" | cut -d ' ' -f 2);
        losetup --verbose --offset "$offset" --sizelimit "$size" `losetup -f` /dev/$device;
    done < $MYTMPDIR/offset-size;
}

detach_device() {

    device="$1";

    for loopdevice in `losetup -a | grep "$device" | cut -d : -f 1`;
    do
        losetup --verbose --detach "$loopdevice";
    done;
}

usage() {
cat <<EOF
Usage:
- $0 -h to print this help
- $0 sda to attach the gpt partitions of sda
- $0 -d sda to detach the gpt partitions of sda
EOF
}


detach=0;

while getopts hd action
do
    case "$action" in
        d) detach=1;;
        h) usage;;
    esac
done
shift $(($OPTIND-1))

if [ $# -ne 1 ];
then
    usage;
fi

if [ "x$detach" = "x0" ]; then
    attach_device $1;
else
    detach_device $1;
fi

答案3

另一种相当直接的方法是使用 parted 的救援功能。但这需要您创建一个新的磁盘标签,因此存在风险。Parted 直接作用于磁盘,因此在运行 parted 之前请根据需要进行备份。然后启动:

parted /dev/sdb

当尝试读取具有与创建分区表时不同的扇区大小的磁盘时,parted 将告诉您类似的内容:

Error: /dev/sdb: unrecognised disk label                                  

根据您之前使用的内容使用 mklabel 创建新的 MBR 或 GPT

(parted) mklabel
New disk label type? mbr

然后运行 ​​rescue 来找到你的旧分区

(parted) rescue
Start? 0
End? 4001GB
Information: A ext4 primary partition was found at 1049kB -> 2000GB.  Do you
want to add it to the partition table?
Yes/No/Cancel? y

如果您有更多分区,请重复救援过程。您现在已完成。

答案4

当我从 WD My Book 外置硬盘盒中取出 4TB 磁盘时,遇到了这个问题。问题是:

  1. MBR 分区表偏离了 8 倍,并且
  2. 当扇区大小为 512 时,MBR 分区表无法处理 >2TB 的数据。

解决方案:将分区表重写为 GPT,并将值转换为使用 512 字节扇区。

在我的例子中,分区从 1MB 偏移开始,并在磁盘末尾之前结束(~856kB)。这很好,因为它允许在分区之前使用 MBR+GPT(17408 字节),在磁盘末尾使用备份 GPT(16896 字节)。

我制作了两个区域的图像以防万一(使用 dd)。

我注意到了 的输出fdisk -l /dev/sde

我使用 gdisk 删除了第一个分区。如果您愿意,可以像我一样将对齐值更改为 8(4096)以尽可能多地使用空间。然后,我创建了一个新分区,起始位置为 2048,结束位置为磁盘末尾。稍后我会扩展文件系统。

值得庆幸的是,扇区大小的变化不会影响文件系统、LVM 或 LUKS。

相关内容