我正在构建一个新系统,其中安装了 Linux,用于特定目的,需要第三方与之交互。除了应用必要的防火墙规则将机器与内联网隔离,同时仍允许从互联网访问/到互联网之外,我还需要此系统上的所有文件保留(即我能够稍后检索它们),即使在发出 (rm
可能是root
)或其他方式(程序发出系统调用以删除它)时也是如此。我不想只获取文件系统操作的日志(我相信可以通过auditd
和 Tripwire 等工具检索),我还需要文件的内容。我已经考虑了几种实现此目标的方法:
- 别名
rm
化为mv some-where-else
, 或者 - 修改
rm
二进制文件。虽然这两种方法在一定程度上可以起作用,但如果程序试图删除文件,它就会失败,因为现在rm
二进制文件没有被调用。 - 在内核中挂载写入和删除函数这样 write 调用将同时写入两个位置,而 remove 调用将仅删除“可见”副本。我认为这是另一种可行的方法,不确定是否会造成任何破坏。这涉及编写一个小的内核模块。虽然我在这方面没有太多专业知识,但如果有源代码,我可以并且会构建一个。
- 为用户提供
fakeroot
。这很可能行不通,因为他们坚持要有 root 权限才能安装程序并修改系统范围的配置(例如,在/etc
和 swap 中的配置)。他们一尝试接触这些文件就会知道这一点。 - 在其他地方保存目录的镜像 IE在逻辑层面上进行镜像。据我所知,可以使用 之类的工具来完成
rclone
。但是,这只有在创建文件的原始进程完成后才能起作用。我认为,如果将一个非常大的zip
文件复制到系统的其他地方,只提取一个很小的文本文件,然后zip
删除该文件,那么它就会丢失。 - 选择支持此级别操作的文件系统或者修改文件系统配置。因为我还没有创建系统,所以我可以决定使用什么文件系统。我总是选择
ext4
正常安装,但我没有任何经验,也不知道针对这种情况要修改什么配置。我以前用过一段时间squashfs
,overlayfs
它确实有一个我想要的功能。当原始文件被删除时,overlayfs
只隐藏文件。如果overlayfs
分区被删除,原始文件会重新出现。但是,我还想在删除新文件之前获取它们的内容。 - 积极监控并创建文件引用(软链接?)这样
rm
文件就不会处于悬空状态并需要回收。理论上,我认为这很好,但我不知道是否存在这样的工具。 - 创建软件 RAID 1,但以某种方式禁用一个卷上的删除功能。我认为这是不可能的,因为 RAID 1 的运行级别低于逻辑文件系统。但是,如果需要,我可以创建更多卷。
- 关闭有问题的系统到虚拟机,并制作其磁盘快照。为第三方提供虚拟机是可以接受的。但是,虚拟机仍然比存储在内部的逻辑文件系统低级。我认为主机没有办法知道内部正在执行什么确切的文件系统操作。按间隔进行快照不是一个好主意,因为它会占用大量存储空间,还会错过在快照窗口之间创建和删除的文件。
如果我的理解有误,请纠正我。任何见解都将不胜感激!
编辑:我想澄清一下从用户的角度来看:
- 我
rm
需要成功以及可能进行系统调用来删除文件的二进制文件,remove
例如返回 0。 ls
“删除”后不再显示该文件。
但是,我仍希望之后能够访问该文件。它可能存储在其他地方。
- 如果可能的话,应该保留原始目录和文件名,如果删除了旧文件,则可以任意更改文件名,并写入同名的新文件。
- 如果需要的话,我可以向系统添加更多卷来实现这个目标。
答案1
对于您的要求(连续快照、具有 root 访问权限的用户、对用户隐藏备份操作),正确选择文件系统应该是实现您的目标的最简单方法。
你必须记住的一个关键词是“奶牛”=“写入时复制”。此功能只会在更改时复制数据。复制文件只会创建指向数据的新指针,因此不会占用新空间(元数据除外)。
在操作系统级别硬链接可以通过链接到相同的文件(查看 inode 的使用情况)ln Path1/OrgFile Path2/OrgfileCopy
来实现类似的效果。但如果某个软件写入同一个文件(inode 仍然存在),副本也会更改内容 —inode
ls -i
那不是 CoW,也不会满足您的期望。
在CoW 感知文件系统副本会创建一个新的 inode,新 inode 的指针指向原始文件的数据块。当文件发生更改时,CoW 功能会在写入新版本的块之前将旧数据块复制到新位置。这样,只有更改的块才会保留新空间。– CoW 是任何快照的核心思想,因此真正的快照在创建时不会复制数据(占用新空间)。
此时使用文件系统可能是一种选择,特别是快照可以传输到其他位置,甚至可以通过 ssh 传输(参见btrfs send
和btrfs receive
)。
由于您偏好的是连续而非定时备份类型,btrfs
因此可能不是第一选择,因为这是定时的,您可能会丢失一些短暂存在的文件。
与 btrfs 类似的是虚拟文件系统,当谈到快照时。ZFS 的快照也代表了时间状态,而不是变化状态的连续描述。ZFS 的优势在于其他功能,即超大卷或本机加密支持,使其成为 NAS 系统的最爱。(虽然在存储上加密,但您必须使用选项-w
将加密的快照发送到其目的地zfs send -w ...
:)
另一个解决方案是近端局部缺血模型(更好的 NILFS2)。
“NILFS”=“日志结构文件系统的新实现”— 整个文件系统的组织方式就像一个日志文件!
因此,在文件系统结束之前,不会覆盖任何数据。由于其组织遵循环形缓冲区的逻辑,垃圾收集器将找到最旧的删除内容,以释放空间来存放新数据。
只要您没有通过连续写入/删除垃圾数据(或使用“擦除磁盘”工具)用新数据或新创建和删除的数据填满整个磁盘大小,您就可以重建删除和更改。这对于几秒钟前有人用不同的数据多次写入相同的感兴趣的 inode 的数据也是如此。
可以列出“检查点”,lscp
然后通过创建快照mkcp -s
(在一段时间内保护此状态免受垃圾收集器的破坏)并安装快照,即使当前卷处于活动状态。 - 可以同步快照,即通过rsync
在其他卷上存档感兴趣的状态。
也可以看看:
https://www.kernel.org/doc/Documentation/filesystems/nilfs2.txt
注解:
NILFS 开发已被 Linux 内核团队接管,并且目前正在维护(尽管在互联网上搜索时可能存在第一印象)。
“小西龙介”的原始资料库(NILFS 创始人/几乎不活跃)
Linux 内核的 NILFS2 文件系统分支(来自 Linus Torvalds/维护)
NILFS 真实主页:https://nilfs.sourceforge.io—
(警惕 nilfs.org — 这是假的!)
答案2
你调查过吗吉特? 看来您的文件系统需要完整的审计功能,这是版本控制系统的一个特性。GitFS 是一个 FUSE 文件系统,它将提交每个更改并将其推送到远程。
现在对整个文件系统进行版本控制可能有些过度,但您可以只挂载第三方感兴趣的目录。
附注:如果您与上述第三方存在信任问题,那么技术解决方案可能不适合这种特殊情况。
答案3
我最初的想法是尝试使用 Syncthing 在其他地方提供目标文件夹的单向(只读)副本,并启用版本控制。
作为一种解决方案,它避免了对低级聪明才智的需求,但如果不尝试,我不确定它将如何处理您的 zip 文件场景。
答案4
如果你真的想完成其他答案建议的所有事情自动且透明,迄今为止实现它的最简单的方法,并保证它将强制执行你的规则,无论如何,就是创建你自己的非常简单的 FUSE 文件系统实现。
因为您要做的就是:
- 使用您最喜欢的语言编写一个简单的“空”直通文件系统,其中每个函数仅针对文件系统中的原始位置调用相同的函数。对于许多语言来说,该实现已经包含在该语言的 FUSE 库实现中,因此无需做任何工作。这是 libfuse 的 C 语言原始示例,您可以直接使用。
- 改变必要的功能(如)
unlink()
或write()
,以使用其他答案提供的解决方案来创建新版本。 - 将原始文件系统挂载到不寻常的位置,远离用户访问。
- 将修改后的直通 FUSE 文件系统挂载为根文件系统。
理想情况下,最后两个步骤是在 Initramfs 中完成的,就像使用其他更不常见的文件系统作为根文件系统一样。
考虑到它的简单性,即使使用 Python 这样的简单语言实现这一点也仍然很快。所以即使你对编程不太了解,这也是绝对可行的。(如果你是一家企业,任何程序员都可以在几分钟内用 C 语言为你实现这一点,而且花费很少。)
您将遇到的唯一麻烦是您的存储空间将被填满。因此您需要一个垃圾收集器。理想情况下,需要一个守护进程来将其运行到空闲时间,并且非常重要的是,在您的 FUSE 文件系统中有一个钩子,当操作因没有剩余空间而遇到麻烦时触发垃圾收集器。这将是非常重要的部分。
但是,如果您真的想让系统透明,又不想弄乱迟早会出问题的自定义黑客,那就没有办法了。
无论如何,垃圾收集器可能只是一个简单的 shell 脚本,这样就没问题了。(对于第一个版本,您甚至可以直接在 FUSE 文件系统代码中实现它,而不必创建单独的守护进程来启动和运行它。)
(此外,上次我尝试使用 btrfs 时,遇到了一个根本的设计问题,即无法知道同一卷上不同快照占用的实际大小。因为它被实现为多个根目录。所以你需要遍历整个树,才能知道它占用了多少空间。由于了解它占用多少空间对于这个解决方案(以及对于多用户服务器)至关重要,所以我只能建议避免将它作为你的基本文件系统。)