1 package DBM::Deep::Storage::File;
6 use warnings FATAL => 'all';
8 use Fcntl qw( :DEFAULT :flock :seek );
10 use constant DEBUG => 0;
12 use base 'DBM::Deep::Storage';
16 DBM::Deep::Storage::File
20 This is an internal-use-only object for L<DBM::Deep/>. It mediates the low-level
21 interaction with the storage mechanism.
23 Currently, the only storage mechanism supported is the file system.
27 This class provides an abstraction to the storage mechanism so that the Engine
28 (the only class that uses this class) doesn't have to worry about that.
49 #XXX Migrate this to the engine, where it really belongs.
50 filter_store_key => undef,
51 filter_store_value => undef,
52 filter_fetch_key => undef,
53 filter_fetch_value => undef,
56 # Grab the parameters we want to use
57 foreach my $param ( keys %$self ) {
58 next unless exists $args->{$param};
59 $self->{$param} = $args->{$param};
62 if ( $self->{fh} && !$self->{file_offset} ) {
63 $self->{file_offset} = tell( $self->{fh} );
66 $self->open unless $self->{fh};
73 This method opens the filehandle for the filename in C< file >.
75 There is no return value.
82 # Adding O_BINARY should remove the need for the binmode below. However,
83 # I'm not going to remove it because I don't have the Win32 chops to be
84 # absolutely certain everything will be ok.
85 my $flags = O_CREAT | O_BINARY;
87 if ( !-e $self->{file} || -w _ ) {
95 sysopen( $fh, $self->{file}, $flags )
96 or die "DBM::Deep: Cannot sysopen file '$self->{file}': $!\n";
99 # Even though we use O_BINARY, better be safe than sorry.
102 if ($self->{autoflush}) {
103 my $old = select $fh;
113 If the filehandle is opened, this will close it.
115 There is no return value.
132 This will return the size of the DB. If file_offset is set, this will take that into account.
139 return 0 unless $self->{fh};
140 return( (-s $self->{fh}) - $self->{file_offset} );
145 This will set the inode value of the underlying file object.
147 This is only needed to handle some obscure Win32 bugs. It reqlly shouldn't be
148 needed outside this object.
150 There is no return value.
157 unless ( defined $self->{inode} ) {
158 my @stats = stat($self->{fh});
159 $self->{inode} = $stats[1];
160 $self->{end} = $stats[7];
166 =head2 print_at( $offset, @data )
168 This takes an optional offset and some data to print.
170 C< $offset >, if defined, will be used to seek into the file. If file_offset is
171 set, it will be used as the zero location. If it is undefined, no seeking will
172 occur. Then, C< @data > will be printed to the current location.
174 There is no return value.
184 my $fh = $self->{fh};
185 if ( defined $loc ) {
186 seek( $fh, $loc + $self->{file_offset}, SEEK_SET );
190 my $caller = join ':', (caller)[0,2];
191 my $len = length( join '', @_ );
192 warn "($caller) print_at( " . (defined $loc ? $loc : '<undef>') . ", $len )\n";
195 print( $fh @_ ) or die "Internal Error (print_at($loc)): $!\n";
200 =head2 read_at( $offset, $length )
202 This takes an optional offset and a length.
204 C< $offset >, if defined, will be used to seek into the file. If file_offset is
205 set, it will be used as the zero location. If it is undefined, no seeking will
206 occur. Then, C< $length > bytes will be read from the current location.
208 The data read will be returned.
214 my ($loc, $size) = @_;
218 my $fh = $self->{fh};
219 if ( defined $loc ) {
220 seek( $fh, $loc + $self->{file_offset}, SEEK_SET );
224 my $caller = join ':', (caller)[0,2];
225 warn "($caller) read_at( " . (defined $loc ? $loc : '<undef>') . ", $size )\n";
229 read( $fh, $buffer, $size);
236 When the ::Storage::File object goes out of scope, it will be closed.
249 =head2 request_space( $size )
251 This takes a size and adds that much space to the DBM.
253 This returns the offset for the new location.
261 #XXX Do I need to reset $self->{end} here? I need a testcase
262 my $loc = $self->{end};
263 $self->{end} += $size;
268 =head2 copy_stats( $target_filename )
270 This will take the stats for the current filehandle and apply them to
271 C< $target_filename >. The stats copied are:
275 =item * Onwer UID and GID
285 my ($temp_filename) = @_;
287 my @stats = stat( $self->{fh} );
288 my $perms = $stats[2] & 07777;
291 chown( $uid, $gid, $temp_filename );
292 chmod( $perms, $temp_filename );
298 # Flush the filehandle
299 my $old_fh = select $self->{fh};
300 my $old_af = $|; $| = 1; $| = $old_af;
309 my $fh = $self->{fh};
310 return unless defined $fh;
311 return unless defined fileno $fh;
312 local $\ = ''; # just in case
313 no warnings; # temporarily disable warnings
314 local $^W; # temporarily disable warnings
321 return $self->_lock( $obj, LOCK_EX );
327 return $self->_lock( $obj, LOCK_SH );
332 my ($obj, $type) = @_;
334 $type = LOCK_EX unless defined $type;
336 #XXX This is a temporary fix for Win32 and autovivification. It
337 # needs to improve somehow. -RobK, 2008-03-09
338 if ( $^O eq 'MSWin32' || $^O eq 'cygwin' ) {
342 if (!defined($self->{fh})) { return; }
344 #XXX This either needs to allow for upgrading a shared lock to an
345 # exclusive lock or something else with autovivification.
347 if ($self->{locking}) {
348 if (!$self->{locked}) {
349 flock($self->{fh}, $type);
351 # refresh end counter in case file has changed size
352 my @stats = stat($self->{fh});
353 $self->{end} = $stats[7];
355 # double-check file inode, in case another process
356 # has optimize()d our file while we were waiting.
357 if (defined($self->{inode}) && $stats[1] != $self->{inode}) {
362 $obj->{engine}->setup( $obj );
364 flock($self->{fh}, $type); # re-lock
366 # This may not be necessary after re-opening
367 $self->{end} = (stat($self->{fh}))[7]; # re-end
381 if (!defined($self->{fh})) { return; }
383 if ($self->{locking} && $self->{locked} > 0) {
386 if (!$self->{locked}) {
387 flock($self->{fh}, LOCK_UN);