飞牛NAS CLI手动创建EXT4存储空间及逆向研究报告
为什么要逆向研究实现手动创建存储空间?
官方 Web UI 采用高度封装的向导式交互,虽降低了入门门槛,但严格限制了底层存储拓扑的灵活性。在实际场景中,手动创建存储空间具有不可替代的价值:
- 单盘多池划分:将一块大容量硬盘拆分为多个独立存储池,分别用于冷数据归档、Docker 持久化卷或虚拟机镜像,实现物理隔离与独立配额。
- 系统崩溃救援:当 Web UI 无法访问、数据库损坏或遭遇异常断电时,手动重建存储栈是抢救数据的唯一可靠途径。
- 规避自动装配干扰:NAS 后台守护进程可能误识别旧 RAID 签名导致卷状态异常。掌握底层逻辑可强制清理并精确对齐。
- 自动化运维基础:理解从裸盘到
/volX的完整路径,是编写批量初始化脚本、集成 CI/CD 或进行深度性能调优的前提。
以下为经过完整验证的逐步骤操作实录。请严格按照顺序执行,并将 /dev/sdb 替换为您的实际目标磁盘。
前置检查:确认目标磁盘状态
在执行任何破坏性操作前,必须确认磁盘路径且未被系统占用。
# 查看当前块设备拓扑
lsblk
预期输出:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 20G 0 disk
├─sda1 8:1 0 94M 0 part
├─sda2 8:2 0 14.9G 0 part /
└─sda3 8:3 0 5G 0 part
└─md127 9:127 0 5G 0 raid1
└─trim_...-0 253:0 0 5G 0 lvm /vol1
sdb 8:16 0 3G 0 disk
说明:确认 /dev/sdb 无分区、无挂载点。若显示已挂载,请先执行 umount /dev/sdb*。
步骤一:彻底清除磁盘残留签名
# 强制卸载可能残留的挂载点
umount /dev/sdb* 2>/dev/null || true
# 擦除所有旧分区表、文件系统与 RAID 超级块
wipefs -a /dev/sdb
预期输出:
/dev/sdb: 8 bytes were erased at offset 0x00000200 (gpt): 45 46 49 20 50 41 52 54
/dev/sdb: 2 bytes were erased at offset 0x000001fe (PMBR): 55 aa
说明:此操作不可逆。确保未误选系统盘。
步骤二:创建 GPT 分区并设置 Linux RAID 类型
# 创建占满全盘的单一分区,类型设为 FD00
sgdisk -n 1:0:0 -t 1:FD00 -c 1:"FnOS_Data" /dev/sdb
# 通知内核刷新分区表并等待设备节点生成
partprobe /dev/sdb
udevadm settle
sleep 2
预期输出:
Creating new GPT entries in memory.
The operation has completed successfully.
说明:类型 FD00 是底层 mdadm 识别的必要条件。udevadm settle 确保 /dev/sdb1 节点已就绪。
步骤三:构建单盘 RAID1 阵列
# 停止可能自动组装的冲突阵列
mdadm --stop /dev/md99 2>/dev/null || true
# 再次清理分区签名,防止 mdadm 拒绝创建
wipefs -a /dev/sdb1
# 创建单盘 RAID1
mdadm --create /dev/md99 --level=1 --raid-devices=1 --metadata=1.2 --name="test:0" --force /dev/sdb1
预期输出:
mdadm: array /dev/md99 started.
说明:飞牛NAS使用单盘 RAID1 作为设备抽象层,以便后续无缝插入第二块硬盘扩容。--metadata=1.2 和 --name="test:0" 是后端服务硬编码的识别特征。
# 验证阵列状态
cat /proc/mdstat
预期输出:
Personalities : [raid1]
md99 : active raid1 sdb1[0]
3141568 blocks super 1.2 [1/1] [U]
步骤四:初始化 LVM 物理卷与卷组
# 生成符合规范的 UUID(横杠转下划线)
RAW_UUID=$(cat /proc/sys/kernel/random/uuid)
VG_NAME="trim_$(echo $RAW_UUID | tr '-' '_')"
echo "生成的卷组名: $VG_NAME"
# 创建物理卷(将提示确认覆盖旧签名)
pvcreate -ff /dev/md99
预期输出:
生成的卷组名: trim_e153b892_b7db_492f_916e_09e9ad5b7d83
Really INITIALIZE physical volume "/dev/md99" of volume group "..." [y/n]? y
Physical volume "/dev/md99" successfully created.
说明:此处需手动输入 y 并回车。VG 名称必须以 trim_ 开头,后接 UUID 格式,否则 Web 端扫描时将直接过滤。
# 创建卷组
vgcreate $VG_NAME /dev/md99
预期输出:
Volume group "trim_e153b892_b7db_492f_916e_09e9ad5b7d83" successfully created
步骤五:创建逻辑卷 (LV)
# 创建逻辑卷,名称必须为 0
lvcreate -l 100%FREE -n 0 $VG_NAME
预期输出:
WARNING: ext4 signature detected on /dev/.../0 at offset 1080. Wipe it? [y/n]: y
Wiping ext4 signature on /dev/.../0.
Logical volume "0" created.
说明:LV 名称固定为 0。系统将自动在 /dev/mapper/ 下生成 ${VG_NAME}-0 符号链接。
步骤六:格式化 ext4 并开启项目配额 (prjquota)
# 定义设备映射路径变量
DEV_MAP="/dev/mapper/${VG_NAME}-0"
# 格式化 ext4,块大小强制 4096
mkfs.ext4 -F -b 4096 $DEV_MAP
# 离线启用 project 特性
tune2fs -O project $DEV_MAP
# 重建文件系统元数据索引
e2fsck -f $DEV_MAP
预期输出:
mke2fs 1.47.0 (5-Feb-2023)
Creating filesystem with 784384 4k blocks...
Setting project quota feature on the filesystem.
e2fsck 1.47.0 (5-Feb-2023)
...
/dev/mapper/...-0: 11/196224 files
说明:tune2fs -O project 是核心步骤。缺失此特性会导致挂载参数无法加载 prjquota,进而触发 Web 端配额 API 拦截。
步骤七:挂载并严格同步系统权限
# 创建挂载点
mkdir -p /vol2
# 挂载,必须显式指定 prjquota 参数
mount -o rw,relatime,prjquota $DEV_MAP /vol2
# 获取系统卷 /vol1 的标准权限与所有者
VOL1_PERM=$(stat -c '%a' /vol1)
VOL1_OWNER=$(stat -c '%U:%G' /vol1)
# 同步至新卷
chown -R ${VOL1_OWNER} /vol2
chmod ${VOL1_PERM} /vol2
# 验证权限
ls -ld /vol2
预期输出:
drwx------ 4 root root 4096 Apr 5 15:35 /vol2
说明:系统卷根目录权限为 700 root:root。若手动创建后为默认的 755,后端文件服务将拒绝读取。
此时可以在web端看到我们创建的存储空间(67b-0)

