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.
79 # TODO: What happens if we ->open when we already have a $fh?
83 # Adding O_BINARY should remove the need for the binmode below. However,
84 # I'm not going to remove it because I don't have the Win32 chops to be
85 # absolutely certain everything will be ok.
86 my $flags = O_CREAT | O_BINARY;
88 if ( !-e $self->{file} || -w _ ) {
96 sysopen( $fh, $self->{file}, $flags )
97 or die "DBM::Deep: Cannot sysopen file '$self->{file}': $!\n";
100 # Even though we use O_BINARY, better be safe than sorry.
103 if ($self->{autoflush}) {
104 my $old = select $fh;
114 If the filehandle is opened, this will close it.
116 There is no return value.
133 This will return the size of the DB. If file_offset is set, this will take that into account.
135 B<NOTE>: This function isn't used internally anywhere.
142 return 0 unless $self->{fh};
143 return( (-s $self->{fh}) - $self->{file_offset} );
148 This will set the inode value of the underlying file object.
150 This is only needed to handle some obscure Win32 bugs. It reqlly shouldn't be
151 needed outside this object.
153 There is no return value.
160 unless ( defined $self->{inode} ) {
161 my @stats = stat($self->{fh});
162 $self->{inode} = $stats[1];
163 $self->{end} = $stats[7];
169 =head2 print_at( $offset, @data )
171 This takes an optional offset and some data to print.
173 C< $offset >, if defined, will be used to seek into the file. If file_offset is
174 set, it will be used as the zero location. If it is undefined, no seeking will
175 occur. Then, C< @data > will be printed to the current location.
177 There is no return value.
187 my $fh = $self->{fh};
188 if ( defined $loc ) {
189 seek( $fh, $loc + $self->{file_offset}, SEEK_SET );
193 my $caller = join ':', (caller)[0,2];
194 my $len = length( join '', @_ );
195 warn "($caller) print_at( " . (defined $loc ? $loc : '<undef>') . ", $len )\n";
198 print( $fh @_ ) or die "Internal Error (print_at($loc)): $!\n";
203 =head2 read_at( $offset, $length )
205 This takes an optional offset and a length.
207 C< $offset >, if defined, will be used to seek into the file. If file_offset is
208 set, it will be used as the zero location. If it is undefined, no seeking will
209 occur. Then, C< $length > bytes will be read from the current location.
211 The data read will be returned.
217 my ($loc, $size) = @_;
219 my $fh = $self->{fh};
220 if ( defined $loc ) {
221 seek( $fh, $loc + $self->{file_offset}, SEEK_SET );
225 my $caller = join ':', (caller)[0,2];
226 warn "($caller) read_at( " . (defined $loc ? $loc : '<undef>') . ", $size )\n";
230 read( $fh, $buffer, $size);
237 When the ::Storage::File object goes out of scope, it will be closed.
250 =head2 request_space( $size )
252 This takes a size and adds that much space to the DBM.
254 This returns the offset for the new location.
262 #XXX Do I need to reset $self->{end} here? I need a testcase
263 my $loc = $self->{end};
264 $self->{end} += $size;
269 =head2 copy_stats( $target_filename )
271 This will take the stats for the current filehandle and apply them to
272 C< $target_filename >. The stats copied are:
276 =item * Onwer UID and GID
286 my ($temp_filename) = @_;
288 my @stats = stat( $self->{fh} );
289 my $perms = $stats[2] & 07777;
292 chown( $uid, $gid, $temp_filename );
293 chmod( $perms, $temp_filename );
299 # Flush the filehandle
300 my $old_fh = select $self->{fh};
301 my $old_af = $|; $| = 1; $| = $old_af;
310 my $fh = $self->{fh};
311 return unless defined $fh;
312 return unless defined fileno $fh;
313 local $\ = ''; # just in case
314 no warnings; # temporarily disable warnings
315 local $^W; # temporarily disable warnings
322 return $self->_lock( $obj, LOCK_EX );
328 return $self->_lock( $obj, LOCK_SH );
333 my ($obj, $type) = @_;
335 $type = LOCK_EX unless defined $type;
337 #XXX This is a temporary fix for Win32 and autovivification. It
338 # needs to improve somehow. -RobK, 2008-03-09
339 if ( $^O eq 'MSWin32' || $^O eq 'cygwin' ) {
343 if (!defined($self->{fh})) { return; }
345 #XXX This either needs to allow for upgrading a shared lock to an
346 # exclusive lock or something else with autovivification.
348 if ($self->{locking}) {
349 if (!$self->{locked}) {
350 flock($self->{fh}, $type);
352 # refresh end counter in case file has changed size
353 my @stats = stat($self->{fh});
354 $self->{end} = $stats[7];
356 # double-check file inode, in case another process
357 # has optimize()d our file while we were waiting.
358 if (defined($self->{inode}) && $stats[1] != $self->{inode}) {
363 $obj->{engine}->setup( $obj );
365 flock($self->{fh}, $type); # re-lock
367 # This may not be necessary after re-opening
368 $self->{end} = (stat($self->{fh}))[7]; # re-end
382 if (!defined($self->{fh})) { return; }
384 if ($self->{locking} && $self->{locked} > 0) {
387 if (!$self->{locked}) {
388 flock($self->{fh}, LOCK_UN);