All sectors now use a string to create themselves
[dbsrgits/DBM-Deep.git] / lib / DBM / Deep / File.pm
CommitLineData
460b1067 1package DBM::Deep::File;
2
2120a181 3use 5.006_000;
460b1067 4
5use strict;
065b45be 6use warnings FATAL => 'all';
460b1067 7
86867f3a 8use Fcntl qw( :DEFAULT :flock :seek );
460b1067 9
695c88b1 10use constant DEBUG => 0;
11
460b1067 12sub new {
13 my $class = shift;
14 my ($args) = @_;
15
16 my $self = bless {
359a01ac 17 autobless => 1,
2120a181 18 autoflush => 1,
460b1067 19 end => 0,
20 fh => undef,
21 file => undef,
22 file_offset => 0,
2120a181 23 locking => 1,
460b1067 24 locked => 0,
2120a181 25#XXX Migrate this to the engine, where it really belongs.
460b1067 26 filter_store_key => undef,
27 filter_store_value => undef,
28 filter_fetch_key => undef,
29 filter_fetch_value => undef,
30 }, $class;
31
32 # Grab the parameters we want to use
33 foreach my $param ( keys %$self ) {
34 next unless exists $args->{$param};
35 $self->{$param} = $args->{$param};
36 }
37
38 if ( $self->{fh} && !$self->{file_offset} ) {
39 $self->{file_offset} = tell( $self->{fh} );
40 }
41
42 $self->open unless $self->{fh};
43
44 return $self;
45}
46
47sub open {
48 my $self = shift;
49
633df1fd 50 # Adding O_BINARY should remove the need for the binmode below. However,
460b1067 51 # I'm not going to remove it because I don't have the Win32 chops to be
52 # absolutely certain everything will be ok.
e9b0b5f0 53 my $flags = O_CREAT | O_BINARY;
54
55 if ( !-e $self->{file} || -w _ ) {
56 $flags |= O_RDWR;
57 }
58 else {
59 $flags |= O_RDONLY;
60 }
460b1067 61
62 my $fh;
63 sysopen( $fh, $self->{file}, $flags )
64 or die "DBM::Deep: Cannot sysopen file '$self->{file}': $!\n";
65 $self->{fh} = $fh;
66
67 # Even though we use O_BINARY, better be safe than sorry.
68 binmode $fh;
69
70 if ($self->{autoflush}) {
71 my $old = select $fh;
72 $|=1;
73 select $old;
74 }
75
76 return 1;
77}
78
79sub close {
80 my $self = shift;
81
82 if ( $self->{fh} ) {
83 close $self->{fh};
84 $self->{fh} = undef;
85 }
86
87 return 1;
88}
89
7dcefff3 90sub set_inode {
91 my $self = shift;
92
2120a181 93 unless ( defined $self->{inode} ) {
7dcefff3 94 my @stats = stat($self->{fh});
95 $self->{inode} = $stats[1];
96 $self->{end} = $stats[7];
97 }
98
99 return 1;
100}
101
019404df 102sub print_at {
103 my $self = shift;
104 my $loc = shift;
105
106 local ($/,$\);
107
108 my $fh = $self->{fh};
7dcefff3 109 if ( defined $loc ) {
110 seek( $fh, $loc + $self->{file_offset}, SEEK_SET );
111 }
112
695c88b1 113 if ( DEBUG ) {
114 my $caller = join ':', (caller)[0,2];
115 my $len = length( join '', @_ );
116 warn "($caller) print_at( " . (defined $loc ? $loc : '<undef>') . ", $len )\n";
117 }
118
45f047f8 119 print( $fh @_ ) or die "Internal Error (print_at($loc)): $!\n";
019404df 120
121 return 1;
122}
123
7dcefff3 124sub read_at {
125 my $self = shift;
126 my ($loc, $size) = @_;
127
128 local ($/,$\);
129
130 my $fh = $self->{fh};
131 if ( defined $loc ) {
132 seek( $fh, $loc + $self->{file_offset}, SEEK_SET );
133 }
134
695c88b1 135 if ( DEBUG ) {
136 my $caller = join ':', (caller)[0,2];
137 warn "($caller) read_at( " . (defined $loc ? $loc : '<undef>') . ", $size )\n";
138 }
139
7dcefff3 140 my $buffer;
141 read( $fh, $buffer, $size);
142
143 return $buffer;
144}
145
460b1067 146sub DESTROY {
147 my $self = shift;
148 return unless $self;
149
150 $self->close;
151
152 return;
153}
154
019404df 155sub request_space {
156 my $self = shift;
157 my ($size) = @_;
158
7dcefff3 159 #XXX Do I need to reset $self->{end} here? I need a testcase
019404df 160 my $loc = $self->{end};
161 $self->{end} += $size;
162
163 return $loc;
164}
165
15ba72cc 166##
167# If db locking is set, flock() the db file. If called multiple
168# times before unlock(), then the same number of unlocks() must
169# be called before the lock is released.
170##
171sub lock {
172 my $self = shift;
173 my ($obj, $type) = @_;
42717e46 174
15ba72cc 175 $type = LOCK_EX unless defined $type;
176
45f047f8 177 #XXX This is a temporary fix for Win32 and autovivification. It
178 # needs to improve somehow. -RobK, 2008-03-09
179 if ( $^O eq 'MSWin32' || $^O eq 'cygwin' ) {
180 $type = LOCK_EX;
181 }
182
15ba72cc 183 if (!defined($self->{fh})) { return; }
184
45f047f8 185 #XXX This either needs to allow for upgrading a shared lock to an
186 # exclusive lock or something else with autovivification.
187 # -RobK, 2008-03-09
15ba72cc 188 if ($self->{locking}) {
189 if (!$self->{locked}) {
190 flock($self->{fh}, $type);
191
192 # refresh end counter in case file has changed size
193 my @stats = stat($self->{fh});
194 $self->{end} = $stats[7];
195
196 # double-check file inode, in case another process
197 # has optimize()d our file while we were waiting.
2120a181 198 if (defined($self->{inode}) && $stats[1] != $self->{inode}) {
15ba72cc 199 $self->close;
200 $self->open;
201
202 #XXX This needs work
203 $obj->{engine}->setup_fh( $obj );
204
205 flock($self->{fh}, $type); # re-lock
206
207 # This may not be necessary after re-opening
208 $self->{end} = (stat($self->{fh}))[7]; # re-end
209 }
210 }
211 $self->{locked}++;
212
213 return 1;
214 }
215
216 return;
217}
218
219##
220# If db locking is set, unlock the db file. See note in lock()
221# regarding calling lock() multiple times.
222##
223sub unlock {
224 my $self = shift;
225
226 if (!defined($self->{fh})) { return; }
227
228 if ($self->{locking} && $self->{locked} > 0) {
229 $self->{locked}--;
230 if (!$self->{locked}) { flock($self->{fh}, LOCK_UN); }
231
232 return 1;
233 }
234
235 return;
236}
237
2120a181 238sub flush {
25c7c8d6 239 my $self = shift;
240
2120a181 241 # Flush the filehandle
242 my $old_fh = select $self->{fh};
243 my $old_af = $|; $| = 1; $| = $old_af;
244 select $old_fh;
25c7c8d6 245
246 return 1;
247}
28394a1a 248
08164b50 249# Taken from http://www.perlmonks.org/?node_id=691054
6e6789b0 250sub is_writable {
251 my $self = shift;
08164b50 252
253 my $fh = $self->{fh};
254 return unless defined $fh;
255 return unless defined fileno $fh;
256 local $\ = ''; # just in case
257 no warnings; # temporarily disable warnings
258 local $^W; # temporarily disable warnings
259 return print $fh '';
6e6789b0 260}
261
262sub copy_stats {
263 my $self = shift;
264 my ($temp_filename) = @_;
265
266 my @stats = stat( $self->{fh} );
267 my $perms = $stats[2] & 07777;
268 my $uid = $stats[4];
269 my $gid = $stats[5];
270 chown( $uid, $gid, $temp_filename );
271 chmod( $perms, $temp_filename );
272}
273
460b1067 2741;
275__END__