是否可以在 linux 中暂时隐藏任何系统调用的文件?
Is it possible to temporarily hide a file from any system call in linux?
我正在尝试使用 FUSE 实现一个文件系统,我希望文件在删除时暂时隐藏起来。我试图将所有文件的名称或其索引节点存储在一个数组中,并在调用 'open' 、 'getattr' 或 'readdir' 等系统调用时检查它们。但是当数量变得非常庞大时,它可能会消耗大量的性能。所以我想知道有没有更好的方法来做到这一点?提前致谢!
据我了解,当调用 unlink 时,您希望将文件标记为已删除而不是实际删除。
您可以将此标记实现为一个扩展属性,一个带有“系统”命名空间(请参阅 https://man7.org/linux/man-pages/man7/xattr.7.html)的属性,它未列为文件 xattr 列表的一部分。
在您的“取消链接”中,执行 setxattr(system.markdelete)。在您使用路径 arg 和 readdir 中的所有其他调用中,getxattr 并将其视为已删除。
您的方法(以及 Oren Kishon 指出并标记为 selected 的解决方案)有两个问题:
- 首先是文件没有名称本身。文件名不是文件的一部分。文件名到文件(实际上是索引节点)的映射是由系统为用户的商品创建的,但名称完全独立于它们指向的文件。这意味着你很容易知道 a link 指向哪个 inode,但是很难进行反向映射(获取指向 inode 的目录条目,只知道 inode)删除 a文件是一个两阶段的过程。在第一阶段,你调用
unlink(2)
系统调用从它所属的目录中删除一个 link(删除一个目录条目),然后释放属于该文件的所有块, 但只有在引用计数(存储在 inode 本身中)下降到零的情况下。 这是一个简单的过程,因为一切都从您要删除的目录条目开始。但是如果你不删除它,以后搜索它会很痛苦,如下所示,在此处所述的第二个问题中。
- 其次,如果您对同一个文件执行此操作,比方说,六个 links(硬 links),您永远不会知道什么时候需要 space 实际重新分配给另一个文件(因为你 运行 未分配 space)因为 link 引用计数在 inode 上仍然是 6。更糟糕的是,如果您在索引节点中添加第二个引用计数以跟踪尚未取消分配的(不同的)真正擦除文件的数量,问题是您必须搜索整个文件系统。(因为您不知道关于 link 应该在哪里)所以你需要维护很多信息(添加到 space 文件在文件系统中占据)首先收集所有 link指向此文件一次,然后检查这是否确实是必须释放的文件,以防文件系统中需要更多 space。
顺便说一句,您的问题在用户 space 中有一个简单的解决方案。只需修改 rm
命令以永不完全删除文件(例如,永不将最后一个 link 取消 link 到文件),而是将文件移动到某个固定目录中的队列中文件所在的同一文件系统,以处理最后一个 link 到它,这将保持文件仍然分配(但你失去了任何参考,或者你可以将它保存在关联文件中,到名称文件)。监控进程可以检查队列中第一个空闲 space 和 select 的数量(erased oldest),并真正擦除它。请注意,如果您删除了大文件,这将使您的系统负载在需要实际删除您正在解除分配的文件时随机增加。
还有另一种选择。使用 zfs 作为你的文件系统。这需要大量内存和 cpu,但它是文件恢复的完整解决方案,因为 zfs 保存了文件系统的完整历史记录,因此您可以及时回到文件存在的快照,然后复制它,实际上是恢复它。 ZFS 可用于 WORM(一次写入多次读取,如 DVD)媒体,这允许您随着时间的推移保存文件系统状态(以不再重复使用相同数据为代价)但您永远不会丢失文件。
编辑
在一种情况下,文件不再可用于打开它的进程之外的任何其他进程。在这种情况下,一个进程打开一个文件,然后将其删除(删除只涉及破坏 link 允许将文件名转换为系统中的索引节点)但继续使用该文件,直到它最终关闭.
您可能知道,一个文件可以同时被多个文件打开。除了磁盘 inode 中的引用数外,内核内存中的 inode table 中还有许多对 inode 的引用。这是磁盘 inode 中文件的引用数(指向文件 inode 的目录条目数)加上每个表明文件已打开的文件条目的引用。
当一个文件未被link编辑时(它应该被删除,因为没有更多的 link 索引节点引用它)释放不会立即进行,因为文件仍在被进程使用。该文件是活动的,尽管它没有出现在文件系统中(在任何目录中都没有对它的更多引用)只有当文件的最后一个 close(2)
发生时,该文件才会在系统中释放。
但是引用该文件的目录条目发生了什么。它可以在释放文件之前很久就可以重用(正如我在其中一条评论中告诉您的那样)。将创建一个新文件(它将强制成为不同的 inode,因为旧文件仍在使用中)并将其命名为原始文件(因为您决定将其命名为相同的文件)并且这没有问题,但是您正在使用不同的文件。旧文件仍在使用中,没有名称,因此除了使用它的进程之外,其他进程看不到它。此技术经常用于使用临时文件,在其中您使用 open(2)
创建一个文件,并立即 unlink(2)
它。没有其他进程可以访问该文件,一旦文件条目为 close(2)
d,该文件将被释放。但是这样的文件将在最后一个 close(2)
被调用时被释放。具有此特征的文件无法在系统重启后继续存在。 (它甚至无法在打开它的过程中存活下来)
如问题所述:
Is it possible to temporarily hide a file from any system call in linux?
该文件对所有需要文件名的系统调用都是隐藏的(它不再有名称),但对其他系统调用不隐藏(例如 fstat(2)
继续工作,而 stat(2)
将无法在该文件上使用,与 link(2)
、rename(2)
、open(2)
等相同)
我正在尝试使用 FUSE 实现一个文件系统,我希望文件在删除时暂时隐藏起来。我试图将所有文件的名称或其索引节点存储在一个数组中,并在调用 'open' 、 'getattr' 或 'readdir' 等系统调用时检查它们。但是当数量变得非常庞大时,它可能会消耗大量的性能。所以我想知道有没有更好的方法来做到这一点?提前致谢!
据我了解,当调用 unlink 时,您希望将文件标记为已删除而不是实际删除。 您可以将此标记实现为一个扩展属性,一个带有“系统”命名空间(请参阅 https://man7.org/linux/man-pages/man7/xattr.7.html)的属性,它未列为文件 xattr 列表的一部分。 在您的“取消链接”中,执行 setxattr(system.markdelete)。在您使用路径 arg 和 readdir 中的所有其他调用中,getxattr 并将其视为已删除。
您的方法(以及 Oren Kishon 指出并标记为 selected 的解决方案)有两个问题:
- 首先是文件没有名称本身。文件名不是文件的一部分。文件名到文件(实际上是索引节点)的映射是由系统为用户的商品创建的,但名称完全独立于它们指向的文件。这意味着你很容易知道 a link 指向哪个 inode,但是很难进行反向映射(获取指向 inode 的目录条目,只知道 inode)删除 a文件是一个两阶段的过程。在第一阶段,你调用
unlink(2)
系统调用从它所属的目录中删除一个 link(删除一个目录条目),然后释放属于该文件的所有块, 但只有在引用计数(存储在 inode 本身中)下降到零的情况下。 这是一个简单的过程,因为一切都从您要删除的目录条目开始。但是如果你不删除它,以后搜索它会很痛苦,如下所示,在此处所述的第二个问题中。 - 其次,如果您对同一个文件执行此操作,比方说,六个 links(硬 links),您永远不会知道什么时候需要 space 实际重新分配给另一个文件(因为你 运行 未分配 space)因为 link 引用计数在 inode 上仍然是 6。更糟糕的是,如果您在索引节点中添加第二个引用计数以跟踪尚未取消分配的(不同的)真正擦除文件的数量,问题是您必须搜索整个文件系统。(因为您不知道关于 link 应该在哪里)所以你需要维护很多信息(添加到 space 文件在文件系统中占据)首先收集所有 link指向此文件一次,然后检查这是否确实是必须释放的文件,以防文件系统中需要更多 space。
顺便说一句,您的问题在用户 space 中有一个简单的解决方案。只需修改 rm
命令以永不完全删除文件(例如,永不将最后一个 link 取消 link 到文件),而是将文件移动到某个固定目录中的队列中文件所在的同一文件系统,以处理最后一个 link 到它,这将保持文件仍然分配(但你失去了任何参考,或者你可以将它保存在关联文件中,到名称文件)。监控进程可以检查队列中第一个空闲 space 和 select 的数量(erased oldest),并真正擦除它。请注意,如果您删除了大文件,这将使您的系统负载在需要实际删除您正在解除分配的文件时随机增加。
还有另一种选择。使用 zfs 作为你的文件系统。这需要大量内存和 cpu,但它是文件恢复的完整解决方案,因为 zfs 保存了文件系统的完整历史记录,因此您可以及时回到文件存在的快照,然后复制它,实际上是恢复它。 ZFS 可用于 WORM(一次写入多次读取,如 DVD)媒体,这允许您随着时间的推移保存文件系统状态(以不再重复使用相同数据为代价)但您永远不会丢失文件。
编辑
在一种情况下,文件不再可用于打开它的进程之外的任何其他进程。在这种情况下,一个进程打开一个文件,然后将其删除(删除只涉及破坏 link 允许将文件名转换为系统中的索引节点)但继续使用该文件,直到它最终关闭.
您可能知道,一个文件可以同时被多个文件打开。除了磁盘 inode 中的引用数外,内核内存中的 inode table 中还有许多对 inode 的引用。这是磁盘 inode 中文件的引用数(指向文件 inode 的目录条目数)加上每个表明文件已打开的文件条目的引用。
当一个文件未被link编辑时(它应该被删除,因为没有更多的 link 索引节点引用它)释放不会立即进行,因为文件仍在被进程使用。该文件是活动的,尽管它没有出现在文件系统中(在任何目录中都没有对它的更多引用)只有当文件的最后一个 close(2)
发生时,该文件才会在系统中释放。
但是引用该文件的目录条目发生了什么。它可以在释放文件之前很久就可以重用(正如我在其中一条评论中告诉您的那样)。将创建一个新文件(它将强制成为不同的 inode,因为旧文件仍在使用中)并将其命名为原始文件(因为您决定将其命名为相同的文件)并且这没有问题,但是您正在使用不同的文件。旧文件仍在使用中,没有名称,因此除了使用它的进程之外,其他进程看不到它。此技术经常用于使用临时文件,在其中您使用 open(2)
创建一个文件,并立即 unlink(2)
它。没有其他进程可以访问该文件,一旦文件条目为 close(2)
d,该文件将被释放。但是这样的文件将在最后一个 close(2)
被调用时被释放。具有此特征的文件无法在系统重启后继续存在。 (它甚至无法在打开它的过程中存活下来)
如问题所述:
Is it possible to temporarily hide a file from any system call in linux?
该文件对所有需要文件名的系统调用都是隐藏的(它不再有名称),但对其他系统调用不隐藏(例如 fstat(2)
继续工作,而 stat(2)
将无法在该文件上使用,与 link(2)
、rename(2)
、open(2)
等相同)