世界是一个 libferris 文件系统

作者:Ben Martin

libferris 虚拟文件系统一直致力于突破文件系统应有的界限,包括可以挂载的内容以及可用于文件的元数据。在过去的五年中,它扩展了其功能,从挂载更传统的内容(例如 tar.gz、SSH、数码相机和 IPC 原语)到能够挂载各种索引顺序访问方法 (ISAM) 文件,包括 db4、tdb、edb、eet 和 gdbm;各种关系数据库,包括 odbc、MySQL 和 PostgreSQL;各种服务器,例如 HTTP、FTP、LDAP、Evolution 和 RDF 图;以及 XML 文件和 Sleepycat 的 dbXML。

最近,添加了对使用 Lucene、ODBC、TSearch2、xapian、LDAP、PostgreSQL 和 Web 搜索的任意组合来索引文件系统数据的支持,并能够查询这些后端以查找匹配的文件。匹配项自然地以虚拟文件系统的形式呈现。libferris 的索引和搜索功能的详细信息出现在 2005 年 2 月的Linux Journal 杂志上,在我的文章“使用 libferris 的文件系统索引”中。我应该提到,您在这篇文章中看到的任何作为文件系统挂载的内容都可以像之前关于搜索的文章中描述的那样进行索引和搜索。

您可以通过原生 libferris 客户端或通过 Samba 导出 libferris 来访问您的 libferris 虚拟文件系统。

libferris 中的两个主要抽象概念是上下文 (Context) 和扩展属性 (EA)。上下文可以被认为是文件或目录的超类。在 libferris 中,文件和目录之间的区别较小,文件如果被视为目录,则可以像目录一样运行。例如,如果您尝试将 tar.gz 文件作为目录读取,libferris 会自动将该存档挂载为文件系统,并将存档的内容列为虚拟文件系统。

EA 接口可以被认为是类似于 Linux 内核的 EA 接口的概念。也就是说,任意键值数据附加到文件和目录。libferris 早期扩展了 EA 概念,允许属性的值从文件的内容中派生出来。这意味着图像或视频文件的宽度和高度等简单内容与文件的大小和修改时间一起成为一流的元数据公民。可用元数据的限制远远超出图像元数据,包括 XMP、EXIF、音乐 ID 标签、Annodex 媒体、地理空间标签、RPM 元数据、SELinux 集成、部分排序的徽标类别以及任意个人 RDF 元数据存储。

通过单个接口提供所有元数据,使 libferris 能够对任何元数据提供过滤和排序功能。因此,您可以像使用ls -Sh按文件大小排序一样轻松地按任何元数据对目录进行排序。libferris 也支持对多个元数据值进行排序;您可以轻松地按 MIME 类型、然后是图像宽度、然后是修改时间对文件进行排序——所有这三个元数据片段都有助于最终的目录排序。任何 libferris 虚拟文件系统都可以应用过滤和排序以获得新的 libferris 虚拟文件系统。

您可以将 EA 值存储到个人 RDF 存储中——例如,当您将图像宽度写入扩展属性时。当您随后读取图像宽度时,您会获得刚刚写入 EA 的值。这自然地扩展到其他情况,例如当您更改窗口的 x 或 y EA 时,这应该移动窗口。

允许将 EA 存储在个人 RDF 文件中,您可以将元数据添加到任何 libferris 对象,即使是您只有读取权限的对象。例如,您可以像对普通文件一样,将徽标或评论附加到 Linux Kongress 网站。

所有文件的一个有趣的 EA 是内容 EA,它等同于文件的字节内容。通过 EA 接口公开文件本身意味着可以通过相同的接口获得有关文件的任何信息。

libferris 使用 C++ 编写,并为上下文和 EA 提供标准的 IOStream 接口。许多标准文件实用程序已被重写以利用 libferris 功能。这些客户端包括 ls、cp、mv、rm、mkdir、cat、find、touch、IO 重定向等等。

文件系统交互

当我们探索这些文件系统时,我使用了 ferrisls 命令,它模仿了 coreutils ls(1) 命令。除了 -l 长列表选项外,我还使用了 ferrisls 的 -0(零)recommended-ea 选项。它的操作方式与 -l 非常相似,只是它会询问文件系统本身,哪些 EA 对于用户来说是最有趣的。我在代码示例中假设了 fls=ferrisls 的 shell 别名。

我首先展示与基于标准内核的文件系统以及一些 EA 可能性的交互。除了 recommended-ea 选项外,ferrisls 还支持 --xml 选项以生成 XML 文档作为输出。这提供了有关每个值所属的 EA 的信息,并提供了一种使用 libferris 驱动 Web 界面的可能性。

