diff -r 000000000000 -r e92e765779e7 fuse --- /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 /^$/; + /^(?\S+):\s+(?.*?)\s*$/ and do { $meta{$+{k}} = $+{v}; next; }; + } + return %meta; + } + +}