131 } |
140 } |
132 close $fh; |
141 close $fh; |
133 return 0; |
142 return 0; |
134 } |
143 } |
135 |
144 |
|
145 sub release { |
|
146 my $path = $IDX . shift; |
|
147 return 0 if not exists $IMAGE{$path}; |
|
148 $debug->("Currently we have " . keys(%DIRTY) . " dirty blocks\n"); |
|
149 return 0; |
|
150 } |
|
151 |
136 sub readbuffer { |
152 sub readbuffer { |
137 my $path = $IDX . shift; |
153 my $path = $IDX . shift; |
138 my ($size, $offset) = @_; |
154 my ($size, $offset) = @_; |
139 my $finfo = $IMAGE{$path} or die "File $path is not opened!"; |
155 my $finfo = $IMAGE{$path} or die "File $path is not opened!"; |
140 return "" if $offset >= $finfo->{meta}{devsize}; |
156 return "" if $offset >= $finfo->{meta}{devsize}; |
147 return $buffer; |
163 return $buffer; |
148 } |
164 } |
149 |
165 |
150 sub _readblock { |
166 sub _readblock { |
151 my ($finfo, $size, $offset) = @_; |
167 my ($finfo, $size, $offset) = @_; |
152 |
168 my ($block, $blockoffset, $length); |
153 my $block = int($offset / $finfo->{meta}{blocksize}); |
169 |
154 my $blockoffset = $offset % $finfo->{meta}{blocksize}; |
170 $debug->("<<< block offset:$offset size:$size\n"); |
155 |
171 $debug->( " block @{[int($offset/BS)]} + @{[$offset % BS]}\n"); |
156 my $length = $finfo->{meta}{blocksize} - $blockoffset; |
172 |
157 $length = $size if $size <= $length; |
173 # first check if it's an dirty block |
158 |
174 $block = int($offset / BS); |
159 if (exists $DIRTY{ $finfo . $block }) { |
175 if (exists $DIRTY{ $finfo . $block }) { |
|
176 $blockoffset = $offset % BS; |
|
177 $length = min(BS - $blockoffset, $size); |
|
178 |
|
179 $debug->("+++ dirty offset:$block*@{[BS]} + $blockoffset size:$length\n"); |
160 return substr $DIRTY{ $finfo . $block }, $blockoffset, $length; |
180 return substr $DIRTY{ $finfo . $block }, $blockoffset, $length; |
161 } |
181 } |
162 |
182 |
|
183 |
|
184 # if not dirty, we've to find it on disk |
|
185 |
|
186 $block = int($offset / $finfo->{meta}{blocksize}); |
|
187 $blockoffset = $offset % $finfo->{meta}{blocksize}; |
|
188 $length = min($finfo->{meta}{blocksize} - $blockoffset, $size); |
|
189 |
|
190 # find the max length we can satisfy w/o colliding |
|
191 # with dirty blocks |
|
192 for (my $l = BS; $l < $length; $l += BS) { |
|
193 my $b = int(($offset + $l)/BS); |
|
194 if ($DIRTY{$finfo . $b}) { |
|
195 $length = $l; |
|
196 last; |
|
197 } |
|
198 } |
|
199 |
|
200 $debug->("=== $length\n"); |
|
201 $debug->("+++ disk offset:$block*$finfo->{meta}{blocksize} + $blockoffset size:$length\n"); |
|
202 |
163 my $fn = "$DATA/" . $finfo->{blocklist}{$block}; |
203 my $fn = "$DATA/" . $finfo->{blocklist}{$block}; |
164 if (-e $fn) { |
204 |
165 open(my $fh => $fn); |
205 state %cache; |
166 binmode($fh); |
206 if (not defined $cache{fn} |
167 seek($fh => $blockoffset, 0) or die "seek: $!"; |
207 or ($cache{fn} ne $fn)) { |
168 local $/ = \$length; |
208 |
169 return scalar <$fh>; |
209 if (-e $fn) { |
170 } |
210 open(my $fh => $fn); |
171 elsif (-e "$fn.gz") { |
211 binmode($fh); |
172 open(my $fh => "$fn.gz"); |
212 local $/ = undef; |
173 binmode($fh); |
213 $cache{data} = <$fh>; |
174 my $buffer; |
214 } |
175 gunzip($fh => \$buffer) |
215 elsif (-e "$fn.gz") { |
176 or die $GunzipError; |
216 open(my $fh => "$fn.gz"); |
177 close($fh); |
217 binmode($fh); |
178 return substr($buffer, $blockoffset, $size); |
218 gunzip($fh => \$cache{data}) |
179 } |
219 or die $GunzipError; |
180 |
220 } |
181 die "$fn: $!\n"; |
221 $cache{fn} = $fn; |
|
222 } |
|
223 |
|
224 return substr($cache{data}, $blockoffset, $size); |
|
225 die "$fn: $!\n"; |
|
226 |
182 } |
227 } |
183 |
228 |
184 sub writebuffer { |
229 sub writebuffer { |
185 my $path = $IDX . shift; |
230 my $path = $IDX . shift; |
186 my ($buffer, $offset) = @_; |
231 my ($buffer, $offset) = @_; |
199 return $size; |
244 return $size; |
200 } |
245 } |
201 |
246 |
202 sub _writeblock { |
247 sub _writeblock { |
203 my ($finfo, $buffer, $offset) = @_; |
248 my ($finfo, $buffer, $offset) = @_; |
|
249 my ($block, $blockoffset, $length); |
204 my $size = length($buffer); |
250 my $size = length($buffer); |
205 |
251 |
206 my $block = int($offset / $finfo->{meta}{blocksize}); |
252 $block = int($offset / BS); |
207 my $blockoffset = $offset % $finfo->{meta}{blocksize}; |
253 $blockoffset = $offset % BS; |
|
254 $length = min(BS - $blockoffset, $size); |
|
255 |
|
256 $debug->(">>> offset:$offset size:$length of $size\n"); |
|
257 $debug->(" block @{[int($offset/BS)]} + @{[$offset % BS]}\n"); |
208 |
258 |
209 if (not exists $DIRTY{ $finfo . $block }) { |
259 if (not exists $DIRTY{ $finfo . $block }) { |
|
260 $debug->("+++ missing $block+$blockoffset\n"); |
210 $DIRTY{ $finfo . $block } = _readblock( |
261 $DIRTY{ $finfo . $block } = _readblock( |
211 $finfo, |
262 $finfo, BS, $block * BS); |
212 $finfo->{meta}{blocksize}, |
263 } |
213 $block * $finfo->{meta}{blocksize} |
|
214 ); |
|
215 } |
|
216 |
|
217 my $length = $finfo->{meta}{blocksize} - $blockoffset; |
|
218 $length = $size if $size < $length; |
|
219 |
264 |
220 substr($DIRTY{ $finfo . $block }, $blockoffset, $length) = |
265 substr($DIRTY{ $finfo . $block }, $blockoffset, $length) = |
221 substr($buffer, 0, $length); |
266 substr($buffer, 0, $length); |
222 |
267 |
223 return $length; |
268 return $length; |