30 |
30 |
31 |
31 |
32 { package fs; |
32 { package fs; |
33 use strict; |
33 use strict; |
34 use warnings; |
34 use warnings; |
35 use POSIX qw(:errno_h); |
35 use POSIX qw(:errno_h); |
|
36 use IO::Uncompress::Gunzip qw(gunzip $GunzipError); |
36 use autodie qw(:all); |
37 use autodie qw(:all); |
37 |
38 |
38 our ($ROOT, $DATA, $IDX); |
39 our ($ROOT, $DATA, $IDX); |
39 my %FILE; |
40 my %FILE; |
40 my %CACHE; |
41 my %CACHE; |
41 |
42 |
42 sub getattr { |
43 sub getattr { |
43 my $path = $IDX . shift; |
44 my $path = $IDX . shift; |
44 return stat $path if -d $path; |
45 return stat $path if -d $path; |
45 # rest are the idx |
46 # rest are the idx |
46 my @attr = stat $path or return -(ENOENT); |
47 my @attr = stat $path or return -(ENOENT); |
47 my %meta = _get_meta($path); |
48 my %meta = _get_meta($path); |
48 use Data::Dumper; |
49 $attr[7] = $meta{devsize}; |
49 warn Dumper \%meta; |
50 $attr[9] = $meta{timestamp}; |
50 $attr[7] = $meta{devsize}; |
51 $attr[2] &= ~0222; # r/o |
51 $attr[9] = $meta{timestamp}; |
52 return @attr; |
52 $attr[2] &= ~0222; # r/o |
|
53 warn Dumper \@attr; |
|
54 return @attr; |
|
55 } |
53 } |
56 |
54 |
57 sub getdir { |
55 sub getdir { |
58 my $path = $IDX . shift; |
56 my $path = $IDX . shift; |
59 opendir(my $dh, $path) or return 0; |
57 opendir(my $dh, $path) or return 0; |
60 return (readdir($dh), 0); |
58 return (readdir($dh), 0); |
61 } |
59 } |
62 |
|
63 |
60 |
64 sub openfile { |
61 sub openfile { |
65 my $path = $IDX . shift; |
62 my $path = $IDX . shift; |
66 return 0 if exists $FILE{$path}; |
63 return 0 if exists $FILE{$path}; |
67 $FILE{$path}{meta} = { _get_meta($path) }; |
64 $FILE{$path}{meta} = { _get_meta($path) }; |
76 /^#/ and last; |
73 /^#/ and last; |
77 my ($block, $cs, $file) = split; |
74 my ($block, $cs, $file) = split; |
78 $block-- if not $FILE{$path}{meta}{format}; |
75 $block-- if not $FILE{$path}{meta}{format}; |
79 $FILE{$path}{blocklist}{$block} = $file; |
76 $FILE{$path}{blocklist}{$block} = $file; |
80 } |
77 } |
|
78 close $fh; |
81 return 0; |
79 return 0; |
82 } |
80 } |
83 |
81 |
84 sub readbuffer { |
82 sub readbuffer { |
85 my $path = $IDX . shift; |
83 my $path = $IDX . shift; |
86 my ($size, $offset) = @_; |
84 my ($size, $offset) = @_; |
87 my $finfo = $FILE{$path} or die "File $path is not opened!"; |
85 my $finfo = $FILE{$path} or die "File $path is not opened!"; |
88 return "" if $offset >= $finfo->{meta}{devsize}; |
86 return "" if $offset >= $finfo->{meta}{devsize}; |
89 |
87 |
90 my $buffer = ""; |
88 my $buffer = ""; |
91 for (my $need = $size; $need; $need = $size - length($buffer)) { |
89 for (my $need = $size; $need > 0; $need = $size - length($buffer)) { |
92 $buffer .= _readblock($finfo, $need, $offset + length($buffer)); |
90 $buffer .= _readblock($finfo, $need, $offset + length($buffer)); |
93 } |
91 } |
94 |
92 |
95 return $buffer; |
93 return $buffer; |
96 } |
94 } |
106 |
104 |
107 if (exists $CACHE{$finfo}{$block}) { |
105 if (exists $CACHE{$finfo}{$block}) { |
108 return substr $CACHE{$finfo}{$block}, $blockoffset, $length; |
106 return substr $CACHE{$finfo}{$block}, $blockoffset, $length; |
109 } |
107 } |
110 |
108 |
111 open(my $fh => "$DATA/" . $finfo->{blocklist}{$block}); |
109 my $fn = "$DATA/" . $finfo->{blocklist}{$block}; |
112 seek($fh => $blockoffset, 0) or die "seek: $!"; |
110 if (-e $fn) { |
113 local $/ = \$length; |
111 open(my $fh => $fn); |
114 return scalar <$fh>; |
112 binmode($fh); |
|
113 seek($fh => $blockoffset, 0) or die "seek: $!"; |
|
114 local $/ = \$length; |
|
115 return scalar <$fh>; |
|
116 } |
|
117 elsif (-e "$fn.gz") { |
|
118 open(my $fh => "$fn.gz"); |
|
119 binmode($fh); |
|
120 my $buffer; |
|
121 gunzip($fh => \$buffer) |
|
122 or die $GunzipError; |
|
123 close($fh); |
|
124 return substr($buffer, $blockoffset, $size); |
|
125 } |
|
126 |
|
127 die "$fn: $!\n"; |
115 } |
128 } |
116 |
129 |
117 sub writebuffer { |
130 sub writebuffer { |
118 my $path = $IDX . shift; |
131 my $path = $IDX . shift; |
119 my ($buffer, $offset) = @_; |
132 my ($buffer, $offset) = @_; |
120 my $size = length($buffer); |
133 my $size = length($buffer); |
121 my $finfo = $FILE{$path} or die "File $path is not opened!"; |
134 my $finfo = $FILE{$path} or die "File $path is not opened!"; |
122 |
135 |
123 my $written = 0; |
136 for (my $written = 0; $written < $size;) { |
124 while ($written < $size) { |
137 # OPTIMIZE: we should not ask for writing more than the |
125 my $n = _writeblock($finfo, substr($buffer, $written), $offset + $written); |
138 # blocksize |
126 return $written if not $n; |
139 my $n = _writeblock($finfo, substr($buffer, $written), $offset + $written) |
|
140 or return $written; |
127 $written += $n; |
141 $written += $n; |
128 } |
142 } |
129 return $size; |
143 return $size; |
130 } |
144 } |
131 |
145 |
135 |
149 |
136 my $block = int($offset / $finfo->{meta}{blocksize}); |
150 my $block = int($offset / $finfo->{meta}{blocksize}); |
137 my $blockoffset = $offset % $finfo->{meta}{blocksize}; |
151 my $blockoffset = $offset % $finfo->{meta}{blocksize}; |
138 |
152 |
139 if (not exists $CACHE{$finfo}{$block}) { |
153 if (not exists $CACHE{$finfo}{$block}) { |
140 open(my $fh => "$DATA/" . $finfo->{blocklist}{$block}); |
154 #open(my $fh => "$DATA/" . $finfo->{blocklist}{$block}); |
141 local $/ = undef; |
155 #local $/ = undef; |
142 $CACHE{$finfo}{$block} = <$fh>; |
156 #$CACHE{$finfo}{$block} = <$fh>; |
143 close($fh); |
157 #close($fh); |
|
158 $CACHE{$finfo}{$block} = _readblock($finfo, $finfo->{meta}{blocksize}, $block * $finfo->{meta}{blocksize}); |
144 } |
159 } |
145 |
160 |
146 my $length = $finfo->{meta}{blocksize} - $blockoffset; |
161 my $length = $finfo->{meta}{blocksize} - $blockoffset; |
147 $length = $size if $size <= $length; |
162 $length = $size if $size < $length; |
148 |
163 |
149 substr($CACHE{$finfo}{$block}, $blockoffset, $length) |
164 substr($CACHE{$finfo}{$block}, $blockoffset, $length) |
150 = substr($buffer, 0, $length); |
165 = substr($buffer, 0, $length); |
151 |
166 |
152 return $length; |
167 return $length; |