pmbs 0.1.1

(每分钟) 创建 btrfs 快照, 并自动清理. (Make btrfs snapshot (every minute), and auto clean)
Documentation
# 胖喵必快 (pmbs) 主要设计文档

```ignore
胖喵必快 (pmbs)

正式名称: 紫腹巨蚊 (Toxorhynchites gravelyi) 系列
    澳大利亚海神草 (Posidonia australis) 软件
```

目录:

- 1 问题背景

- 2 源代码目录结构

- 3 命令行环境变量

- 4 快照目录结构

- 5 systemd timer

- 6 自动清理

## 1 问题背景

本软件的主要设计用途是 **防误删**. 也就是不小心删除自己的重要文件, 造成数据丢失.

典型的 **定期备份** (backup) 是不够的, 因为间隔时间通常较长,
比如每周或每天备份一次. **实时同步** (sync) 文件 (比如给一个目录设置镜像)
也是不够的, 因为原文件被删除之后, 镜像也会被同步删除.

**快照** (snapshot) 是一个很好的解决方案, 特别是 `btrfs` 快照. 因为 btrfs 的 CoW
(写时复制) 特性, 快照是轻量级, 快速的. 只有快照后变动的文件,
才会占用额外的存储空间. 通过使用 `root` 创建 **只读** 快照,
普通用户无权限删除快照 (只能读取), 从而可以在很大程度上防止误删除.
如果发生了误删, 普通用户可以直接读取之前的快照, 恢复文件 (复制回来),
恢复过程无需 root, 进一步降低了风险 (避免误操作破坏快照, 导致数据彻底丢失).

snapper (<http://snapper.io/>) 是一个现有的自动 btrfs 快照开源软件,
本来考虑的就是直接使用 snapper. 然而 snapper 只支持 **每小时** 快照一次,
间隔时间太长了. 对窝来说, 丢失最近一小时的数据是不可接受的. 因此,
只能自己再写一个软件了, 目标是实现 **分钟级** 快照 (Per Minute Btrfs Snapshot),
如果不小心发生了误删, 数据丢失通常在 1 分钟以内, 这是可以接受的.

注意, 快照 **不能替代备份** !! 如果发生 **硬件损坏** (比如磁盘故障), 操作系统
BUG (比如 Linux 内核, btrfs 文件系统的 BUG) 等情况, 仍然会发生数据丢失损坏,
快照对此无能为力.

---

根据上述使用场景, 只读快照需要以 `root` 在后台定期自动创建. root 是 Linux
系统中的超级用户权限, 如果出问题可能造成严重的破坏, 因此必须谨慎使用.

所以选择使用 `rust` (<https://www.rust-lang.org/>) 编程语言来编写本软件,
避免使用 `unsafe`, 同时尽量保持代码和功能的简单 (简单的东西更不容易出 BUG).

创建快照 (删除快照) 功能, 实现方法是直接调用 `btrfs` 命令行, 比如
`btrfs subvol snapshot -r` 和 `btrfs subvol delete`.

定期执行功能, 使用 systemd timer 触发
(<https://wiki.archlinux.org/title/Systemd>). 因为 systemd 是 Linux
系统的关键基础系统组件 (PID 1), 所以稳定性应该是很高的, 不容易出问题.

大量快照会占用大量的存储空间, 因此 **自动清理** 功能也是很重要的,
根据配置自动删除旧的快照.

注意: 目前本软件 **不会检测剩余存储空间**, 存在耗尽存储空间的风险, 需要多关注,
经常查看剩余存储空间. 因为本软件认为, 相比耗尽存储空间的风险,
数据丢失是更严重的情况, 更不能接受, 因此及时创建新的快照是更优先的事情.

## 2 源代码目录结构

- `src/`: 本软件的源代码 (rust).

- `doc/`: 文档.

- `systemd-unit/`: systemd 服务文件, 用于实现 systemd timer.

- `etc-pmbs/`: 配置文件示例.

- `build-aur/`: 用于构建 ArchLinux 软件包 (含有 AUR 的 `PKGBUILD`).

- `build-rpm/`: 用于构建 RPM 软件包 (用于 Fedora CoreOS).

- `.github/`: CI (自动化测试编译).

## 3 命令行环境变量

本软件的命令行参数是很简单的, 这可以简化解析命令行的代码. 更多配置选项通过
**环境变量** (env var) 实现.

[`config::ConfigEnv`] 定义了所有使用的环境变量:

| 环境变量         | 默认值          | 说明                           |
| :--------------- | :-------------- | :----------------------------- |
| `PMBS_DIR_ETC`   | `/etc/pmbs`     | 存放配置文件 (`*.toml`) 的目录 |
| `PMBS_DIR_LOG`   | `/var/log/pmbs` | 写入日志文件的目录             |
| `PMBS_BIN_BTRFS` | `btrfs`         | btrfs 命令                     |
| `RUST_LOG`       | `info`          | 输出日志级别 (`env_logger`)    |

## 4 快照目录结构

比如:

```sh
> ls -ali .pmbs
总计 4
929570 drwxr-xr-x 1 root root   20  9月 2日 03:48 ./
   256 drwxr-xr-x 1 root root   54  8月28日 23:32 ../
929571 drwxr-xr-x 1 root root 4080  9月 2日 03:48 2025/
944869 lrwxrwxrwx 1 root root   15  9月 2日 03:48 latest -> 2025/1756756128/
> ls -ali .pmbs/2025

   256 drwxr-xr-x 1 root root   54  8月28日 23:32 1756756005/
   256 drwxr-xr-x 1 root root   54  8月28日 23:32 1756756066/
   256 drwxr-xr-x 1 root root   54  8月28日 23:32 1756756128/
```

- 在目标 `subvol` (被快照的 subvol) 之下直接创建 `.pmbs` 目录, 比如
  `/home/.pmbs`

- 快照 (snapshot) 路径类似 `.pmbs/2025/1756756128/`

  其中 `1756756128` 是时间戳 (`UNIX_EPOCH` 开始的秒数), `2025` 是对应的年.

- 符号链接 `.pmbs/latest` 指向最新的快照.

## 5 systemd timer

有 2 个 systemd timer (以及对应的 service):

- `pmbs-snapshot.timer` (`pmbs-snapshot.service`) 定期执行:

  ```sh
  pmbs config snapshot
  ```

  读取所有配置文件, 并创建对应的快照.

  每分钟 (60 秒) 执行一次, 时间精度 1 秒, 随机延迟 1 秒.

- `pmbs-clean.timer` (`pmbs-clean.service`) 定期执行:

  ```sh
  pmbs config clean
  ```

  读取所有配置文件, 并执行自动清理.

  每 10 分钟执行一次, 时间精度 1 分钟, 随机延迟 1 分钟.

## 6 自动清理

TODO