列表 1. 具有显式元数据的目录的长列表

$ fls -l \
--show-ea=size-human-readable,width,height,name
4.5k    48      46      emacs.png
1.9k    48      48      gnome-warning.png
3.2k    48      48      gnome-xterm.png
2.5k    48      48      gtkvim.png

列表 2. 请求 libferris 本身确定当前目录的哪些 EA 感兴趣,并生成 XML 文档作为输出。

$ fls -0 --xml
<ferrisls>
<ferrisls url="file:///tmp/lj"  name="lj"  >
 <context  size-human-readable="4.5k"
  protection-ls="-rw-r-----"
  mtime-display="05 Dec  4 23:39"
  name="emacs.png"  width="48"  height="46"  />
 ...
</ferrisls>
</ferrisls>

如前所述,如果您在不提供完整排序的 EA 上对目录进行排序,您可以将排序谓词链接在一起。例如,在列表 3 中,我根据数字 EA 高度对输出进行了排序,然后对名称 EA 使用了版本字符串排序。版本排序类似于 ls(1) -v 选项,在列表 3 中,它将 foo20.png 放在 foo3.png 之后。当按文件类型或 MIME 主要类型然后按名称排序时,这种排序非常有用。

列表 3. 排序输出

$ fls --show-ea=width,height,size,name \
  --ferris-sort='(:#:height)(:V:name)'
48      48      1968    gnome-warning.png
48      48      3253    gnome-xterm.png
48      48      2550    gtkvim.png
48      46      4589    emacs.png
48      46      4589    foo3.png
48      46      4589    foo20.png

文件形成树和文件附加键值对这两个概念类似于 XML 的结构。使用 libferris,您可以像浏览另一个文件系统一样浏览 XML 文档。例如,请参见列表 4。

列表 4. XML 作为文件系统的初步探索

$ cat example.xml
<root>
  <file1 size="200" />
  <file2 interesting="yes" />
  <file3>filesystems rock
</file3>
</root>

$ fls -0 ./example.xml/root
file1
file2
file3

$ fls -d --show-ea=name,interesting \
   ./example.xml/root/file2
file2   yes

$ fcat example.xml/root/file3
filesystems rock

通过与文件系统交互,您还可以导致底层 XML 文档的更新。ferris-redirect 客户端的存在是为了允许 shell 类型的重定向到 libferris 文件中。-T 或 --trunc 选项在将 stdin 写入文件之前截断现有文件。这很像 >| shell 选项。正如您从列表 5 中的交互中看到的那样,我们通过文件系统交互显着更改了 example.xml 文档的结构。

列表 5. 通过文件系统更改 XML 文件

$ echo "VIRTUAL filesystems rock more" | \
  ferris-redirect -T ./example.xml/root/file3

$ echo "a new way" | \
  ferris-redirect ./example.xml/root/file4

$ ferrisrm ./example.xml/root/file2

$ ftouch ./example.xml/root/touched

$ cat example.xml
<?xml version="1.0" encoding="UTF-8"
   standalone="no" ?>
<root>
  <file1 size="200"/>

  <file3>VIRTUAL filesystems rock more
</file3>
  <file4>a new way
</file4>

  <touched/>

</root>

由于许多现代文字处理文档都是压缩容器内部的 XML,因此 libferris 允许您像浏览文件系统一样深入研究 Office 文档。在列表 6 中,我将一个简单的 OpenOffice.org Writer 文档列为文件系统。

列表 6. OpenOffice.org 文档也是文件系统

$ fls -lh show-ea=size,name,content \
~/sample-oo-writer.odt/content.xml/ \
office:document-content/office:body/office:text
 0       office:forms
 18      text:p Paragraph number 1
 0       text:p-1
 116     text:p-2 This is the second paragraph ...
 0       text:p-3
 39      text:p-4 And in summary, this is really...
 0       text:p=5
 0       text:sequence-decls

可以为任何 libferris 文件系统获得 Xerces-C 文档对象模型 (DOM),就像可以将 Xerces-C DOM 挂载为 libferris 文件系统一样。文件系统的 DOM 的创建是延迟评估的,因此您可以获得 file:// 的 DOM,并且只会创建所需的 DOM 部分。

将任何 libferris 文件系统转换为 DOM 的能力使您可以轻松地将 XSLT 应用于您的文件系统。列表 7 中的示例 C++ 代码将样式表应用于已挂载的 OpenOffice.org 文档。

列表 7. 将 XSLT 应用于文件系统的 C++ 代码片段

fh_context c = Resolve( "~/example.odt/content.xml/"
 "office:document-content/office:body/office:text");
