Linux가 파티션을 감지하지 못하도록 방지

Linux가 파티션을 감지하지 못하도록 방지

부팅 시스템에서 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블록을 동적으로 비활성화/재활성화할 수도 있습니다.01

에서 테스트했지만 에서는 테스트 AMD64하지 않았습니다 . 코드는 함수 인수에 해당하는 레지스터 선택에 의존합니다. 이는 아키텍처에 따라 정의되며 정확하지 않을 수 있습니다.x86ARM

모듈의 코드는 다음과 같습니다.

#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다시 활성화할 때까지 기다려야 합니다.

관련 정보