我并不是Fuse模块或FUSE系统的常规用户,只是出于好奇尝试解决这个问题。因此,虽然我无法详细解释如何使用普通的Fuse模块来实现您的目标,但我有一个可以创建所需文件系统的工作代码(至少在我的系统上,并且看起来它能够创建任意的文件系统树),我可以解释一下我是如何让这段代码工作的。
首先,我在CPAN上发现了
Fuse::Simple模块。它的SYNOPSIS显示它为Fuse模块提供了一个非常简单的API,用于从哈希结构创建任意文件系统。它的
源代码并不大,所以我创建了一个'listing.pl'脚本文件,并将其中大部分函数复制到那里(除了导致
Modification of a read-only value
异常的fserr),将主要子内容放出来,使它们成为主脚本的流程,硬编码文件系统结构(
$fs
变量),进行了一些微小的调整(例如使用
my
声明变量以防止异常),最终得到了挂载文件系统、列出所有目录和可读取文件的代码。因此,这是我最后得到的代码:
use strict;
use warnings;
use diagnostics;
use Carp;
use Fuse;
use Errno qw(:POSIX);
use Fcntl qw(:DEFAULT :mode);
use Switch;
my $debug = 0;
my %codecache = ();
my $ctime = time();
my $uid = $>;
my $gid = $) + 0;
my $fs = {
"file1.txt" => "File 1 contents",
"subdir" => {
"subdir-l2" => {},
"file2.txt" => "File 2 contents"
}
};
my %args = (
"mountpoint" => "listing",
"debug" => $debug,
"fuse_debug" => 0,
"threaded" => 0,
"/" => $fs
);
my %fs_subs = (
"chmod" => \&fs_not_imp,
"chown" => \&fs_not_imp,
"flush" => \&fs_flush,
"fsync" => \&fs_not_imp,
"getattr" => \&fs_getattr,
"getdir" => \&fs_getdir,
"getxattr" => \&fs_not_imp,
"link" => \&fs_not_imp,
"listxattr" => \&fs_not_imp,
"mkdir" => \&fs_not_imp,
"mknod" => \&fs_not_imp,
"open" => \&fs_open,
"read" => \&fs_read,
"readlink" => \&fs_readlink,
"release" => \&fs_release,
"removexattr" => \&fs_not_imp,
"rmdir" => \&fs_not_imp,
"rename" => \&fs_not_imp,
"setxattr" => \&fs_not_imp,
"statfs" => \&fs_statfs,
"symlink" => \&fs_not_imp,
"truncate" => \&fs_truncate,
"unlink" => \&fs_not_imp,
"utime" => sub{return 0},
"write" => \&fs_write,
);
$debug = delete $args{"debug"};
$args{"debug"} = delete( $args{"fuse_debug"} ) || 0;
delete $args{"/"};
for my $name (keys %fs_subs) {
my $sub = $fs_subs{$name};
$args{$name} ||= $sub;
}
Fuse::main(%args);
sub fetch {
my ($path, @args) = @_;
my $obj = $fs;
for my $elem (split '/', $path) {
next if $elem eq "";
$obj = runcode($obj);
return ENOTDIR() unless ref($obj) eq "HASH";
return ENOENT() unless exists $obj->{$elem};
$obj = $obj->{$elem};
}
return runcode($obj, @args);
}
sub runcode {
my ($obj, @args) = @_;
while (ref($obj) eq "CODE") {
my $old = $obj;
if (@args) {
delete $codecache{$old};
print "running $obj(",quoted(@args),") NO CACHE\n" if $debug;
$obj = saferun($obj, @args);
} elsif (exists $codecache{$obj}) {
print "got cached $obj\n" if $debug;
$obj = $codecache{$obj};
} else {
print "running $obj() to cache\n" if $debug;
$obj = $codecache{$old} = saferun($obj);
}
if (ref($obj) eq "NOCACHE") {
print "returned a nocache() value - flushing\n" if $debug;
delete $codecache{$old};
$obj = $$obj;
}
print "returning ",ref($obj)," ",
defined($obj) ? $obj : "undef",
"\n" if $debug;
}
return $obj;
}
sub saferun {
my ($sub, @args) = @_;
my $ret = eval { &$sub(@args) };
my $died = $@;
if (ref($died)) {
print "+++ Error $$died\n" if ref($died) eq "ERROR";
return $died;
} elsif ($died) {
print "+++ $died\n";
return ESTALE();
}
return $ret;
}
sub nocache {
return bless(\ shift, "NOCACHE");
}
sub dump_open_flags {
my $flags = shift;
printf " flags: 0%o = (", $flags;
for my $bits (
[ O_ACCMODE(), O_RDONLY(), "O_RDONLY" ],
[ O_ACCMODE(), O_WRONLY(), "O_WRONLY" ],
[ O_ACCMODE(), O_RDWR(), "O_RDWR" ],
[ O_APPEND(), O_APPEND(), "|O_APPEND" ],
[ O_NONBLOCK(), O_NONBLOCK(), "|O_NONBLOCK" ],
[ O_SYNC(), O_SYNC(), "|O_SYNC" ],
[ O_DIRECT(), O_DIRECT(), "|O_DIRECT" ],
[ O_LARGEFILE(), O_LARGEFILE(), "|O_LARGEFILE" ],
[ O_NOFOLLOW(), O_NOFOLLOW(), "|O_NOFOLLOW" ],
) {
my ($mask, $flag, $name) = @$bits;
if (($flags & $mask) == $flag) {
$flags -= $flag;
print $name;
}
}
printf "| 0%o !!!", $flags if $flags;
print ")\n";
}
sub accessor {
my $var_ref = shift;
croak "accessor() requires a reference to a scalar var\n"
unless defined($var_ref) && ref($var_ref) eq "SCALAR";
return sub {
my $new = shift;
$$var_ref = $new if defined($new);
return $$var_ref;
}
}
sub fs_not_imp { return -ENOSYS() }
sub fs_flush {
print "Flushing\n" if $debug;
%codecache = ();
return 0;
}
sub easy_getattr {
my ($mode, $size) = @_;
return (
0, 0,
$mode,
1,
$uid, $gid,
0,
$size,
$ctime, $ctime, $ctime,
1024, 1,
);
}
sub fs_getattr {
my $path = shift;
my $obj = fetch($path);
return easy_getattr(S_IFREG | 0200, 0) unless defined($obj);
switch (ref($obj)) {
case "ERROR" {
return -$$obj;
}
case "" {
return easy_getattr(S_IFREG | 0644, length($obj));
}
case "HASH" {
return easy_getattr(S_IFDIR | 0755, 1);
}
case "SCALAR" {
return easy_getattr(S_IFLNK | 0777, 1);
}
else {
print "+++ What on earth is ",ref($obj)," $path ?\n";
return easy_getattr(S_IFREG | 0000, 0);
}
}
}
sub fs_getdir {
my $obj = fetch(shift);
return -$$obj if ref($obj) eq "ERROR";
return -ENOENT() unless ref($obj) eq "HASH";
return (".", "..", sort(keys %$obj), 0);
}
sub fs_open {
my $obj = fetch(shift);
my $flags = shift;
dump_open_flags($flags) if $debug;
return -EBADF() unless defined($obj) or ($flags & O_ACCMODE());
switch (ref($obj)) {
case "ERROR" { return -$$obj; }
case "" { return 0 }
case "HASH" { return -EISDIR(); }
else { return -ENOSYS(); }
}
}
sub fs_read {
my $obj = fetch(shift);
my $size = shift;
my $off = shift;
return -ENOENT() unless defined($obj);
return -$$obj if ref($obj) eq "ERROR";
return -ENOENT() if ref($obj);
if ($off > length($obj)) {
return -EINVAL();
} elsif ($off == length($obj)) {
return 0;
}
return substr($obj, $off, $size);
}
sub fs_readlink {
my $obj = fetch(shift);
return -$$obj if ref($obj) eq "ERROR";
return -EINVAL() unless ref($obj) eq "SCALAR";
return $$obj;
}
sub fs_release {
my ($path, $flags) = @_;
dump_open_flags($flags) if $debug;
return 0;
}
sub fs_statfs {
return (
255,
1,1,
1,1,
2,
);
}
sub fs_truncate {
my $obj = fetch(shift, "");
return -$$obj if ref($obj) eq "ERROR";
return 0;
}
sub fs_write {
my ($path, $buf, $off) = @_;
my $obj = fetch($path, $buf, $off);
return -$$obj if ref($obj) eq "ERROR";
return length($buf);
}
最后的话:我没有尝试使用这个模块本身(它没有列在我的发行版软件包存储库中,而且我太懒了(抱歉)不想通过cpanm或其他方式安装它)。但是我认为,如果我只需要使用Perl与FUSE,我可能会使用Fuse::Simple而不是Fuse,也许会分叉它。我想我只会在我的学术研究中使用普通的Fuse...
希望这有所帮助。