mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-29 00:51:53 +08:00
init
This commit is contained in:
531
libs/ventoy-img-rs/docs/CLI.md
Normal file
531
libs/ventoy-img-rs/docs/CLI.md
Normal file
@@ -0,0 +1,531 @@
|
||||
# ventoy-img CLI 使用说明
|
||||
|
||||
## 安装
|
||||
|
||||
### 从源码编译
|
||||
|
||||
```bash
|
||||
# 克隆仓库
|
||||
git clone https://github.com/user/ventoy-img-rs.git
|
||||
cd ventoy-img-rs
|
||||
|
||||
# 编译 release 版本
|
||||
cargo build --release
|
||||
|
||||
# 二进制文件位于 target/release/ventoy-img
|
||||
```
|
||||
|
||||
### 直接使用
|
||||
|
||||
```bash
|
||||
# 复制到 PATH
|
||||
sudo cp target/release/ventoy-img /usr/local/bin/
|
||||
|
||||
# 或添加别名
|
||||
alias ventoy-img='/path/to/ventoy-img-rs/target/release/ventoy-img'
|
||||
```
|
||||
|
||||
## 命令概览
|
||||
|
||||
```
|
||||
ventoy-img <COMMAND>
|
||||
|
||||
Commands:
|
||||
create 创建新的 Ventoy IMG 文件
|
||||
add 添加文件到镜像(支持子目录和覆盖)
|
||||
list 列出镜像中的文件(支持递归列出)
|
||||
remove 从镜像删除文件或目录(支持递归删除)
|
||||
mkdir 创建目录(支持递归创建父目录)
|
||||
info 显示镜像信息
|
||||
help 显示帮助信息
|
||||
```
|
||||
|
||||
## 命令详解
|
||||
|
||||
### create - 创建镜像
|
||||
|
||||
创建新的 Ventoy 可启动 IMG 文件。
|
||||
|
||||
```bash
|
||||
ventoy-img create [OPTIONS]
|
||||
```
|
||||
|
||||
**选项:**
|
||||
|
||||
| 选项 | 简写 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `--size` | `-s` | `8G` | 镜像大小 |
|
||||
| `--output` | `-o` | `ventoy.img` | 输出文件路径 |
|
||||
| `--label` | `-L` | `Ventoy` | 数据分区卷标 |
|
||||
|
||||
**大小格式:**
|
||||
- `G` 或 `GB`: 千兆字节,如 `8G`, `16G`
|
||||
- `M` 或 `MB`: 兆字节,如 `512M`, `1024M`
|
||||
- 纯数字: 字节数,如 `8589934592`
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 创建 8GB 镜像(默认)
|
||||
ventoy-img create
|
||||
|
||||
# 创建 16GB 镜像,指定输出路径
|
||||
ventoy-img create -s 16G -o /path/to/my-ventoy.img
|
||||
|
||||
# 创建 512MB 小镜像,自定义卷标
|
||||
ventoy-img create -s 512M -o small.img -L "MyUSB"
|
||||
|
||||
# 创建 32GB 镜像
|
||||
ventoy-img create --size 32G --output ventoy-32g.img --label "Ventoy32"
|
||||
```
|
||||
|
||||
**输出示例:**
|
||||
|
||||
```
|
||||
========================================
|
||||
Ventoy IMG Creator (Rust Edition)
|
||||
========================================
|
||||
|
||||
[INFO] Creating 8192MB image: ventoy.img
|
||||
[INFO] Writing boot code...
|
||||
[INFO] Writing MBR partition table...
|
||||
Data partition: sector 2048 - 16744447 (8160 MB)
|
||||
EFI partition: sector 16744448 - 16809983 (32 MB)
|
||||
[INFO] Writing Ventoy signature...
|
||||
[INFO] Writing EFI partition...
|
||||
[INFO] Formatting data partition as exFAT...
|
||||
[INFO] Ventoy IMG created successfully!
|
||||
|
||||
========================================
|
||||
Image: ventoy.img
|
||||
Size: 8G
|
||||
Label: Ventoy
|
||||
========================================
|
||||
```
|
||||
|
||||
### add - 添加文件
|
||||
|
||||
将 ISO/IMG 文件添加到 Ventoy 镜像的数据分区。
|
||||
|
||||
```bash
|
||||
ventoy-img add [OPTIONS] <IMAGE> <FILE>
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `IMAGE`: Ventoy IMG 文件路径
|
||||
- `FILE`: 要添加的文件路径
|
||||
|
||||
**选项:**
|
||||
|
||||
| 选项 | 简写 | 说明 |
|
||||
|------|------|------|
|
||||
| `--dest` | `-d` | 目标路径(支持子目录,如 `iso/linux/ubuntu.iso`) |
|
||||
| `--force` | `-f` | 覆盖已存在的文件 |
|
||||
| `--parents` | `-p` | 自动创建父目录 |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 添加单个 ISO 到根目录
|
||||
ventoy-img add ventoy.img ubuntu-22.04-desktop-amd64.iso
|
||||
|
||||
# 添加到子目录(目录必须存在)
|
||||
ventoy-img add ventoy.img ubuntu.iso -d iso/linux/ubuntu.iso
|
||||
|
||||
# 添加到子目录并自动创建父目录
|
||||
ventoy-img add ventoy.img ubuntu.iso -d iso/linux/ubuntu.iso -p
|
||||
|
||||
# 覆盖已存在的文件
|
||||
ventoy-img add ventoy.img new-ubuntu.iso -d iso/linux/ubuntu.iso -f
|
||||
|
||||
# 组合使用:创建目录 + 覆盖
|
||||
ventoy-img add ventoy.img ubuntu.iso -d iso/linux/ubuntu.iso -p -f
|
||||
```
|
||||
|
||||
**批量添加(使用 shell):**
|
||||
|
||||
```bash
|
||||
# 添加目录下所有 ISO 到根目录
|
||||
for iso in /path/to/isos/*.iso; do
|
||||
ventoy-img add ventoy.img "$iso"
|
||||
done
|
||||
|
||||
# 添加到子目录并保持目录结构
|
||||
for iso in /path/to/isos/*.iso; do
|
||||
ventoy-img add ventoy.img "$iso" -d "iso/$(basename "$iso")" -p
|
||||
done
|
||||
```
|
||||
|
||||
### list - 列出文件
|
||||
|
||||
列出镜像数据分区中的文件。
|
||||
|
||||
```bash
|
||||
ventoy-img list [OPTIONS] <IMAGE>
|
||||
```
|
||||
|
||||
**选项:**
|
||||
|
||||
| 选项 | 简写 | 说明 |
|
||||
|------|------|------|
|
||||
| `--path` | | 指定要列出的目录路径 |
|
||||
| `--recursive` | `-r` | 递归列出所有文件和目录 |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 列出根目录
|
||||
ventoy-img list ventoy.img
|
||||
|
||||
# 列出指定目录
|
||||
ventoy-img list ventoy.img --path iso/linux
|
||||
|
||||
# 递归列出所有文件
|
||||
ventoy-img list ventoy.img -r
|
||||
```
|
||||
|
||||
**输出示例(根目录):**
|
||||
|
||||
```
|
||||
NAME SIZE TYPE
|
||||
------------------------------------------------------------
|
||||
ubuntu-22.04-desktop-amd64.iso 3.6 GB FILE
|
||||
iso 0 B DIR
|
||||
```
|
||||
|
||||
**输出示例(递归):**
|
||||
|
||||
```
|
||||
PATH SIZE TYPE
|
||||
----------------------------------------------------------------------
|
||||
iso 0 B DIR
|
||||
iso/linux 0 B DIR
|
||||
iso/linux/ubuntu.iso 3.6 GB FILE
|
||||
iso/windows 0 B DIR
|
||||
iso/windows/win11.iso 5.2 GB FILE
|
||||
```
|
||||
|
||||
**空镜像输出:**
|
||||
|
||||
```
|
||||
No files in image
|
||||
```
|
||||
|
||||
### remove - 删除文件或目录
|
||||
|
||||
从镜像中删除指定文件或目录。
|
||||
|
||||
```bash
|
||||
ventoy-img remove [OPTIONS] <IMAGE> <PATH>
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `IMAGE`: Ventoy IMG 文件路径
|
||||
- `PATH`: 要删除的文件或目录路径
|
||||
|
||||
**选项:**
|
||||
|
||||
| 选项 | 简写 | 说明 |
|
||||
|------|------|------|
|
||||
| `--recursive` | `-r` | 递归删除目录及其内容 |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 删除根目录的文件
|
||||
ventoy-img remove ventoy.img ubuntu.iso
|
||||
|
||||
# 删除子目录中的文件
|
||||
ventoy-img remove ventoy.img iso/linux/ubuntu.iso
|
||||
|
||||
# 删除空目录
|
||||
ventoy-img remove ventoy.img iso/empty-dir
|
||||
|
||||
# 递归删除目录及其所有内容
|
||||
ventoy-img remove ventoy.img iso -r
|
||||
|
||||
# 文件名大小写不敏感
|
||||
ventoy-img remove ventoy.img ISO/LINUX/UBUNTU.ISO
|
||||
```
|
||||
|
||||
**注意:**
|
||||
- 删除非空目录时必须使用 `-r` 选项
|
||||
- 递归删除会删除目录下的所有文件和子目录
|
||||
|
||||
### mkdir - 创建目录
|
||||
|
||||
在镜像中创建目录。
|
||||
|
||||
```bash
|
||||
ventoy-img mkdir [OPTIONS] <IMAGE> <PATH>
|
||||
```
|
||||
|
||||
**参数:**
|
||||
- `IMAGE`: Ventoy IMG 文件路径
|
||||
- `PATH`: 要创建的目录路径
|
||||
|
||||
**选项:**
|
||||
|
||||
| 选项 | 简写 | 说明 |
|
||||
|------|------|------|
|
||||
| `--parents` | `-p` | 递归创建父目录(类似 `mkdir -p`) |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 创建单级目录
|
||||
ventoy-img mkdir ventoy.img iso
|
||||
|
||||
# 递归创建多级目录
|
||||
ventoy-img mkdir ventoy.img iso/linux/ubuntu -p
|
||||
|
||||
# 创建多个目录
|
||||
ventoy-img mkdir ventoy.img iso -p
|
||||
ventoy-img mkdir ventoy.img iso/linux -p
|
||||
ventoy-img mkdir ventoy.img iso/windows -p
|
||||
```
|
||||
|
||||
### info - 显示信息
|
||||
|
||||
显示镜像的详细信息。
|
||||
|
||||
```bash
|
||||
ventoy-img info <IMAGE>
|
||||
```
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
ventoy-img info ventoy.img
|
||||
```
|
||||
|
||||
**输出示例:**
|
||||
|
||||
```
|
||||
Image: ventoy.img
|
||||
|
||||
Partition Layout:
|
||||
Data partition:
|
||||
Start: sector 2048 (offset 1.0 MB)
|
||||
Size: 16742400 sectors (8.0 GB)
|
||||
EFI partition:
|
||||
Start: sector 16744448 (offset 8.0 GB)
|
||||
Size: 65536 sectors (32 MB)
|
||||
```
|
||||
|
||||
## 使用场景
|
||||
|
||||
### 场景 1: 创建多系统启动盘
|
||||
|
||||
```bash
|
||||
# 1. 创建 32GB 镜像
|
||||
ventoy-img create -s 32G -o multiboot.img
|
||||
|
||||
# 2. 添加各种系统 ISO
|
||||
ventoy-img add multiboot.img ubuntu-22.04.iso
|
||||
ventoy-img add multiboot.img windows11.iso
|
||||
ventoy-img add multiboot.img fedora-39.iso
|
||||
ventoy-img add multiboot.img archlinux.iso
|
||||
|
||||
# 3. 查看文件列表
|
||||
ventoy-img list multiboot.img
|
||||
|
||||
# 4. 写入 U 盘
|
||||
sudo dd if=multiboot.img of=/dev/sdX bs=4M status=progress
|
||||
```
|
||||
|
||||
### 场景 2: 维护现有镜像
|
||||
|
||||
```bash
|
||||
# 查看当前文件(递归)
|
||||
ventoy-img list ventoy.img -r
|
||||
|
||||
# 删除旧版本
|
||||
ventoy-img remove ventoy.img iso/linux/ubuntu-20.04.iso
|
||||
|
||||
# 添加新版本(覆盖)
|
||||
ventoy-img add ventoy.img ubuntu-24.04.iso -d iso/linux/ubuntu-24.04.iso -f
|
||||
|
||||
# 确认更改
|
||||
ventoy-img list ventoy.img -r
|
||||
```
|
||||
|
||||
### 场景 2.5: 组织文件到子目录
|
||||
|
||||
```bash
|
||||
# 创建目录结构
|
||||
ventoy-img mkdir ventoy.img iso/linux -p
|
||||
ventoy-img mkdir ventoy.img iso/windows -p
|
||||
ventoy-img mkdir ventoy.img iso/tools -p
|
||||
|
||||
# 添加文件到对应目录
|
||||
ventoy-img add ventoy.img ubuntu.iso -d iso/linux/ubuntu.iso
|
||||
ventoy-img add ventoy.img fedora.iso -d iso/linux/fedora.iso
|
||||
ventoy-img add ventoy.img win11.iso -d iso/windows/win11.iso
|
||||
ventoy-img add ventoy.img hiren.iso -d iso/tools/hiren.iso
|
||||
|
||||
# 查看目录结构
|
||||
ventoy-img list ventoy.img -r
|
||||
```
|
||||
|
||||
### 场景 3: 自动化脚本
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# create-ventoy.sh - 自动创建 Ventoy 镜像
|
||||
|
||||
ISO_DIR="/path/to/isos"
|
||||
OUTPUT="ventoy-$(date +%Y%m%d).img"
|
||||
SIZE="64G"
|
||||
|
||||
# 创建镜像
|
||||
ventoy-img create -s "$SIZE" -o "$OUTPUT" || exit 1
|
||||
|
||||
# 添加所有 ISO
|
||||
for iso in "$ISO_DIR"/*.iso; do
|
||||
if [ -f "$iso" ]; then
|
||||
echo "Adding: $(basename "$iso")"
|
||||
ventoy-img add "$OUTPUT" "$iso" || echo "Failed: $iso"
|
||||
fi
|
||||
done
|
||||
|
||||
# 显示结果
|
||||
echo ""
|
||||
echo "=== Created: $OUTPUT ==="
|
||||
ventoy-img list "$OUTPUT"
|
||||
```
|
||||
|
||||
### 场景 4: 在没有 root 权限的环境中使用
|
||||
|
||||
```bash
|
||||
# 在用户目录创建镜像
|
||||
ventoy-img create -s 8G -o ~/ventoy.img
|
||||
|
||||
# 添加文件
|
||||
ventoy-img add ~/ventoy.img ~/Downloads/linux.iso
|
||||
|
||||
# 之后可以用 dd 写入 U 盘(需要 root)
|
||||
# 或者复制到有权限的机器上写入
|
||||
```
|
||||
|
||||
## 写入 U 盘
|
||||
|
||||
创建的 IMG 文件可以直接写入 U 盘:
|
||||
|
||||
### Linux
|
||||
|
||||
```bash
|
||||
# 查找 U 盘设备
|
||||
lsblk
|
||||
|
||||
# 写入(替换 sdX 为实际设备)
|
||||
sudo dd if=ventoy.img of=/dev/sdX bs=4M status=progress conv=fsync
|
||||
|
||||
# 或使用 pv 显示进度
|
||||
pv ventoy.img | sudo dd of=/dev/sdX bs=4M conv=fsync
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
||||
```bash
|
||||
# 查找 U 盘
|
||||
diskutil list
|
||||
|
||||
# 卸载
|
||||
diskutil unmountDisk /dev/diskN
|
||||
|
||||
# 写入
|
||||
sudo dd if=ventoy.img of=/dev/rdiskN bs=4m
|
||||
|
||||
# 弹出
|
||||
diskutil eject /dev/diskN
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
使用 [Rufus](https://rufus.ie/) 或 [balenaEtcher](https://www.balena.io/etcher/):
|
||||
1. 选择 ventoy.img 文件
|
||||
2. 选择目标 U 盘
|
||||
3. 点击写入
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 镜像最小可以多大?
|
||||
|
||||
A: 最小 64MB(32MB EFI 分区 + 32MB 数据分区)。但实际使用建议至少 512MB。
|
||||
|
||||
### Q: 支持多大的 ISO 文件?
|
||||
|
||||
A: 理论上支持 exFAT 的最大文件大小(约 16 EB)。实际受限于镜像大小和可用空间。
|
||||
|
||||
### Q: 为什么添加文件失败?
|
||||
|
||||
可能原因:
|
||||
1. 镜像空间不足
|
||||
2. 文件名已存在(使用 `-f` 选项覆盖,或先删除)
|
||||
3. 目标目录不存在(使用 `-p` 选项自动创建)
|
||||
4. 文件名包含非法字符
|
||||
5. 镜像文件损坏
|
||||
|
||||
### Q: 如何覆盖已存在的文件?
|
||||
|
||||
使用 `-f` 或 `--force` 选项:
|
||||
```bash
|
||||
ventoy-img add ventoy.img new-file.iso -d existing-file.iso -f
|
||||
```
|
||||
|
||||
### Q: 如何创建多级目录?
|
||||
|
||||
使用 `-p` 或 `--parents` 选项:
|
||||
```bash
|
||||
# 创建目录
|
||||
ventoy-img mkdir ventoy.img path/to/deep/dir -p
|
||||
|
||||
# 或在添加文件时自动创建
|
||||
ventoy-img add ventoy.img file.iso -d path/to/deep/file.iso -p
|
||||
```
|
||||
|
||||
### Q: 如何删除整个目录?
|
||||
|
||||
使用 `-r` 或 `--recursive` 选项:
|
||||
```bash
|
||||
ventoy-img remove ventoy.img directory-name -r
|
||||
```
|
||||
|
||||
### Q: 如何验证镜像是否正确?
|
||||
|
||||
```bash
|
||||
# 检查分区表
|
||||
fdisk -l ventoy.img
|
||||
|
||||
# 检查 Ventoy 签名
|
||||
xxd -s 0x190 -l 16 ventoy.img
|
||||
# 应显示: 5654 0047 6500 4844 0052 6400 2045 720d
|
||||
|
||||
# 列出文件
|
||||
ventoy-img list ventoy.img
|
||||
```
|
||||
|
||||
### Q: 可以在 Windows 上使用吗?
|
||||
|
||||
A: 可以。编译 Windows 版本:
|
||||
```bash
|
||||
cargo build --release --target x86_64-pc-windows-gnu
|
||||
```
|
||||
|
||||
## 退出码
|
||||
|
||||
| 码 | 含义 |
|
||||
|----|------|
|
||||
| 0 | 成功 |
|
||||
| 1 | 错误(详见错误信息) |
|
||||
|
||||
## 环境变量
|
||||
|
||||
目前不使用任何环境变量。
|
||||
|
||||
## 另请参阅
|
||||
|
||||
- [技术文档](TECHNICAL.md) - 内部实现细节
|
||||
- [库使用说明](LIBRARY.md) - Rust 库 API
|
||||
- [Ventoy 官方文档](https://www.ventoy.net/en/doc_start.html)
|
||||
579
libs/ventoy-img-rs/docs/LIBRARY.md
Normal file
579
libs/ventoy-img-rs/docs/LIBRARY.md
Normal file
@@ -0,0 +1,579 @@
|
||||
# ventoy-img 库使用说明
|
||||
|
||||
## 安装
|
||||
|
||||
### 作为依赖添加
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
ventoy-img = { path = "path/to/ventoy-img-rs" }
|
||||
```
|
||||
|
||||
或发布到 crates.io 后:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
ventoy-img = "0.1"
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
```rust
|
||||
use ventoy_img::{VentoyImage, Result};
|
||||
use std::path::Path;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// 创建 8GB Ventoy 镜像
|
||||
let img = VentoyImage::create(
|
||||
Path::new("ventoy.img"),
|
||||
"8G",
|
||||
"Ventoy"
|
||||
)?;
|
||||
|
||||
// 打开已有镜像
|
||||
let mut img = VentoyImage::open(Path::new("ventoy.img"))?;
|
||||
|
||||
// 列出文件
|
||||
for file in img.list_files()? {
|
||||
println!("{}: {} bytes", file.name, file.size);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## API 参考
|
||||
|
||||
### VentoyImage
|
||||
|
||||
主要的镜像操作结构体。
|
||||
|
||||
#### 创建镜像
|
||||
|
||||
```rust
|
||||
pub fn create(path: &Path, size_str: &str, label: &str) -> Result<Self>
|
||||
```
|
||||
|
||||
创建新的 Ventoy IMG 文件。
|
||||
|
||||
**参数:**
|
||||
- `path`: 输出文件路径
|
||||
- `size_str`: 镜像大小,支持格式:`"8G"`, `"16G"`, `"1024M"`, `"1073741824"`
|
||||
- `label`: 数据分区卷标(最长 11 字符)
|
||||
|
||||
**示例:**
|
||||
```rust
|
||||
// 创建 8GB 镜像
|
||||
let img = VentoyImage::create(Path::new("ventoy.img"), "8G", "Ventoy")?;
|
||||
|
||||
// 创建 512MB 镜像
|
||||
let img = VentoyImage::create(Path::new("small.img"), "512M", "MyUSB")?;
|
||||
```
|
||||
|
||||
#### 打开镜像
|
||||
|
||||
```rust
|
||||
pub fn open(path: &Path) -> Result<Self>
|
||||
```
|
||||
|
||||
打开已有的 Ventoy IMG 文件。会验证 Ventoy 签名。
|
||||
|
||||
**示例:**
|
||||
```rust
|
||||
let img = VentoyImage::open(Path::new("ventoy.img"))?;
|
||||
```
|
||||
|
||||
#### 列出文件
|
||||
|
||||
```rust
|
||||
pub fn list_files(&self) -> Result<Vec<FileInfo>>
|
||||
pub fn list_files_at(&self, path: &str) -> Result<Vec<FileInfo>>
|
||||
pub fn list_files_recursive(&self) -> Result<Vec<FileInfo>>
|
||||
```
|
||||
|
||||
列出数据分区中的文件。
|
||||
|
||||
**返回:**
|
||||
```rust
|
||||
pub struct FileInfo {
|
||||
pub name: String,
|
||||
pub size: u64,
|
||||
pub is_directory: bool,
|
||||
pub path: String, // 完整路径(用于递归列出)
|
||||
}
|
||||
```
|
||||
|
||||
**示例:**
|
||||
```rust
|
||||
let img = VentoyImage::open(Path::new("ventoy.img"))?;
|
||||
|
||||
// 列出根目录
|
||||
for file in img.list_files()? {
|
||||
if file.is_directory {
|
||||
println!("[DIR] {}", file.name);
|
||||
} else {
|
||||
println!("[FILE] {} ({} bytes)", file.name, file.size);
|
||||
}
|
||||
}
|
||||
|
||||
// 列出指定目录
|
||||
for file in img.list_files_at("iso/linux")? {
|
||||
println!("{}", file.name);
|
||||
}
|
||||
|
||||
// 递归列出所有文件
|
||||
for file in img.list_files_recursive()? {
|
||||
println!("{}: {} bytes", file.path, file.size);
|
||||
}
|
||||
```
|
||||
|
||||
#### 添加文件
|
||||
|
||||
```rust
|
||||
pub fn add_file(&mut self, src_path: &Path) -> Result<()>
|
||||
pub fn add_file_overwrite(&mut self, src_path: &Path, overwrite: bool) -> Result<()>
|
||||
pub fn add_file_to_path(&mut self, src_path: &Path, dest_path: &str, create_parents: bool, overwrite: bool) -> Result<()>
|
||||
```
|
||||
|
||||
将文件添加到数据分区。
|
||||
|
||||
**示例:**
|
||||
```rust
|
||||
let mut img = VentoyImage::open(Path::new("ventoy.img"))?;
|
||||
|
||||
// 添加到根目录
|
||||
img.add_file(Path::new("/path/to/ubuntu.iso"))?;
|
||||
|
||||
// 添加并覆盖已存在的文件
|
||||
img.add_file_overwrite(Path::new("/path/to/new-ubuntu.iso"), true)?;
|
||||
|
||||
// 添加到子目录(自动创建父目录)
|
||||
img.add_file_to_path(
|
||||
Path::new("/path/to/ubuntu.iso"),
|
||||
"iso/linux/ubuntu.iso",
|
||||
true, // create_parents
|
||||
false, // overwrite
|
||||
)?;
|
||||
|
||||
// 添加并覆盖子目录中的文件
|
||||
img.add_file_to_path(
|
||||
Path::new("/path/to/new-ubuntu.iso"),
|
||||
"iso/linux/ubuntu.iso",
|
||||
false, // create_parents (目录已存在)
|
||||
true, // overwrite
|
||||
)?;
|
||||
```
|
||||
|
||||
#### 创建目录
|
||||
|
||||
```rust
|
||||
pub fn create_directory(&mut self, path: &str, create_parents: bool) -> Result<()>
|
||||
```
|
||||
|
||||
在数据分区中创建目录。
|
||||
|
||||
**示例:**
|
||||
```rust
|
||||
let mut img = VentoyImage::open(Path::new("ventoy.img"))?;
|
||||
|
||||
// 创建单级目录
|
||||
img.create_directory("iso", false)?;
|
||||
|
||||
// 递归创建多级目录(类似 mkdir -p)
|
||||
img.create_directory("iso/linux/ubuntu", true)?;
|
||||
```
|
||||
|
||||
#### 删除文件
|
||||
|
||||
```rust
|
||||
pub fn remove_file(&mut self, name: &str) -> Result<()>
|
||||
pub fn remove_path(&mut self, path: &str) -> Result<()>
|
||||
pub fn remove_recursive(&mut self, path: &str) -> Result<()>
|
||||
```
|
||||
|
||||
从数据分区删除文件或目录。
|
||||
|
||||
**示例:**
|
||||
```rust
|
||||
let mut img = VentoyImage::open(Path::new("ventoy.img"))?;
|
||||
|
||||
// 删除根目录的文件
|
||||
img.remove_file("ubuntu.iso")?;
|
||||
|
||||
// 删除子目录中的文件或空目录
|
||||
img.remove_path("iso/linux/ubuntu.iso")?;
|
||||
img.remove_path("iso/empty-dir")?;
|
||||
|
||||
// 递归删除目录及其所有内容
|
||||
img.remove_recursive("iso")?;
|
||||
```
|
||||
|
||||
#### 获取分区布局
|
||||
|
||||
```rust
|
||||
pub fn layout(&self) -> &PartitionLayout
|
||||
```
|
||||
|
||||
获取分区布局信息。
|
||||
|
||||
**示例:**
|
||||
```rust
|
||||
let img = VentoyImage::open(Path::new("ventoy.img"))?;
|
||||
let layout = img.layout();
|
||||
println!("Data partition: {} MB", layout.data_size() / 1024 / 1024);
|
||||
println!("EFI partition: {} MB", layout.efi_size_sectors * 512 / 1024 / 1024);
|
||||
```
|
||||
|
||||
### ExfatFs
|
||||
|
||||
底层 exFAT 文件系统操作。
|
||||
|
||||
#### 打开文件系统
|
||||
|
||||
```rust
|
||||
pub fn open(path: &Path, layout: &PartitionLayout) -> Result<Self>
|
||||
```
|
||||
|
||||
**示例:**
|
||||
```rust
|
||||
use ventoy_img::exfat::ExfatFs;
|
||||
|
||||
let layout = PartitionLayout::calculate(8 * 1024 * 1024 * 1024)?;
|
||||
let mut fs = ExfatFs::open(Path::new("ventoy.img"), &layout)?;
|
||||
```
|
||||
|
||||
#### 写入文件
|
||||
|
||||
```rust
|
||||
// 基本写入(根目录)
|
||||
pub fn write_file(&mut self, name: &str, data: &[u8]) -> Result<()>
|
||||
|
||||
// 带覆盖选项
|
||||
pub fn write_file_overwrite(&mut self, name: &str, data: &[u8], overwrite: bool) -> Result<()>
|
||||
|
||||
// 写入到指定路径
|
||||
pub fn write_file_path(&mut self, path: &str, data: &[u8], create_parents: bool, overwrite: bool) -> Result<()>
|
||||
```
|
||||
|
||||
**示例:**
|
||||
```rust
|
||||
// 写入到根目录
|
||||
fs.write_file("config.txt", b"content")?;
|
||||
|
||||
// 覆盖已存在的文件
|
||||
fs.write_file_overwrite("config.txt", b"new content", true)?;
|
||||
|
||||
// 写入到子目录(自动创建父目录)
|
||||
fs.write_file_path("iso/linux/config.txt", b"content", true, false)?;
|
||||
```
|
||||
|
||||
#### 读取文件
|
||||
|
||||
```rust
|
||||
pub fn read_file(&mut self, name: &str) -> Result<Vec<u8>>
|
||||
pub fn read_file_path(&mut self, path: &str) -> Result<Vec<u8>>
|
||||
```
|
||||
|
||||
读取文件内容到内存。
|
||||
|
||||
**示例:**
|
||||
```rust
|
||||
// 从根目录读取
|
||||
let data = fs.read_file("config.txt")?;
|
||||
|
||||
// 从子目录读取
|
||||
let data = fs.read_file_path("iso/linux/config.txt")?;
|
||||
println!("{}", String::from_utf8_lossy(&data));
|
||||
```
|
||||
|
||||
#### 流式读取(大文件)
|
||||
|
||||
```rust
|
||||
pub fn read_file_to_writer<W: Write>(&mut self, name: &str, writer: &mut W) -> Result<u64>
|
||||
pub fn read_file_path_to_writer<W: Write>(&mut self, path: &str, writer: &mut W) -> Result<u64>
|
||||
```
|
||||
|
||||
流式读取文件到 Writer,适合大文件。返回读取的字节数。
|
||||
|
||||
**示例:**
|
||||
```rust
|
||||
use std::fs::File;
|
||||
use std::io::BufWriter;
|
||||
|
||||
// 从镜像中提取文件
|
||||
let mut output = BufWriter::new(File::create("extracted.iso")?);
|
||||
let bytes = fs.read_file_to_writer("ubuntu.iso", &mut output)?;
|
||||
println!("Extracted {} bytes", bytes);
|
||||
|
||||
// 从子目录流式读取
|
||||
let mut output = Vec::new();
|
||||
fs.read_file_path_to_writer("iso/linux/ubuntu.iso", &mut output)?;
|
||||
```
|
||||
|
||||
#### 删除文件
|
||||
|
||||
```rust
|
||||
pub fn delete_file(&mut self, name: &str) -> Result<()>
|
||||
pub fn delete_path(&mut self, path: &str) -> Result<()>
|
||||
pub fn delete_recursive(&mut self, path: &str) -> Result<()>
|
||||
```
|
||||
|
||||
删除文件或目录并释放空间。
|
||||
|
||||
**示例:**
|
||||
```rust
|
||||
// 删除根目录的文件
|
||||
fs.delete_file("config.txt")?;
|
||||
|
||||
// 删除子目录中的文件或空目录
|
||||
fs.delete_path("iso/linux/config.txt")?;
|
||||
|
||||
// 递归删除目录
|
||||
fs.delete_recursive("iso")?;
|
||||
```
|
||||
|
||||
#### 创建目录
|
||||
|
||||
```rust
|
||||
pub fn create_directory(&mut self, path: &str, create_parents: bool) -> Result<()>
|
||||
```
|
||||
|
||||
**示例:**
|
||||
```rust
|
||||
// 创建单级目录
|
||||
fs.create_directory("iso", false)?;
|
||||
|
||||
// 递归创建多级目录
|
||||
fs.create_directory("iso/linux/ubuntu", true)?;
|
||||
```
|
||||
|
||||
#### 列出文件
|
||||
|
||||
```rust
|
||||
pub fn list_files(&mut self) -> Result<Vec<FileInfo>>
|
||||
pub fn list_files_at(&mut self, path: &str) -> Result<Vec<FileInfo>>
|
||||
pub fn list_files_recursive(&mut self) -> Result<Vec<FileInfo>>
|
||||
```
|
||||
|
||||
**示例:**
|
||||
```rust
|
||||
// 列出根目录
|
||||
let files = fs.list_files()?;
|
||||
|
||||
// 列出指定目录
|
||||
let files = fs.list_files_at("iso/linux")?;
|
||||
|
||||
// 递归列出所有文件
|
||||
let all_files = fs.list_files_recursive()?;
|
||||
```
|
||||
|
||||
#### 流式写入(大文件)
|
||||
|
||||
```rust
|
||||
pub fn write_file_from_reader<R: Read>(&mut self, name: &str, reader: &mut R, size: u64) -> Result<()>
|
||||
pub fn write_file_from_reader_overwrite<R: Read>(&mut self, name: &str, reader: &mut R, size: u64, overwrite: bool) -> Result<()>
|
||||
pub fn write_file_from_reader_path<R: Read>(&mut self, path: &str, reader: &mut R, size: u64, create_parents: bool, overwrite: bool) -> Result<()>
|
||||
```
|
||||
|
||||
从 Reader 流式写入文件,适合大文件。
|
||||
|
||||
**示例:**
|
||||
```rust
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
|
||||
let file = File::open("large.iso")?;
|
||||
let size = file.metadata()?.len();
|
||||
let mut reader = BufReader::new(file);
|
||||
|
||||
// 写入到根目录
|
||||
fs.write_file_from_reader("large.iso", &mut reader, size)?;
|
||||
|
||||
// 写入到子目录并覆盖
|
||||
fs.write_file_from_reader_path(
|
||||
"iso/linux/large.iso",
|
||||
&mut reader,
|
||||
size,
|
||||
true, // create_parents
|
||||
true, // overwrite
|
||||
)?;
|
||||
```
|
||||
|
||||
### ExfatFileWriter
|
||||
|
||||
手动控制的流式写入器。
|
||||
|
||||
```rust
|
||||
use ventoy_img::exfat::ExfatFileWriter;
|
||||
|
||||
// 创建写入器(根目录)
|
||||
let mut writer = ExfatFileWriter::create(&mut fs, "file.iso", total_size)?;
|
||||
|
||||
// 创建写入器(带覆盖选项)
|
||||
let mut writer = ExfatFileWriter::create_overwrite(&mut fs, "file.iso", total_size, true)?;
|
||||
|
||||
// 创建写入器(指定路径,支持创建父目录和覆盖)
|
||||
let mut writer = ExfatFileWriter::create_at_path(
|
||||
&mut fs,
|
||||
"iso/linux/file.iso",
|
||||
total_size,
|
||||
true, // create_parents
|
||||
false, // overwrite
|
||||
)?;
|
||||
|
||||
// 分块写入
|
||||
loop {
|
||||
let n = source.read(&mut buffer)?;
|
||||
if n == 0 { break; }
|
||||
writer.write(&buffer[..n])?;
|
||||
}
|
||||
|
||||
// 完成写入(创建目录条目)
|
||||
writer.finish()?;
|
||||
```
|
||||
|
||||
### ExfatFileReader
|
||||
|
||||
手动控制的流式读取器,实现 `std::io::Read` 和 `std::io::Seek` 特征。
|
||||
|
||||
```rust
|
||||
use ventoy_img::exfat::ExfatFileReader;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
|
||||
// 打开文件读取器(根目录)
|
||||
let mut reader = ExfatFileReader::open(&mut fs, "file.iso")?;
|
||||
|
||||
// 打开文件读取器(指定路径)
|
||||
let mut reader = ExfatFileReader::open_path(&mut fs, "iso/linux/file.iso")?;
|
||||
|
||||
// 获取文件信息
|
||||
println!("File size: {} bytes", reader.file_size());
|
||||
println!("Current position: {}", reader.position());
|
||||
println!("Remaining: {} bytes", reader.remaining());
|
||||
|
||||
// 读取数据
|
||||
let mut buffer = [0u8; 4096];
|
||||
let n = reader.read(&mut buffer)?;
|
||||
|
||||
// 读取全部内容
|
||||
let mut data = Vec::new();
|
||||
reader.read_to_end(&mut data)?;
|
||||
|
||||
// Seek 操作
|
||||
reader.seek(SeekFrom::Start(1000))?; // 从开头偏移
|
||||
reader.seek(SeekFrom::Current(100))?; // 从当前位置偏移
|
||||
reader.seek(SeekFrom::End(-100))?; // 从结尾偏移
|
||||
|
||||
// 精确读取
|
||||
let mut exact_buffer = [0u8; 100];
|
||||
reader.read_exact(&mut exact_buffer)?;
|
||||
```
|
||||
|
||||
**特性:**
|
||||
- 实现 `std::io::Read` 和 `std::io::Seek`
|
||||
- 自动 cluster 缓存,减少 I/O 次数
|
||||
- 支持任意位置的 seek
|
||||
- 内存占用低,只缓存当前 cluster
|
||||
|
||||
## 错误处理
|
||||
|
||||
所有操作返回 `Result<T, VentoyError>`:
|
||||
|
||||
```rust
|
||||
use ventoy_img::{Result, VentoyError};
|
||||
|
||||
fn example() -> Result<()> {
|
||||
let img = VentoyImage::open(Path::new("ventoy.img"))
|
||||
.map_err(|e| {
|
||||
match &e {
|
||||
VentoyError::Io(io_err) => eprintln!("IO error: {}", io_err),
|
||||
VentoyError::ImageError(msg) => eprintln!("Image error: {}", msg),
|
||||
VentoyError::FilesystemError(msg) => eprintln!("FS error: {}", msg),
|
||||
_ => eprintln!("Error: {}", e),
|
||||
}
|
||||
e
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## 完整示例
|
||||
|
||||
### 创建镜像并添加 ISO
|
||||
|
||||
```rust
|
||||
use ventoy_img::{VentoyImage, Result};
|
||||
use std::path::Path;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// 创建 16GB 镜像
|
||||
println!("Creating Ventoy image...");
|
||||
let mut img = VentoyImage::create(
|
||||
Path::new("ventoy.img"),
|
||||
"16G",
|
||||
"Ventoy"
|
||||
)?;
|
||||
|
||||
// 添加 ISO 文件
|
||||
println!("Adding ISO files...");
|
||||
img.add_file(Path::new("/isos/ubuntu-22.04.iso"))?;
|
||||
img.add_file(Path::new("/isos/windows11.iso"))?;
|
||||
|
||||
// 列出文件
|
||||
println!("\nFiles in image:");
|
||||
for file in img.list_files()? {
|
||||
println!(" {} ({:.1} MB)", file.name, file.size as f64 / 1024.0 / 1024.0);
|
||||
}
|
||||
|
||||
println!("\nDone! Write ventoy.img to USB drive.");
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### 批量处理 ISO 文件
|
||||
|
||||
```rust
|
||||
use ventoy_img::{VentoyImage, Result};
|
||||
use std::path::Path;
|
||||
use std::fs;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let iso_dir = Path::new("/path/to/isos");
|
||||
|
||||
// 计算所需大小
|
||||
let total_size: u64 = fs::read_dir(iso_dir)?
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|e| e.path().extension().map_or(false, |ext| ext == "iso"))
|
||||
.map(|e| e.metadata().map(|m| m.len()).unwrap_or(0))
|
||||
.sum();
|
||||
|
||||
// 添加 1GB 余量 + 32MB EFI
|
||||
let image_size = total_size + 1024 * 1024 * 1024 + 32 * 1024 * 1024;
|
||||
let size_str = format!("{}M", image_size / 1024 / 1024);
|
||||
|
||||
// 创建镜像
|
||||
let mut img = VentoyImage::create(Path::new("ventoy.img"), &size_str, "Ventoy")?;
|
||||
|
||||
// 添加所有 ISO
|
||||
for entry in fs::read_dir(iso_dir)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
if path.extension().map_or(false, |ext| ext == "iso") {
|
||||
println!("Adding: ", path.display());
|
||||
img.add_file(&path)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **文件大小限制**: 单个文件最大支持 exFAT 限制(约 16 EB)
|
||||
2. **文件名**: 最长 255 个 UTF-16 字符,大小写不敏感
|
||||
3. **Unicode 支持**: 完整支持国际字符(中日韩、西里尔、希腊字母、Emoji 等)
|
||||
4. **并发**: `ExfatFs` 不是线程安全的,需要外部同步
|
||||
5. **磁盘空间**: 创建镜像时会预分配全部空间(稀疏文件)
|
||||
6. **最小大小**: 镜像最小 64MB(32MB EFI + 32MB 数据)
|
||||
7. **动态簇大小**: 根据卷大小自动选择(<256MB: 4KB, 256MB-8GB: 32KB, >8GB: 128KB)
|
||||
510
libs/ventoy-img-rs/docs/TECHNICAL.md
Normal file
510
libs/ventoy-img-rs/docs/TECHNICAL.md
Normal file
@@ -0,0 +1,510 @@
|
||||
# ventoy-img 技术文档
|
||||
|
||||
## 概述
|
||||
|
||||
ventoy-img 是一个纯 Rust 实现的 Ventoy 可启动镜像生成工具,无需 root 权限或 loop 设备即可创建完整可用的 Ventoy IMG 文件。
|
||||
|
||||
## 架构设计
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ CLI (main.rs) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ VentoyImage (image.rs) │
|
||||
├──────────────┬──────────────┬──────────────┬───────────────┤
|
||||
│ Partition │ exFAT │ Resources │ Error │
|
||||
│ (partition.rs)│ (exfat/) │(resources.rs)│ (error.rs) │
|
||||
└──────────────┴──────────────┴──────────────┴───────────────┘
|
||||
```
|
||||
|
||||
## 核心模块
|
||||
|
||||
### 1. 分区模块 (partition.rs)
|
||||
|
||||
负责 MBR 分区表的创建和管理。
|
||||
|
||||
#### 分区布局
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ Sector 0: MBR (Boot Code + Partition Table + Signature) │
|
||||
├────────────────────────────────────────────────────────────┤
|
||||
│ Sector 1-2047: GRUB core.img (引导代码) │
|
||||
├────────────────────────────────────────────────────────────┤
|
||||
│ Sector 2048 - N: 数据分区 (exFAT, 存放 ISO 文件) │
|
||||
├────────────────────────────────────────────────────────────┤
|
||||
│ Sector N+1 - End: EFI 分区 (FAT16, 32MB, UEFI 启动) │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### MBR 结构 (512 字节)
|
||||
|
||||
| 偏移 | 大小 | 内容 |
|
||||
|------|------|------|
|
||||
| 0x000 | 440 | Boot Code (来自 boot.img) |
|
||||
| 0x1B8 | 4 | Disk Signature |
|
||||
| 0x1BC | 2 | Reserved |
|
||||
| 0x1BE | 16 | Partition Entry 1 (数据分区) |
|
||||
| 0x1CE | 16 | Partition Entry 2 (EFI 分区) |
|
||||
| 0x1DE | 16 | Partition Entry 3 (未使用) |
|
||||
| 0x1EE | 16 | Partition Entry 4 (未使用) |
|
||||
| 0x1FE | 2 | Boot Signature (0x55AA) |
|
||||
|
||||
#### Ventoy 签名
|
||||
|
||||
位于 MBR 偏移 0x190,16 字节:
|
||||
```
|
||||
56 54 00 47 65 00 48 44 00 52 64 00 20 45 72 0D
|
||||
```
|
||||
|
||||
### 2. exFAT 模块 (exfat/)
|
||||
|
||||
完整的 exFAT 文件系统实现,支持读写操作。
|
||||
|
||||
#### 2.1 格式化 (format.rs)
|
||||
|
||||
创建 exFAT 文件系统结构:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Boot Region (Sector 0-11) │
|
||||
│ - Boot Sector (512 bytes) │
|
||||
│ - Extended Boot Sectors (8 sectors) │
|
||||
│ - OEM Parameters (2 sectors) │
|
||||
│ - Boot Checksum Sector │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Backup Boot Region (Sector 12-23) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ FAT Region (Sector 24+) │
|
||||
│ - FAT Table (每个 cluster 4 字节) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Cluster Heap │
|
||||
│ - Cluster 2: Allocation Bitmap │
|
||||
│ - Cluster 3..N: Upcase Table (128KB,可能跨多个簇) │
|
||||
│ - Cluster N+1: Root Directory │
|
||||
│ - Cluster N+2+: 用户数据 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
##### 动态簇大小
|
||||
|
||||
根据卷大小自动选择最优簇大小:
|
||||
|
||||
| 卷大小 | 簇大小 | 说明 |
|
||||
|--------|--------|------|
|
||||
| < 256MB | 4KB | 适合小文件,减少浪费 |
|
||||
| 256MB - 8GB | 32KB | 平衡性能和空间 |
|
||||
| > 8GB | 128KB | 优化大文件 (ISO) 性能 |
|
||||
|
||||
```rust
|
||||
fn get_cluster_size(total_sectors: u64) -> u32 {
|
||||
let volume_size = total_sectors * 512;
|
||||
match volume_size {
|
||||
n if n < 256 * 1024 * 1024 => 4096, // < 256MB
|
||||
n if n < 8 * 1024 * 1024 * 1024 => 32768, // 256MB - 8GB
|
||||
_ => 128 * 1024, // > 8GB
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Boot Sector 关键字段
|
||||
|
||||
| 偏移 | 大小 | 字段 | 说明 |
|
||||
|------|------|------|------|
|
||||
| 0 | 3 | JumpBoot | 跳转指令 (0xEB 0x76 0x90) |
|
||||
| 3 | 8 | FileSystemName | "EXFAT " |
|
||||
| 64 | 8 | PartitionOffset | 分区偏移 |
|
||||
| 72 | 8 | VolumeLength | 卷大小(扇区数) |
|
||||
| 80 | 4 | FatOffset | FAT 起始扇区 |
|
||||
| 84 | 4 | FatLength | FAT 长度(扇区数) |
|
||||
| 88 | 4 | ClusterHeapOffset | Cluster Heap 起始扇区 |
|
||||
| 92 | 4 | ClusterCount | Cluster 总数 |
|
||||
| 96 | 4 | FirstClusterOfRootDirectory | 根目录起始 Cluster |
|
||||
| 100 | 4 | VolumeSerialNumber | 卷序列号 |
|
||||
| 108 | 1 | BytesPerSectorShift | 扇区大小位移 (9 = 512) |
|
||||
| 109 | 1 | SectorsPerClusterShift | Cluster 大小位移 |
|
||||
| 510 | 2 | BootSignature | 0xAA55 |
|
||||
|
||||
#### 2.2 文件操作 (ops.rs)
|
||||
|
||||
##### Cluster 编号规则
|
||||
|
||||
- Cluster 0, 1: 保留
|
||||
- Cluster 2: Allocation Bitmap
|
||||
- Cluster 3: Upcase Table
|
||||
- Cluster 4: Root Directory
|
||||
- Cluster 5+: 用户数据
|
||||
|
||||
##### FAT 表条目值
|
||||
|
||||
| 值 | 含义 |
|
||||
|----|------|
|
||||
| 0x00000000 | 空闲 |
|
||||
| 0x00000002 - 0xFFFFFFF6 | 下一个 Cluster |
|
||||
| 0xFFFFFFF7 | 坏 Cluster |
|
||||
| 0xFFFFFFF8 - 0xFFFFFFFF | 链结束 |
|
||||
|
||||
##### 目录条目类型
|
||||
|
||||
| 类型 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| Volume Label | 0x83 | 卷标 |
|
||||
| Allocation Bitmap | 0x81 | 位图描述 |
|
||||
| Upcase Table | 0x82 | 大写表描述 |
|
||||
| File | 0x85 | 文件/目录 |
|
||||
| Stream Extension | 0xC0 | 流扩展 |
|
||||
| File Name | 0xC1 | 文件名 |
|
||||
|
||||
##### 文件条目集结构
|
||||
|
||||
创建一个文件需要 3+ 个目录条目:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ File Directory Entry (0x85) - 32 bytes │
|
||||
│ - EntryType: 0x85 │
|
||||
│ - SecondaryCount: 后续条目数 │
|
||||
│ - SetChecksum: 校验和 │
|
||||
│ - FileAttributes: 属性 │
|
||||
│ - Timestamps: 创建/修改/访问时间 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Stream Extension Entry (0xC0) - 32 bytes │
|
||||
│ - EntryType: 0xC0 │
|
||||
│ - GeneralSecondaryFlags: 标志 │
|
||||
│ - NameLength: 文件名长度 (UTF-16 字符数) │
|
||||
│ - NameHash: 文件名哈希 │
|
||||
│ - FirstCluster: 数据起始 Cluster │
|
||||
│ - DataLength: 文件大小 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ File Name Entry (0xC1) - 32 bytes × N │
|
||||
│ - EntryType: 0xC1 │
|
||||
│ - FileName: 15 个 UTF-16 字符 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
##### 校验和算法
|
||||
|
||||
```rust
|
||||
// Entry Set Checksum
|
||||
fn calculate_entry_set_checksum(entries: &[[u8; 32]]) -> u16 {
|
||||
let mut checksum: u16 = 0;
|
||||
for (entry_idx, entry) in entries.iter().enumerate() {
|
||||
for (byte_idx, &byte) in entry.iter().enumerate() {
|
||||
// 跳过第一个条目的校验和字段 (bytes 2-3)
|
||||
if entry_idx == 0 && (byte_idx == 2 || byte_idx == 3) {
|
||||
continue;
|
||||
}
|
||||
checksum = checksum.rotate_right(1).wrapping_add(byte as u16);
|
||||
}
|
||||
}
|
||||
checksum
|
||||
}
|
||||
|
||||
// Name Hash (使用 Unicode 大写转换)
|
||||
fn calculate_name_hash(name: &str) -> u16 {
|
||||
let mut hash: u16 = 0;
|
||||
for ch in name.encode_utf16() {
|
||||
let upper = unicode::to_uppercase_simple(ch); // 使用 Unicode 模块
|
||||
let bytes = upper.to_le_bytes();
|
||||
hash = hash.rotate_right(1).wrapping_add(bytes[0] as u16);
|
||||
hash = hash.rotate_right(1).wrapping_add(bytes[1] as u16);
|
||||
}
|
||||
hash
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3 Unicode 模块 (unicode.rs)
|
||||
|
||||
提供 exFAT 文件名的 Unicode 支持:
|
||||
|
||||
##### UTF-16 大写转换
|
||||
|
||||
支持以下字符范围的大小写转换:
|
||||
- ASCII (a-z)
|
||||
- Latin-1 Supplement (à-ÿ)
|
||||
- Latin Extended-A (ā-ž)
|
||||
- Greek (α-ω)
|
||||
- Cyrillic (а-я, ѐ-џ)
|
||||
|
||||
```rust
|
||||
pub fn to_uppercase_simple(ch: u16) -> u16 {
|
||||
match ch {
|
||||
0x0061..=0x007A => ch - 32, // ASCII a-z
|
||||
0x00E0..=0x00F6 | 0x00F8..=0x00FE => ch - 32, // Latin-1
|
||||
0x03B1..=0x03C1 => ch - 32, // Greek α-ρ
|
||||
0x03C3..=0x03C9 => ch - 32, // Greek σ-ω
|
||||
0x03C2 => 0x03A3, // ς -> Σ
|
||||
0x0430..=0x044F => ch - 32, // Cyrillic а-я
|
||||
0x0450..=0x045F => ch - 80, // Cyrillic ѐ-џ
|
||||
// ... 更多 Latin Extended-A 映射
|
||||
_ => ch,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### Upcase Table
|
||||
|
||||
生成 128KB 的 Upcase 表,映射每个 UTF-16 代码单元到其大写形式:
|
||||
|
||||
```rust
|
||||
pub fn generate_upcase_table() -> Vec<u8> {
|
||||
let mut table = Vec::with_capacity(65536 * 2);
|
||||
for i in 0u32..65536 {
|
||||
let upper = to_uppercase_simple(i as u16);
|
||||
table.extend_from_slice(&upper.to_le_bytes());
|
||||
}
|
||||
table // 128KB
|
||||
}
|
||||
```
|
||||
|
||||
##### UTF-16 编解码
|
||||
|
||||
支持 BMP 和补充平面字符(如 Emoji):
|
||||
|
||||
```rust
|
||||
// 编码
|
||||
pub fn encode_utf16le(s: &str) -> Vec<u8>
|
||||
|
||||
// 解码(处理代理对)
|
||||
pub fn decode_utf16le(bytes: &[u8]) -> String
|
||||
```
|
||||
|
||||
### 3. 资源模块 (resources.rs)
|
||||
|
||||
内嵌 Ventoy 启动所需的二进制资源:
|
||||
|
||||
| 资源 | 大小 | 用途 |
|
||||
|------|------|------|
|
||||
| boot.img | 512 bytes | MBR 引导代码 |
|
||||
| core.img.xz | ~448 KB | GRUB 核心镜像 (XZ 压缩) |
|
||||
| ventoy.disk.img.xz | ~13 MB | EFI 分区镜像 (XZ 压缩) |
|
||||
|
||||
资源使用 `include_bytes!` 宏在编译时嵌入,运行时使用 `lzma-rs` 解压。
|
||||
|
||||
### 4. 错误处理 (error.rs)
|
||||
|
||||
使用 `thiserror` 定义错误类型:
|
||||
|
||||
```rust
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum VentoyError {
|
||||
#[error("IO error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error("Invalid size format: {0}")]
|
||||
InvalidSize(String),
|
||||
|
||||
#[error("Image error: {0}")]
|
||||
ImageError(String),
|
||||
|
||||
#[error("Filesystem error: {0}")]
|
||||
FilesystemError(String),
|
||||
|
||||
#[error("File not found: {0}")]
|
||||
FileNotFound(String),
|
||||
|
||||
#[error("Decompression error: {0}")]
|
||||
DecompressionError(String),
|
||||
}
|
||||
```
|
||||
|
||||
## 关键实现细节
|
||||
|
||||
### Cluster 大小选择
|
||||
|
||||
根据卷大小动态选择 cluster 大小:
|
||||
- < 256MB: 4KB clusters (适合小文件)
|
||||
- 256MB - 8GB: 32KB clusters (平衡)
|
||||
- > 8GB: 128KB clusters (优化大文件性能)
|
||||
|
||||
Upcase Table 固定为 128KB (65536 × 2 bytes),可能跨多个 cluster。
|
||||
|
||||
### 流式读写
|
||||
|
||||
#### ExfatFileWriter
|
||||
|
||||
支持流式写入大文件:
|
||||
|
||||
```rust
|
||||
pub struct ExfatFileWriter<'a> {
|
||||
fs: &'a mut ExfatFs,
|
||||
name: String,
|
||||
total_size: u64, // 必须预先知道
|
||||
allocated_clusters: Vec<u32>,
|
||||
current_cluster_index: usize,
|
||||
cluster_buffer: Vec<u8>, // 缓冲区(簇大小)
|
||||
bytes_written: u64,
|
||||
}
|
||||
```
|
||||
|
||||
写入流程:
|
||||
1. 预先分配所有需要的 clusters
|
||||
2. 数据写入 cluster_buffer
|
||||
3. 缓冲区满时写入当前 cluster
|
||||
4. finish() 时创建目录条目
|
||||
|
||||
#### ExfatFileReader
|
||||
|
||||
支持流式读取大文件,实现 `std::io::Read` 和 `std::io::Seek`:
|
||||
|
||||
```rust
|
||||
pub struct ExfatFileReader<'a> {
|
||||
fs: &'a mut ExfatFs,
|
||||
cluster_chain: Vec<u32>, // 文件的 cluster 链
|
||||
file_size: u64, // 文件总大小
|
||||
position: u64, // 当前读取位置
|
||||
cluster_cache: Option<(u32, Vec<u8>)>, // 当前 cluster 缓存
|
||||
}
|
||||
```
|
||||
|
||||
读取流程:
|
||||
1. 根据 position 计算当前 cluster 索引和偏移
|
||||
2. 如果 cluster 不在缓存中,读取并缓存
|
||||
3. 从缓存中复制数据到用户缓冲区
|
||||
4. 更新 position
|
||||
|
||||
Seek 支持:
|
||||
- `SeekFrom::Start(n)` - 从文件开头偏移
|
||||
- `SeekFrom::Current(n)` - 从当前位置偏移
|
||||
- `SeekFrom::End(n)` - 从文件结尾偏移
|
||||
|
||||
### 文件名大小写
|
||||
|
||||
exFAT 文件名大小写不敏感但保留大小写:
|
||||
- 查找时转换为小写比较
|
||||
- 存储时保留原始大小写
|
||||
- Name Hash 使用大写计算
|
||||
|
||||
## 性能考虑
|
||||
|
||||
1. **稀疏文件**: 使用 `file.set_len()` 创建稀疏文件,避免写入全零
|
||||
2. **批量写入**: cluster 级别批量写入,减少 I/O 次数
|
||||
3. **内存映射**: 未使用 mmap,保持跨平台兼容性
|
||||
4. **缓冲**: 流式写入使用 128KB 缓冲区
|
||||
|
||||
## 限制
|
||||
|
||||
1. ~~仅支持根目录文件操作~~ ✅ 已支持子目录
|
||||
2. ~~不支持文件覆盖~~ ✅ 已支持文件覆盖
|
||||
3. ~~目录条目限制在单个 cluster 内~~ ✅ 已支持目录扩展(多 cluster)
|
||||
4. ~~仅支持 ASCII 文件名~~ ✅ 已支持完整 Unicode(中日韩、西里尔、希腊、Emoji)
|
||||
5. 不支持扩展属性和 ACL
|
||||
|
||||
## 新增功能
|
||||
|
||||
### 子目录支持
|
||||
|
||||
支持完整的目录操作:
|
||||
- 路径解析:支持 `path/to/file` 格式
|
||||
- 创建目录:支持递归创建父目录(mkdir -p)
|
||||
- 目录遍历:支持遍历多 cluster 的目录
|
||||
- 递归列出:支持列出所有子目录中的文件
|
||||
- 递归删除:支持删除目录及其所有内容
|
||||
|
||||
```rust
|
||||
// 路径解析
|
||||
fn parse_path(path: &str) -> Vec<&str> {
|
||||
path.trim_matches('/')
|
||||
.split('/')
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect()
|
||||
}
|
||||
|
||||
// 解析路径到目标目录
|
||||
fn resolve_path(&mut self, path: &str, create_parents: bool) -> Result<ResolvedPath>
|
||||
```
|
||||
|
||||
### 文件覆盖支持
|
||||
|
||||
所有写入方法都支持覆盖选项:
|
||||
- `write_file_overwrite()` - 覆盖根目录文件
|
||||
- `write_file_path()` - 支持 `overwrite` 参数
|
||||
- `ExfatFileWriter::create_overwrite()` - 流式写入覆盖
|
||||
- `ExfatFileWriter::create_at_path()` - 指定路径 + 覆盖
|
||||
|
||||
覆盖逻辑:
|
||||
1. 检查目标文件是否存在
|
||||
2. 如果存在且 `overwrite=true`,先删除旧文件
|
||||
3. 创建新文件
|
||||
|
||||
### 目录扩展支持
|
||||
|
||||
当目录中的文件数量超过单个 cluster 容量时,自动扩展目录:
|
||||
|
||||
```rust
|
||||
fn find_free_slot_in_directory(&mut self, dir_cluster: u32, entries_needed: usize) -> Result<(u32, u32)> {
|
||||
// 1. 遍历目录链中的所有 cluster
|
||||
// 2. 查找连续的空闲条目
|
||||
// 3. 如果空间不足,调用 extend_cluster_chain() 分配新 cluster
|
||||
// 4. 清除旧 cluster 中的 END 标记
|
||||
// 5. 返回新 cluster 和偏移
|
||||
}
|
||||
|
||||
fn extend_cluster_chain(&mut self, first_cluster: u32) -> Result<u32> {
|
||||
// 1. 读取 cluster 链,找到最后一个 cluster
|
||||
// 2. 分配一个新 cluster
|
||||
// 3. 更新 FAT 表链接
|
||||
// 4. 初始化新 cluster 为零
|
||||
// 5. 返回新 cluster 编号
|
||||
}
|
||||
```
|
||||
|
||||
### 流式读取支持
|
||||
|
||||
`ExfatFileReader` 支持流式读取大文件:
|
||||
|
||||
```rust
|
||||
use ventoy_img::exfat::ExfatFileReader;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
|
||||
// 打开文件
|
||||
let mut reader = ExfatFileReader::open(&mut fs, "large.iso")?;
|
||||
|
||||
// 获取文件信息
|
||||
println!("Size: {}, Position: {}", reader.file_size(), reader.position());
|
||||
|
||||
// 读取数据
|
||||
let mut buf = vec![0u8; 4096];
|
||||
let n = reader.read(&mut buf)?;
|
||||
|
||||
// Seek 操作
|
||||
reader.seek(SeekFrom::Start(1024))?;
|
||||
reader.seek(SeekFrom::Current(100))?;
|
||||
reader.seek(SeekFrom::End(-100))?;
|
||||
```
|
||||
|
||||
特性:
|
||||
- 实现 `std::io::Read` 和 `std::io::Seek` 特征
|
||||
- Cluster 级别缓存,减少 I/O
|
||||
- 支持任意位置 seek
|
||||
- 内存占用低(只缓存当前 cluster)
|
||||
|
||||
### Unicode 支持
|
||||
|
||||
完整的 Unicode 文件名支持:
|
||||
|
||||
支持的字符范围:
|
||||
- ASCII (a-z, A-Z)
|
||||
- Latin-1 Supplement (à-ÿ, À-Þ)
|
||||
- Latin Extended-A (ā-ž)
|
||||
- Greek (α-ω, Α-Ω)
|
||||
- Cyrillic (а-я, А-Я, ѐ-џ, Ѐ-Џ)
|
||||
- CJK 字符(中日韩)
|
||||
- Emoji(通过 UTF-16 代理对)
|
||||
|
||||
```rust
|
||||
// 支持 Unicode 文件名
|
||||
fs.write_file("中文文件.txt", b"content")?;
|
||||
fs.write_file("Файл.txt", b"content")?; // 俄语
|
||||
fs.write_file("αβγ.txt", b"content")?; // 希腊语
|
||||
fs.write_file("😀🎉.txt", b"content")?; // Emoji
|
||||
|
||||
// 大小写不敏感查找
|
||||
let data = fs.read_file("ФАЙЛ.TXT")?; // 找到 Файл.txt
|
||||
```
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [exFAT File System Specification](https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification)
|
||||
- [Ventoy Official Documentation](https://www.ventoy.net/en/doc_start.html)
|
||||
- [GRUB Manual](https://www.gnu.org/software/grub/manual/grub/)
|
||||
Reference in New Issue
Block a user