步骤八:注入系统“指纹”目录
# 查看系统卷根目录结构
ls /vol1
# 递归复制指纹目录(-a 保留原始权限、时间戳与属性)
cp -a /vol1/lost+found /vol2/
cp -a /vol1/thumb /vol2/
cp -a /vol1/1000 /vol2/
预期输出:
1000 lost+found thumb
说明:Web 端文件管理器通过检查这三个目录判断卷是否完成初始化。
lost+found:ext4 标准目录,校验权限完整性。thumb:缩略图缓存根目录。缺失时 API 判定卷未就绪。1000:默认管理员(UID 1000)空间映射,用于建立用户索引。
此时刷新 Web UI 存储池与文件管理器页面,新卷应完全可用,显示为存储空间X。

关键技术约束对照表
| 层级 | 强制要求 | 偏离后果 |
|---|---|---|
| 分区表 | GPT,类型 FD00 (Linux RAID) |
mdadm 拒绝组装或内核不识别 |
| RAID元数据 | mdadm v1.2,单盘 --force,名称 test:0 |
后端正则过滤失败,卷不可见 |
| LVM命名 | VG前缀 trim_+UUID转下划线;LV名必须为 0 |
filestor_service 扫描丢弃 |
| 文件系统 | ext4,Block Size 4096,启用 project 特性 |
prjquota 挂载失败,配额API拦截 |
| 挂载参数 | rw,relatime,prjquota |
Web端返回“内部错误”或拒绝访问 |
| 根目录指纹 | 必须存在 lost+found、thumb、1000 且权限为 700 root:root |
文件管理器初始化中断 |
安全与运维警告
- 路径二次确认:所有命令中的
/dev/sdb必须与lsblk输出严格一致。误操作将导致系统盘或数据盘永久损毁。 - 无物理冗余:
--raid-devices=1仅为统一设备抽象层,不提供硬盘级容灾。重要数据需定期冷备份。 - 数据库清理:若多次创建失败,内部 SQLite 可能残留错误状态。需定位
/var/lib/fnos/*.db并清理volumes表中状态为error的记录。 - 版本差异:本流程基于当前版本逆向验证。大版本更新可能调整服务校验逻辑或引入 ZFS/Btrfs,生产环境请先在测试盘验证。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)