--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/fuse Tue Jul 12 00:21:58 2011 +0200
@@ -0,0 +1,157 @@
+#! /usr/bin/perl
+
+use 5.010;
+use strict;
+use warnings;
+use autodie qw(:all);
+
+use Fuse;
+
+my $src = shift // die "need source directory\n";
+my $mp = shift // die "need mountpoint\n";
+
+$fs::DATA = "$src/data";
+$fs::IDX = "$src/idx";
+
+Fuse::main(mountpoint => $mp,
+ debug => 0,
+ getattr => "fs::getattr",
+ getdir => "fs::getdir",
+ open => "fs::openfile",
+ read => "fs::readbuffer",
+ write => "fs::writebuffer",
+ );
+
+
+{ package fs;
+ use strict;
+ use warnings;
+ use POSIX qw(:errno_h);
+ use autodie qw(:all);
+
+ our ($ROOT, $DATA, $IDX);
+ my %FILE;
+ my %CACHE;
+
+ sub getattr {
+ my $path = $IDX . shift;
+ return stat $path if $path eq "$IDX/";
+ # rest are the idx
+ my @attr = stat $path or return -(ENOENT);
+ my %meta = _get_meta($path);
+ $attr[7] = $meta{devsize};
+ $attr[9] = $meta{timestamp};
+ $attr[2] &= ~0222; # r/o
+ return @attr;
+ }
+
+ sub getdir {
+ my $path = $IDX . shift;
+ opendir(my $dh, $path) or return 0;
+ return (readdir($dh), 0);
+ }
+
+
+ sub openfile {
+ my $path = $IDX . shift;
+ return 0 if exists $FILE{$path};
+ $FILE{$path}{meta} = { _get_meta($path) };
+ $FILE{$path}{blocklist} = {};
+
+ open(my $fh => $path);
+ { # the file header
+ local $/ = "";
+ scalar <$fh>;
+ }
+ while (<$fh>) {
+ /^#/ and last;
+ my ($block, $cs, $file) = split;
+ $block-- if not $FILE{$path}{meta}{format};
+ $FILE{$path}{blocklist}{$block} = $file;
+ }
+ return 0;
+ }
+
+ sub readbuffer {
+ my $path = $IDX . shift;
+ my ($size, $offset) = @_;
+ my $finfo = $FILE{$path} or die "File $path is not opened!";
+ return "" if $offset >= $finfo->{meta}{devsize};
+
+ my $buffer = "";
+ for (my $need = $size; $need; $need = $size - length($buffer)) {
+ $buffer .= _readblock($finfo, $need, $offset + length($buffer));
+ }
+
+ return $buffer;
+ }
+
+ sub _readblock {
+ my ($finfo, $size, $offset) = @_;
+
+ my $block = int($offset / $finfo->{meta}{blocksize});
+ my $blockoffset = $offset % $finfo->{meta}{blocksize};
+
+ my $length = $finfo->{meta}{blocksize} - $blockoffset;
+ $length = $size if $size <= $length;
+
+ if (exists $CACHE{$finfo}{$block}) {
+ return substr $CACHE{$finfo}{$block}, $blockoffset, $length;
+ }
+
+ open(my $fh => "$DATA/" . $finfo->{blocklist}{$block});
+ seek($fh => $blockoffset, 0) or die "seek: $!";
+ local $/ = \$length;
+ return scalar <$fh>;
+ }
+
+ sub writebuffer {
+ my $path = $IDX . shift;
+ my ($buffer, $offset) = @_;
+ my $size = length($buffer);
+ my $finfo = $FILE{$path} or die "File $path is not opened!";
+
+ my $written = 0;
+ while ($written < $size) {
+ my $n = _writeblock($finfo, substr($buffer, $written), $offset + $written);
+ return $written if not $n;
+ $written += $n;
+ }
+ return $size;
+ }
+
+ sub _writeblock {
+ my ($finfo, $buffer, $offset) = @_;
+ my $size = length($buffer);
+
+ my $block = int($offset / $finfo->{meta}{blocksize});
+ my $blockoffset = $offset % $finfo->{meta}{blocksize};
+
+ if (not exists $CACHE{$finfo}{$block}) {
+ open(my $fh => "$DATA/" . $finfo->{blocklist}{$block});
+ local $/ = undef;
+ $CACHE{$finfo}{$block} = <$fh>;
+ close($fh);
+ }
+
+ my $length = $finfo->{meta}{blocksize} - $blockoffset;
+ $length = $size if $size <= $length;
+
+ substr($CACHE{$finfo}{$block}, $blockoffset, $length)
+ = substr($buffer, 0, $length);
+
+ return $length;
+ }
+
+ sub _get_meta {
+ my $path = shift;
+ my %meta;
+ open(my $fh => $path);
+ while(<$fh>) {
+ last if /^$/;
+ /^(?<k>\S+):\s+(?<v>.*?)\s*$/ and do { $meta{$+{k}} = $+{v}; next; };
+ }
+ return %meta;
+ }
+
+}