DOMDocument* theDOM = Factory::makeDOM( c );
...
// should use XercesDOMWrapperParsedSource
XalanTransformer theXalanTransformer;
theXalanTransformer.transform(
    theDOM, "~/my-oo.xsl", cout );

最近,libferris 添加了对挂载 Firefox、Evolution 和 X Window System 等应用程序的支持。

evolution:// 文件系统允许您挂载您的 Evolution 邮件客户端。当前支持扩展到您的邮件文件夹和联系人。使用此文件系统,不再需要将附件保存到临时文件才能从支持 ferris 的系统访问它们。

挂载您的 X Window System 是通过 xwin:// 文件系统完成的。这允许访问您的窗口对象,并允许您在 KDE 桌面版上挂载 Klipper。对于 Klipper,您可以ls, catcp轻松地访问您过去的剪贴板交互,并且覆盖顶部的剪贴板元素实际上就是剪贴板复制。窗口挂载让您了解您的窗口在 x,y 偏移量方面的位置以及其他有趣的数据。列表 8 显示了挂载我的 Evolution 邮件客户端和 X Window System 的示例会话。

列表 8. 挂载 Evolution 和 X Window System

$ fls evolution://
contacts  mail
$ fls -0 evolution:///contacts/system/
...
witme-ferris   witme-ferris@lists.sourceforge.net
...
$ fls -0 xwin:///clipboard
0       #include <Ferris/Ferris.hh>
1       Let the cricket stick to its hearth
2       ...

挂载数据库允许您浏览数据库服务器、其数据库及其表和视图。如列表 9 所示,我创建了一个数据库,填充了它,并将其作为虚拟文件系统进行交互。使用 ferrisls 的 --xml 选项的最终命令以 XML 格式导出每个元组。

libferris 选择将用户名和密码存储在配置文件中,而不是将用户名和密码嵌入到 URL 中。这是一种权衡,在权衡中,意外复制和粘贴带有嵌入式用户凭据的 URL 的风险被最小化,但代价是拥有可用的凭据的中心存储以及在哪里使用每个凭据的映射。对于许多常见的 URL,也支持内联身份验证信息。

列表 9. PostgreSQL 作为文件系统

$ psql
# create database tmp;
# \c tmp
# create table foo ( message varchar(100) not null,
                    id int primary key );
# insert into foo values ( 'doki doki', 1 );
# \q

$ fls -0 pg:///tmp/foo
doki doki 1       1       id

$ fcat pg:///tmp/foo/1
<context  id="1"  message="doki doki"  />

$ echo "waku waku" | ferris-redirect \
  -T --ea=message pg:///tmp/foo/1

$ fls -0 pg:///tmp/foo
waku waku  1       1        id

$ gfcreate pg:///tmp/foo
# See the gfcreate-tuple figure

$ fls -0 pg:///tmp/foo
utsukushii  2       2        id

$ psql tmp;
# select * from foo;
        message         | id
------------------------+----
waku waku               |  1
utsukushii              |  2

列表 9 中显示的 gfcreate 调用在图 1 中捕获。

The World Is a libferris Filesystem

图 1. 通过文件系统在 PostgreSQL 中创建新元组

libferris 文件系统可以指定它乐于在其上创建哪些对象。您可以使用 ferriscreate 包中的 fcreate 或 gfcreate 工具查看此列表。对于fcreate -l /tmp,例如,将显示大量可能性。对于 PostgreSQL 数据库,您只能创建少量新的对象类型,如列表 10 所示。我稍后将使用 fcreate 创建一个新的空 db4 文件,以展示文件系统监视如何在 libferris 中虚拟化。

列表 10. 我可以为 PostgreSQL 文件系统创建哪些类型的东西?

$ fcreate -l pg:///tmp
listing types that can be created
    for context: pg:////tmp
queryview
table

$ fcreate -l pg:///tmp/foo
listing types that can be created
    for context: pg:////tmp/foo
tuple

对 libferris 文件系统所做的许多更改会立即反映在其他 libferris 应用程序中。许多内核级接口让应用程序知道内核文件系统何时发生更改——例如,inotify 和 dnotify。libferris 扩展了这一点,允许客户端知道虚拟文件系统何时发生更改。例如,当您更新 XML 文件内部的元素时,inotify 只会告诉您 XML 文件已更改。使用 libferris,您可以准确地看到 XML 文件的哪个部分被其他 libferris 应用程序修改。

列表 11 演示了文件系统监视支持。在该示例中,我使用了 ferrisls 的 --monitor-all 选项。这使 ferrisls 的操作类似于给定 URL 的 tail -f;任何创建、删除或有趣的文件系统活动都会显示在控制台上。在另一个终端中,列表 12,我正在 Berkeley db4 文件中创建、删除和写入“文件”。ferrisls 愉快地报告了这些虚拟文件中发生的事情。

