UnRaid使用多LUN磁盘(如ExOS 2x14)

大类
Env
技术标签
逆向-Linux
环境增强
优先级
Low
开始日期
Oct 1, 2023
状态
Maintaining
Public
Public
最后更新
Oct 2, 2023

介绍

这类盘有多个磁头(多LUN),以ExOS 2x14为例,经过SAS背板的处理之后会expose给OS两个2个7TB的block device
# root @ MistyNAS-School in ~ [18:16:55] $ fdisk -l /dev/sde Disk /dev/sde: 6.37 TiB, 7000259821568 bytes, 1709047808 sectors Disk model: ST14000NM0001 Units: sectors of 1 * 4096 = 4096 bytes Sector size (logical/physical): 4096 bytes / 4096 bytes I/O size (minimum/optimal): 4096 bytes / 4096 bytes # root @ MistyNAS-School in ~ [18:17:14] $ fdisk -l /dev/sdf Disk /dev/sdf: 6.37 TiB, 7000259821568 bytes, 1709047808 sectors Disk model: ST14000NM0001 Units: sectors of 1 * 4096 = 4096 bytes Sector size (logical/physical): 4096 bytes / 4096 bytes I/O size (minimum/optimal): 4096 bytes / 4096 bytes
notion image

使用思路 - 同盘 R0 后再给其他raid方案用

由于这两个block device实际上还是同一个盘,坏了一般也是同时坏,所以不好直接丢给其他raid来用。
选择了一波之后选了LVM提供的striped volume,这是因为LVM相比mdadm方便管理,并且UnRAID里面只有LVM可用(mdadm被unraid魔改了)
 

创建LVM Pool

UnRAID里面的各种名称都有长度限制,所以名称要尽可能短。
我这边选择的命名方式是v-[Serial末8位]/lv,这样可以有效区分各个盘。
vgcreate v-C20729ZJ /dev/sdg /dev/sdh lvcreate -l 100%Vg -i 2 -I 128k -n lv v-C20729ZJ -v

让UnRAID添加LVM LV

UnRAID并不支持手动把设备添加到Array

UnRAID并不支持挂载LV。从内部看,UnRAID挂载是通过mdcmd import实现的,具体的逻辑可以再unraid kernel的md_unraid.c里面找到。
mdcmd (2): import 1 sdb 64 13672382412 0 TOSHIBA_MG07ACA14TE_21P0A8UMF94G
对应语法
mdcmd import [slot] [name] [offset] [size] [erased] [id]
mdcmd会把当前的array信息保存到/boot/config/super.dat中(实际上就是superblock)
不过不要以为直接自己mdcmd import就可以万事大吉了。emhttpd跟mdcmd的返回高度耦合,他会持续监测/proc/mdstat并同步状态,如果状态不一样就会导致内部的device数组下标越界,直接SEGFAULT,整个界面都没法使用。
同时mdcmd只是负责提供底层的RAID,上层的filesystem挂载还是由emhttpd负责,所以我们绝对要保证emhttpd正常运行。

Emhttpd是怎么识别设备的?

emhttpd的设备识别流程非常简单粗暴,都是直接对着/sys/dev/block目录遍历然后匹配符号连接的路径。
  1. opendir + readdir64遍历/sys/dev/block,解析每个符号连接的路径
    1. notion image
  1. 匹配这些路径,找到里面不同类型设备的路径pattern,目前unraid 6.12.4匹配了这些pattern
      • nvme/nvme
      • block/sd
      • block/hd
      • block/vd
  1. 匹配到路径后,readlink取出最后末尾的sdX、hdX、vdX、nvmeX等,和/dev/拼接,得到/dev/sdX这样的device path
  1. 然后找到每个device的SerialID:scandir64遍历/dev/disk/by-id,readlink后和上一步的name匹配,然后再匹配路径的开头pattern里面取出来id
      • 目前有这些:ata-、scsi-SATA_、scsi-1ATA_、scsi-、usb-、nvme-、virtio-
      notion image
  1. 每次重启,emhttpd根据superblock中保存的disk id,找到正确的device,然后将对应device mdcmd import进去完成配置

强行让emhttpd识别LVM LV

我试了一下强行hook掉opendir、readdir64、readlink、closedir、scandir64来做全流程的劫持,这样可以避免对二进制进行patch,从而避免固定offset。但是emhttpd里面全是在使用/dev/XXXXX这样来引用device,这导致虽然可以用各种方法来在第一步插入device,但是却不好在后面解析device id的时候做patch。
思考再三发现可以通过patch程序中的匹配字符串来解决这个问题。考虑到大家不太可能把2x14直通给unraid跑,所以我直接把上面的/block/vd和相对应的virtio-这两个pattern修改,改为lvm lv的对应pattern:/block/dm 、dm-name-
由于patch的空间不够,所以我只用dm-nam来做pattern,这样生成出来的LVM id就是dm-name-XXXXX截断为e-XXXXX,也算是不错。
 
虽然这样可以过掉大部分的逻辑,但是emhttpd鸡贼的多加了个限制,限制dm后面的字符必须是字母或数字,而我们的device是/dev/dm-X,横杠不满足这个条件。
对于这个问题我选择hook掉__ctype_b_loc(),让所有的字符都变成字母数字的flag,这样就能避免问题了。
notion image
但是全部都是字母数字会导致前面过滤/dev/sdXY这样的逻辑失效,所以我们加了额外的限制,通过判断返回地址来决定需不需要修改逻辑:
notion image

让UnRAID挂载LVM LV

虽然添加能够正常成功了,但是挂载仍然会提示Unmountable。
  • format的时候,他会提示no pool importable,format完了之后并不能成功挂载。
    • 手动执行类似zpool create:
    • zpool create disk4 /dev/md4p1
    • 然后再次format即可成功挂载
  • 虽然成功挂载,但是stop后再start仍然会有问题
    • 提示Unsupported partition layout
    • 具体原因有两个
        1. sgdisk计算MBR分区表起始C/H/S时有bug,导致本应该是00 02 00,变成了fe ff ff
          1. echo -n '\x00\x02\x00' | dd of=/dev/dm-1 bs=1 seek=447
        1. /dev/dm-X不像其他的block device,不会自动创建sda1、nvme1p1这样的分区设备名,需要手动创建
            • 可以用partx -v -u /dev/dm-X来创建,写一个自动启动的while循环来自动定时partx即可
    • 写个循环自动把上面两个fix循环apply即可
 

问题记录

提示Configuration Invalid,无法Start

去Tools → New Config,勾选All保留所有设置,apply即可清除错误,重新挂载