# Btrfs文件系统

* [写时复制](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#写时复制)
* [子卷（Subvolume）](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#子卷subvolumearch-wiki-btrfs-subvolume)
  * [挂载子卷](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#挂载子卷)
  * [子卷的修改操作](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#子卷的修改操作)
    * [列出子卷](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#列出子卷)
    * [创建子卷](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#创建子卷)
    * [删除子卷](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#删除子卷)
    * [默认子卷](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#默认子卷)
    * [临时挂载](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#临时挂载)
  * [Btrfs子卷组织形式的探究](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#btrfs子卷组织形式的探究some-info-about-subvolume-in-opensuse)
  * [创建子卷的正规步骤](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#创建子卷的正规步骤)
* [快照](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#快照)
* [Btrfs启用压缩](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#btrfs启用压缩mount-compress)
* [使用snapper进行管理](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#使用snapper进行管理suse-restore-from-snapper)
  * [创建一个新的配置](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#创建一个新的配置)
  * [配置快照的设置](https://acherstyx.gitbook.io/notes/linux-system/pages/-M3lSeMHWGz8JX58hl_b#配置快照的设置)

Btrfs文件系统已经逐渐被各种Linux发行版本支持，虽然其似乎还存在一些小问题（unstable），但是在其各种诱人的功能面前似乎并不那么重要。 由于文件系统事关重大，在实际使用前，务必详细阅读[官方参考手册](https://btrfs.wiki.kernel.org/index.php/Category:Manpage)，其中详细描述了许多有价值的细节，解释了文件系统的运作模式，也包括命令参数的详解。

## 写时复制

写时复制（Copy-on-write, CoW）指了在多个调用者请求相同资源时，只有在某个调用者试图修改资源的内容时，系统才会为其复制一份专用副本。这样没有写操作的时候，就不会有多余的副本被创建。 CoW的缺点之一在于对于像VM镜像、数据库文件这样的就地更改（updated-in-place）的文件，会导致写入**碎片化**。所以对于这一类数据，不妨建一个子卷然后禁用CoW来储存他们。（别忘了修改`fstab`）

Btrfs默认启用写时复制，要停止使用写时复制，使用`nodatacow`选项，但是这一更改只会影响新创建的文件，对于已有文件（夹）使用下列命令进行修改，但仍存在一些细节问题，使用前务必参见参考资料中关于此节的详细描述。

```
chattr +C </path/to/file/or/folder>
```

## 子卷（Subvolume）

Btrfs通过Subvolume来实现在备份时排除某些文件夹。

### 挂载子卷

通过设置挂载的选项可以挂载指定的子卷：

```
mount -o subvol=<subvol> <device> <mount_path>
```

### 子卷的修改操作

#### 列出子卷

```
btrfs subvolume list -p path
```

使用后会列出对应`path`下的所有子卷，其数量可能会很多，因为所有的快照也以subvolume的形式储存，有意思的是Docker镜像也被保存为了subvolume：

#### 创建子卷

```
btrfs subvolume create </path/to/subvolume>
```

> 这里的`path`指的是子卷的绝对路径，比如当前挂载了`@`到`/mnt/@`目录下，则使用路径`/mnt/@/home`创建出来的子卷为`@/home`。

#### 删除子卷

```
btrfs subvolume delete /path/to/subvolume
```

> 如果只移除文件目录，而不使用`btrfs subvolume delete`命令并不会真正删除一个子卷。

#### 默认子卷

```
# 获取默认子卷
btrfs subvolume get-default /
# 设置默认子卷
btrfs subvolume set-default <subvolume-id> /
```

#### 临时挂载

```
# 使用路径挂载
mount -t btrfs -o subvol=<subvolume> </mount/point>
# 使用id挂载
mount -t btrfs -o subvolid=<id> </dev/device> </mount/point>
```

### Btrfs子卷组织形式的探究

在openSUSE中查看当前的Btrfs的子卷，可能会显示大量的子卷，因为snapshot实际也是通过子卷来实现的，另外值得注意的是Docker镜像也被作为snapshot独立开了：

```
~$ sudo btrfs subvolume list /
ID 256 gen 90 top level 5 path @
ID 257 gen 113574 top level 256 path @/var
...
ID 263 gen 113574 top level 256 path @/home
ID 266 gen 112569 top level 256 path @/.snapshots
ID 298 gen 98293 top level 257 path @/var/lib/docker/btrfs/subvolumes/ce11ad5...    # docker镜像
...
```

其中`@`代表了文件系统的根（rootfs），但事实上它也仍然是一个snapshot，最顶层的卷是以0为标号的子卷，不过通常不使用。 同时默认的`/`同样也不是`@`子卷，一般也是某一个子卷，只是默认被挂载为了`/`，通过查看默认子卷可以得知：

```
~$ btrfs subvolume get-default /
ID 267 gen 113599 top level 266 path @/.snapshots/1/snapshot  # 一个snapshot被作为默认子卷，挂载为了文件系统的 `/` 目录
```

可见当前系统的`/`实际上是一个路径为`@/.snapshots/1/snapshot`的子卷，真正的`@`在openSUSE中是隔离开的，作为独立的根来储存需要永久保存的子卷。

### 创建子卷的正规步骤

正如上述讨论，由于目前的系统目录也是一个（临时）快照。 如果我们此时要创建一个子卷，不可以建立在一个一个已有的快照下，否则在进行rollback操作后就不能再删除这个子卷了。正确的操作因该是将这个子卷建立在`@`子卷下。

```
sudo mount /dev/sda2 -o subvol=@ /mnt
sudo btrfs subvolume create /mnt/usr/important
sudo umount /mnt
```

## 快照

Btrfs的快照是建立在其“写时复制”的功能基础上的。 创建快照可以使用如下命令：

```
btrfs subvolume snapshot </path/to/source> </path/to/dest>
```

> 对于openSUSE，目标目录通常为`/.shapshots`，这一目录为默认的统一存放快照的目录。 另外添加参数`-r`可以创建只读快照，在只读快照上再创建一个快照可以获得只读快照的一个可写版本。

注意快照**不是递归包含**的，意味着子卷里的子卷在快照中会是空目录。 这也是为什么openSUSE下部分目录被排除在默认的snapper备份之外：它们都被创建为了额外的子卷，由于上述非递归性，他们在对`/`创建的快照中均被忽略了。

## Btrfs启用压缩

在openSUSE中是支持Btrfs的压缩功能的，通过`mount`的参数可以启用压缩：

```
mount -o compress </dev/sdx> </mount/point>
```

`compress`的默认规则是：如果你创建了一个文件，Btrfs压缩后发现压缩率低，那对于之后的写入它都不再会进行压缩。如果不希望这样，可以使用`compress-force`。 对于已经写入的文件，均不会被压缩，**压缩仅对新写入的文件有效**。

压缩有三种算法可选：

1. **lzo**：压缩率低但是CPU资源占用少。
2. **zlib**：压缩率高但是资源占用多。
3. **zstd**：旧版本内核和`GRUB`引导对其缺乏支持，暂时忽略。

在`fstab`中永久启用压缩，并指定压缩算法（算法以不指定）：

```
UUID=1a2b3c4d /home btrfs subvol=@/home,compress=lzo  0   0
```

## 使用snapper进行管理

`snapper`通过一系列的配置来管理Btrfs分区，配置文件默认位于`/etc/snapper/configs/`下。 默认的方案只为`/`创建快照，且内容还要排除名下的子卷。

### 创建一个新的配置

```
sudo snapper -c <config-name> create-config </path>
```

这一操作会创建一个快照并从`/etc/snapper/config-templates/default`获取一套默认配置。

### 配置快照的设置

见openSUSE[相关文档的对应章节](https://documentation.suse.com/zh-cn/sles/12-SP4/html/SLES-all/cha-snapper.html#sec-snapper-config-modify)，以获得更准确的信息。

使用`snapper -c home set-config "<KEY>=<value>"`来修改设置。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://acherstyx.gitbook.io/notes/linux-system/btrfs-wen-jian-xi-tong.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
