#fuse #file #hpc #shared #systems #cli

应用 ldpfuse

与LDP_FUSE一起使用的CLI实用程序。有关更多信息,请参阅https://github.com/sholtrop/ldpfuse。

1个不稳定版本

0.1.0 2022年7月27日

#1043文件系统

MIT 许可证

5KB
58

简介

LDP_FUSE (LD_PRELOAD 用户空间文件系统) 是一个仅包含头文件的C库,用于编写文件系统,利用了 LD_PRELOAD 小技巧。简要来说,你使用LDP_FUSE提供的API编写一个共享库(.so文件),然后运行一个带有你的共享库 LD_PRELOAD 的二进制文件。LDP_FUSE将为你处理几个底层细节(见文档)。

重要提示:此项目主要是一个概念证明。所有当前的限制使其不适合在生产环境中运行。

安装

include/文件夹下的所有文件包含到您的构建系统中。

为了更轻松地管理LDP_FUSE文件系统,您可以可选地安装一个基于Rust的CLI工具

cargo install ldpfuse

用法

示例

如何编写LDP_FUSE文件系统的示例

#include <stdio.h>
#include "ldpfuse.h"

ssize_t my_read(int fd, void *buf, size_t count, off_t offset,
                    orig_pread_t pread_fn) {
  printf("Read called!\n");
  return pread_fn(fd, buf, count, offset);
}

LDPRELOAD_FUSE_MAIN {
  struct ldp_fuse_funcs funcs = {.read = my_read };
  ldp_fuse_init(&funcs);
}

使用gcc将其编译为共享对象(.so)文件。您必须动态链接libdl,例如通过指定-ldl

使用CLI

如果您已安装CLI,现在可以像这样运行您的文件系统

ldpfuse -m <mount_path> -s <so_file_path> -- myprogram

其中 <mount_path> 是您的文件系统挂载的路径。LDP_FUSE会忽略没有此挂载路径作为祖先的路径上的文件系统操作,并将其传递给原始函数。 <so_file_path> 是您的LDP_FUSE文件系统共享对象。路径可以是相对的。

完整示例,读取目录内容

ldpfuse -m /tmp/test -s ./my_fs.so -- ls -la /tmp/test

环境变量

与其使用CLI来管理环境变量并运行程序,您也可以自己设置它们

  • LD_PRELOAD - 必须是您的LDP_FUSE文件系统共享库的绝对路径。
  • LDP_FUSE_PATH - 必须设置为您的文件系统'挂载'下的绝对路径。例如 /tmp/ldpfuse。如果没有设置,任何路径都将被视为在文件系统中。

文档

函数、宏和结构

LDPRELOAD_FUSE_MAIN

在此函数宏的作用域内定义任何设置代码。这个宏应在任何LDP_FUSE文件系统中调用,并包含对ldp_fuse_init的调用。

LDPRELOAD_FUSE_MAIN {
  // Any one-time setup code...
  ldp_fuse_init(&funcs);
}

ldp_fuse_init

void ldp_fuse_init(ldp_fuse_funcs* funcs)

初始化文件系统并将其函数设置为funcs参数中描述的函数。这应在LDPRELOAD_FUSE_MAIN的某个点调用。

ldp_fuse_funcs

一个结构体,用于定义LDP_FUSE的操作。每个成员都是一个指向函数的指针,该函数将替换常规文件系统I/O函数。请参阅覆盖函数概述,以查看可以覆盖的原始函数。

覆盖函数概述

以下是使用LDP_FUSE可以覆盖的文件系统函数概述。类似的系统调用被重定向到单个用户函数——最通用的一个。例如,statlstatfstatfstatat都被重定向到fstatat。因此,您只需编写一个fstatat实现。

原始函数 LDP_FUSE函数 注意
access access
faccessat access
euidaccess access
mkdir mkdir
close close
close_range close 标志参数被忽略。将在范围内的每个fd上调用close一次。
opendir opendir
creat open
open open
openat openat
truncate truncate
chmod chmod
chown chown
symlink symlink
rename rename
link link
rmdir rmdir
unlink unlink
mknod mknod
readlink readlink
write write
pwrite write
stat stat
lstat stat
fstat stat
fstatat stat
read read
pread read
getxattr getxattr
lgetxattr getxattr 将遵循符号链接,与默认的询问链接本身相反。
fgetxattr getxattr

条件编译

您可以指定以下标志之一

  • -D LDP_FUSE_DEBUG - 将调试输出记录到stderr。
  • -D LDP_FUSE_THREAD_SAFE - 使LDP_FUSE的内部数据结构线程安全。如果LDP_FUSE文件系统将用于运行执行多线程文件I/O的程序,则应包含此标志。如果包含,则必须动态链接pthreads
  • -D LDP_FUSE_OFT_SIZE <size> - 设置LDP_FUSE的打开文件描述符表的大小(请参阅打开文件描述符表)。默认为200。

打开文件描述符表(OFT)

LDP_FUSE有其自己的打开文件描述符表,用于跟踪读取偏移量,确定路径是否在文件系统中,并在必要时进行读写锁定。库的用户不需要直接与此交互。条目的最大数量在编译时确定(LDP_FUSE_OFT_SIZE),当超出时,应用程序将带错误信息退出。

陷阱

在您的自定义文件系统函数中,请勿使用原始函数。例如,在my_read中,不要调用read。这样做将导致无限循环,因为LDP_FUSE将read重定向到my_read。相反,使用最后一个参数,它是指向原始pread函数的函数指针。

如果使用LDP_FUSE的程序是多线程的,请包含LDP_FUSE_THREAD_SAFE标志(请参阅条件编译

已知问题与限制

LDP_FUSE可能与某些二进制文件不兼容。以下列举了一些可能的原因,但并不全面。

Mmap

内存映射文件I/O不能通过LD_PRELOAD进行拦截。因此,任何使用mmap来访问文件的程序都无法使用LDP_FUSE。

内联系统调用,静态链接glibc

任何使用内联系统调用(例如直接使用syscall)或静态链接glibc的程序都不能使用LDP_FUSE。

Setuid二进制文件

Linux以安全执行模式执行setuid二进制文件。在此模式下,出于安全考虑,LD_PRELOAD被忽略。因此,您无法使用LDP_FUSE与setuid二进制文件一起使用。

没有glibc包装器的函数

某些文件系统I/O函数没有glibc包装器(例如openat2)。因此,这些函数调用无法被拦截。

没有后端常规文件系统的LDP_FUSE文件系统

LDP_FUSE为每个进程维护一个OFDT。目前还没有ipc或r/w机制可以避免两个进程同时访问文件。这意味着您仍然需要一个常规文件系统作为后端,以便它可以处理这个问题。这个问题可能在将来通过在共享内存中实现单个OFDT来缓解。

鸣谢

此库包含likle的出色cwalk库,用于路径解析。

许可协议

MIT

依赖项

~4.5MB
~95K SLoC