ブートシステムでHDDが検出されたときにすべての種類のパーティション検索を回避し、基本ドライブ機能(モデル、セクタサイズ、容量など)のみを検出してデバイスファイルのみを生成できるようにするにはどうすればよいですか/dev/sda
。 libata/カーネルの問題ですか?
ハードドライブが破損しているため、パーティションテーブルに対応するセクタを読み取ることができないデータ復旧の経験に関するものです。サイレントインストールについて話すのではなく、すでに無効になっています。
パーティションに接続されたセクタは、傷のある領域に位置する。頭がそこを通り過ぎると、ドライブがクラッシュします。
通常のHDDを接続すると、次のことが起こります。
メッセージ出力
Oct 13 16:21:42 wks-01 kernel: [ 906.796660] sd 8:0:0:0: [sdb] 1953525167 512-byte logical blocks: (1.00 TB/931 GiB)...
Oct 13 16:21:42 wks-01 kernel: [ 906.915646] sdb: sdb1
(最後のルーチンを無効にする必要があります)
Udev出力
KERNEL[906.915935] add /devices/pci0000:00/0000:00:1d.7/usb8/8-3/8-3:1.0/host8/target8:0:0/8:0:0:0/block/sdb (block)<br>
KERNEL[906.915999] add /devices/pci0000:00/0000:00:1d.7/usb8/8-3/8-3:1.0/host8/target8:0:0/8:0:0:0/block/sdb/**sdb1** (block) (**I need to disable this routine)** ... <br>
UDEV [907.392087] add /devices/pci0000:00/0000:00:1d.7/usb8/8-3/8-3:1.0/host8/target8:0:0/8:0:0:0/block/sdb/sdb1 (block)
答え1
更新:より迅速で安全な方法を公開しました。次のようなカーネルモジュールの使用
@grochmalが指摘したように、これを行う組み込みの方法はありませんが、自分のカーネルをコンパイルしたい場合は非常に簡単です。
ファイルのblock/partition-generic.c
関数の前に次のコードを追加しますrescan_partitions
。
int do_partscan = 1;
static const struct kernel_param_ops do_partscan_param_ops = {
.set = param_set_int,
.get = param_get_int,
};
module_param_cb(do_partscan, &do_partscan_param_ops, &do_partscan, 0644);
そして、関数の先頭に次のコードを挿入してください。
if (!do_partscan) {
bdev->bd_invalidated = 0;
return 0;
}
これにより、パーティション検索を切り替えるために使用できるモジュールパラメータが提供されます/sys/module/...
。任意の検索に設定すると、0
パーティションなしですぐに返されます。必要に応じて再び反転して1
実行して、blockdev --rereadpt <device>
ディスクに接続されているパーティションをロードできます。
/sys/module
[パラメータがツリーのどこにあるかわかりません。 find itを使用してくださいfind /sys/module -name do_partscan
。設定すればいいと思います。
#undef MODULE_PARAM_PREFIX
#define MODULE_PARAM_PREFIX "block."
module_param_cb ...
コードに入れる前に/sys/module/block/parameters/do_partscan
このようにテストしたことはありません。 ]
答え2
方法がない
UDEVの観点からは、パーティションueventは間接的ではなくカーネルから直接転送されます。
カーネル側で何が起こるかは次のとおりです。__blkdev_get()
常に少なくとも一部のパーティションテーブルを読み込みます。disk_get_part()
。これにより、パーティションテーブルが十分に読み取られ、パーティションテーブルがどのタイプであるかがわかります。
あなたができることは、CONFIG_MSDOS_PARTITION
カーネルのコンパイル中に設定を解除することだけです。msdos_partition()
内部的には使用されませんcheck_partition()
。disk_get_part()
これに比べてどのくらいのパーティションを読み取るのか分かりません。
ノート
- これは、ディスクがMSDOSパーティションを使用していると想定しています。その中には他にもいくつかの
CONFIG_*_PARTIOTION
パラメータがあります。/block/partitions/check.c
。 - バックアップしたいパーティションタイプとは異なるパーティションタイプを使用するドライブからカーネルを起動する必要があります。これは面倒かもしれません(現在のGPTパーティションはかなり可能です)。
- 別の方法はおそらくそうかもしれません
rmmod scsi
。ただし、これを行うには、SCSIサブシステムは何も必要としません。これを達成する唯一の方法は、ネットワークを介してリリースすることです。その後、破損したディスクをmodprobe scsi
リカバリサブシステムに接続し、mknod
手動でノードを作成できます()。これは家庭的であり(試したことがない)、これがすべての努力を引き起こすmknod
ことができないかどうかはわかりません。__blkdev_get()
答え3
私が見つけた最高のソリューションはカーネル2.6
以上で動作し、kretprobe
インターセプトadd_disk
機能を使用してパーティションの読み取りをブロックするフラグを設定することで動作します。関数が返されると、フラグは元の状態に復元されます。これにより、後で(などを使用して)パーティションを手動で読み取ることができますpartprobe
。
この方法を使用すると、ステータスをまたはに設定して/sys/module/no_partscan/parameters/enabled
ブロックを動的に無効に/無効にすることもできます。0
1
でテストしたが、ではテストAMD64
していません。コードは、関数引数に対応するレジスタの選択に依存します。これはアーキテクチャによって定義され、正確ではない可能性があります。x86
ARM
モジュールのコードは次のとおりです。
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/genhd.h>
#ifdef CONFIG_X86_32
#define ARG1 ax
#define ARG2 bx
#elif defined CONFIG_X86_64
#define ARG1 di
#define ARG2 si
#elif defined CONFIG_ARM || CONFIG_ARM64
#define ARG1 regs[0]
#define ARG2 regs[1]
#endif
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,7,10)
#define ARG ARG1
static char func_name[NAME_MAX] = "add_disk";
#else // after 4.7.10, add_disk is a macro pointing to device_add_disk, which has the disk as its 2nd argument
#define ARG ARG2
static char func_name[NAME_MAX] = "device_add_disk";
#endif
static int enabled = 1;
module_param(enabled, int, 0664);
MODULE_PARM_DESC(enabled, "Enable intercepting disk initializing so we can block partscan.");
struct instance_data {
struct gendisk *disk;
};
static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
struct instance_data *data;
struct gendisk *disk;
data = (struct instance_data *)ri->data;
disk = (struct gendisk *)(regs->ARG);
if (!enabled || disk->flags & GENHD_FL_NO_PART_SCAN) {
data->disk = NULL;
} else {
pr_warn("Intercepted partition read for disk: %s.\n", disk->disk_name);
disk->flags |= (GENHD_FL_NO_PART_SCAN);
data->disk = disk; // store this so we can remove the NO_PARTSCAN flag on function return
}
return 0;
}
static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
struct instance_data *data;
data = (struct instance_data *)ri->data;
if (data->disk)
data->disk->flags &= ~(GENHD_FL_NO_PART_SCAN);
return 0;
}
static struct kretprobe my_kretprobe = {
.handler = ret_handler,
.entry_handler = entry_handler,
.data_size = sizeof(struct instance_data),
.maxactive = 20,
};
static int __init kretprobe_init(void)
{
int ret;
my_kretprobe.kp.symbol_name = func_name;
ret = register_kretprobe(&my_kretprobe);
if (ret < 0) {
pr_warn("register_kretprobe failed, returned %d\n", ret);
return ret;
}
return 0;
}
static void __exit kretprobe_exit(void)
{
unregister_kretprobe(&my_kretprobe);
pr_info("kretprobe at unregistered\n");
/* nmissed > 0 suggests that maxactive was set too low. */
if (my_kretprobe.nmissed) pr_warn("Missed probing %d instances.\n", my_kretprobe.nmissed);
}
module_init(kretprobe_init)
module_exit(kretprobe_exit)
MODULE_LICENSE("GPL");
livepatch
他の答えのモジュールのように構築して接続することができます。
答え4
Linuxカーネル4以降では、livepatch
モジュールを使用してパーティションテーブルの読み取りを防ぐことができます。これはカーネル全体を再コンパイルするよりも高速で簡単です。私はlivepatch
マクロを使用してさまざまな形式のAPIを扱うモジュールを作成しましたIF
。
これは5.13.0-28
以下でテストされましたUbuntu Server 20.10
。他のカーネルを試していませんでした。コードが正しくない可能性があります。
これはno_partscan.c
:
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/livepatch.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#if LINUX_VERSION_CODE <= KERNEL_VERSION(5,4,179)
static int livepatch_rescan_partitions(struct gendisk *disk, struct block_device *bdev)
{
pr_warn("Intercepted partition read for disk: %s.\n", disk->disk_name);
return -EIO;
}
static struct klp_func funcs[] = {
{
.old_name = "rescan_partitions",
.new_func = livepatch_rescan_partitions,
}, { }
};
#else
static int livepatch_blk_add_partitions(struct gendisk *disk)
{
pr_warn("Intercepted partition read for disk: %s.\n", disk->disk_name);
return 0;
}
#endif
static struct klp_func funcs[] = {
{
.old_name = "blk_add_partitions",
.new_func = livepatch_blk_add_partitions,
}, { }
};
static struct klp_object objs[] = {
{
/* name being NULL means vmlinux */
.funcs = funcs,
}, { }
};
static struct klp_patch patch = {
.mod = THIS_MODULE,
.objs = objs,
};
#if LINUX_VERSION_CODE <= KERNEL_VERSION(5,0,21)
static int livepatch_init(void)
{
int ret;
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,15,18) && LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,12)
if (!klp_have_reliable_stack() && !patch.immediate) {
// Use of this option will also prevent removal of the patch.
// See Documentation/livepatch/livepatch.txt for more details.
patch.immediate = true;
pr_notice("The consistency model isn't supported for your architecture. Bypassing safety mechanisms and applying the patch immediately.\n");
}
#endif
ret = klp_register_patch(&patch);
if (ret)
return ret;
ret = klp_enable_patch(&patch);
if (ret) {
WARN_ON(klp_unregister_patch(&patch));
return ret;
}
return 0;
}
static void livepatch_exit(void)
{
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,11,12)
WARN_ON(klp_disable_patch(&patch));
#endif
WARN_ON(klp_unregister_patch(&patch));
}
#else
static int livepatch_init(void)
{
return klp_enable_patch(&patch);
}
static void livepatch_exit(void)
{
}
#endif
module_init(livepatch_init);
module_exit(livepatch_exit);
MODULE_LICENSE("GPL");
MODULE_INFO(livepatch, "Y");
以下で使用できますMakefile
。
obj-m := no_partscan.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
(このMakefileを実行中に「Missing delimiter」と表示された場合は、4つのスペースをタブに置き換えます。)
その後、パーティションの読み取りをブロックする準備ができたら、次を使用します。
sudo insmod no_partscan
パッチを無効にするには、次を使用します。
echo 0 > /sys/kernel/livepatch/no_partscan/enabled
これによりパッチが削除されますが、まだモジュールとしてインストールされます。もう一度実行しsudo rmmod no_partscan.ko
てinsmod
再度有効にするまで待つ必要があります。