列表 11. 一个虚拟控制台的输出

$ fcreate --create-type=db4 --rdn=raw.db .
$ fls --monitor-all -0 ./raw.db
Created new1
Changed c:0x8321f88     /tmp/ljdb/raw.db
Changed c:0x8321f88     /tmp/ljdb/raw.db
Deleted new1
Created redirected-output
Changed c:0x8321f88     /tmp/ljdb/raw.db

列表 12. 另一个虚拟控制台的输出

$ ftouch ./raw.db/new1

$ ferrisrm -v ./raw.db/new1
removing ./raw.db/new1

$ echo "hello" | \
   ferris-redirect -T ./raw.db/redirected-output

$ fcat ./raw.db/redirected-output
hello

使用 libferris 执行的许多操作也会被存储以供将来可能使用。这包括您最近创建的文件类型(png、jpeg、db、元组等)、您最近编辑和查看的文件等等。所有这些都仅供您个人使用,绝不会发送到任何地方。在您查看和编辑的文件上存储元数据在 libferris 中称为 remembrance(记忆)。目前仅记住通过 libferris 调用的查看和编辑操作。列表 13 显示了我如何设置 Xine 以作为 Annodex 媒体文件的默认查看操作执行。

列表 13. 设置 Xine 播放 Annodex 文件

$ cat xine.desktop
[Desktop Entry]
Name=xine
Comment=Video Player
Exec=xine
MimeType=video/mpeg;...
Icon=~/icons/xine.png
Terminal=0
Type=Application
$ ferris-import-desktop-file xine.desktop
$ ferris-set-file-action-for-type -v -a xine \
     /tmp/Wombats.anx

# Lets view the video.
$ alias fv="ferris-file-action -v"
$ fv /tmp/Wombats.anx

现在我们可以探索 libferris 对我们过去的操作的了解。默认情况下,记住的操作按操作类型,然后按媒体类型分组。树中最终目录的推荐 EA 是文件名和上次查看或编辑的时间。列表 14 中显示的此历史记录虚拟文件系统仅显示一组最近的操作,以避免变得太大。

列表 14. 显示最近的查看操作

$ fls remembrance://
history
$ fls remembrance://history
edit  view
$ fls remembrance://history/view
video
$ fls -0h remembrance://history/view/video
/tmp/Wombats.anx 05 Dec  6 21:34

对于每个文件,您还可以调出完整的查看和编辑时间列表。这使用了 libferris 所谓的 branch filesystem(分支文件系统)。分支文件系统最好被描述为附加到文件的整个个人文件系统。分支文件系统使用 branches:// 处理程序访问;所有其他 URL 处理程序都显示为 branches:// 的直接子项。

在列表 15 中,我查看了我的媒体文件有哪些分支,并探索了 remembrance 查看文件系统。然后,出于好奇,我查看了 extents 分支,并看到内核的 XFS 文件系统已将整个媒体文件放在磁盘上的单个连续区中。

要查看文件是否具有有效的数字签名,您只需读取文件上的 has-valid-signature EA 即可。signatures 分支文件系统允许公开有关签名的更多详细信息。branchfs-attributes 文件系统将文件的所有 EA 公开为文件系统。有时,将 EA 作为文件访问更方便。

列表 15. 分支文件系统:关于文件的文件系统

$ fls branches://file/tmp/Wombats.anx
branchfs-attributes  branchfs-medallions
branchfs-remembrance branchfs-extents
branchfs-parents     branchfs-signatures
$ fls -0 branches://file/tmp/\
  Wombats.anx/branchfs-remembrance/view
10.7M -rw-rw---- 05 Dec  6 21:34 ... 05 Dec  6 21:35
10.7M -rw-rw---- 05 Dec  6 21:34 ... 05 Dec  6 21:39
...
$ fls --xml \
  branches://file/tmp/Wombats.anx/branchfs-extents
<ferrisls>
<ferrisls
   url="branches://.../branchfs-extents"
   name="branchfs-extents"  >
 <context  name="0"
   start-block="14245376"
   end-block="14267375"
   start-address="0"
   end-address="21999"  />
</ferrisls>
</ferrisls>

未来方向

未来,libferris 将继续支持挂载更多内容并在可能的情况下获取更多元数据。计划为 FUSE 模块补充当前 Samba 支持。

本文的资源: /article/8947

Ben Martin 从事文件系统工作已超过十年。他目前正在攻读博士学位,将语义文件系统与形式概念分析相结合,以改善人机文件系统交互。

加载 Disqus 评论