The header now has its own sector. A lot needs to be moved over to it, but it's there.
[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
00d9bd0b 90sub size {
91 my $self = shift;
92
93 return 0 unless $self->{fh};
94 return -s $self->{fh};
95}
96
7dcefff3 97sub set_inode {
98 my $self = shift;
99
2120a181 100 unless ( defined $self->{inode} ) {
7dcefff3 101 my @stats = stat($self->{fh});
102 $self->{inode} = $stats[1];
103 $self->{end} = $stats[7];
104 }
105
106 return 1;
107}
108
019404df 109sub print_at {
110 my $self = shift;
111 my $loc = shift;
112
113 local ($/,$\);
114
115 my $fh = $self->{fh};
7dcefff3 116 if ( defined $loc ) {
117 seek( $fh, $loc + $self->{file_offset}, SEEK_SET );
118 }
119
695c88b1 120 if ( DEBUG ) {
121 my $caller = join ':', (caller)[0,2];
122 my $len = length( join '', @_ );
123 warn "($caller) print_at( " . (defined $loc ? $loc : '<undef>') . ", $len )\n";
124 }
125
45f047f8 126 print( $fh @_ ) or die "Internal Error (print_at($loc)): $!\n";
019404df 127
128 return 1;
129}
130
7dcefff3 131sub read_at {
132 my $self = shift;
133 my ($loc, $size) = @_;
134
135 local ($/,$\);
136
137 my $fh = $self->{fh};
138 if ( defined $loc ) {
139 seek( $fh, $loc + $self->{file_offset}, SEEK_SET );
140 }
141
695c88b1 142 if ( DEBUG ) {
143 my $caller = join ':', (caller)[0,2];
144 warn "($caller) read_at( " . (defined $loc ? $loc : '<undef>') . ", $size )\n";
145 }
146
7dcefff3 147 my $buffer;
148 read( $fh, $buffer, $size);
149
150 return $buffer;
151}
152
460b1067 153sub DESTROY {
154 my $self = shift;
155 return unless $self;
156
157 $self->close;
158
159 return;
160}
161
019404df 162sub request_space {
163 my $self = shift;
164 my ($size) = @_;
165
7dcefff3 166 #XXX Do I need to reset $self->{end} here? I need a testcase
019404df 167 my $loc = $self->{end};
168 $self->{end} += $size;
169
170 return $loc;
171}
172
15ba72cc 173##
174# If db locking is set, flock() the db file. If called multiple
175# times before unlock(), then the same number of unlocks() must
176# be called before the lock is released.
177##
5c0756fc 178sub lock_exclusive {
179 my $self = shift;
180 my ($obj) = @_;
181 return $self->lock( $obj, LOCK_EX );
182}
183
184sub lock_shared {
185 my $self = shift;
186 my ($obj) = @_;
187 return $self->lock( $obj, LOCK_SH );
188}
189
15ba72cc 190sub lock {
191 my $self = shift;
192 my ($obj, $type) = @_;
42717e46 193
15ba72cc 194 $type = LOCK_EX unless defined $type;
195
45f047f8 196 #XXX This is a temporary fix for Win32 and autovivification. It
197 # needs to improve somehow. -RobK, 2008-03-09
198 if ( $^O eq 'MSWin32' || $^O eq 'cygwin' ) {
199 $type = LOCK_EX;
200 }
201
15ba72cc 202 if (!defined($self->{fh})) { return; }
203
45f047f8 204 #XXX This either needs to allow for upgrading a shared lock to an
205 # exclusive lock or something else with autovivification.
206 # -RobK, 2008-03-09
15ba72cc 207 if ($self->{locking}) {
208 if (!$self->{locked}) {
209 flock($self->{fh}, $type);
210
211 # refresh end counter in case file has changed size
212 my @stats = stat($self->{fh});
213 $self->{end} = $stats[7];
214
215 # double-check file inode, in case another process
216 # has optimize()d our file while we were waiting.
2120a181 217 if (defined($self->{inode}) && $stats[1] != $self->{inode}) {
15ba72cc 218 $self->close;
219 $self->open;
220
221 #XXX This needs work
222 $obj->{engine}->setup_fh( $obj );
223
224 flock($self->{fh}, $type); # re-lock
225
226 # This may not be necessary after re-opening
227 $self->{end} = (stat($self->{fh}))[7]; # re-end
228 }
229 }
230 $self->{locked}++;
231
232 return 1;
233 }
234
235 return;
236}
237
238##
239# If db locking is set, unlock the db file. See note in lock()
240# regarding calling lock() multiple times.
241##
242sub unlock {
243 my $self = shift;
244
245 if (!defined($self->{fh})) { return; }
246
247 if ($self->{locking} && $self->{locked} > 0) {
248 $self->{locked}--;
15ba72cc 249
a8d2331c 250 if (!$self->{locked}) {
251 flock($self->{fh}, LOCK_UN);
252 return 1;
253 }
254
255 return;
15ba72cc 256 }
257
258 return;
259}
260
2120a181 261sub flush {
25c7c8d6 262 my $self = shift;
263
2120a181 264 # Flush the filehandle
265 my $old_fh = select $self->{fh};
266 my $old_af = $|; $| = 1; $| = $old_af;
267 select $old_fh;
25c7c8d6 268
269 return 1;
270}
28394a1a 271
08164b50 272# Taken from http://www.perlmonks.org/?node_id=691054
6e6789b0 273sub is_writable {
274 my $self = shift;
08164b50 275
276 my $fh = $self->{fh};
277 return unless defined $fh;
278 return unless defined fileno $fh;
279 local $\ = ''; # just in case
280 no warnings; # temporarily disable warnings
281 local $^W; # temporarily disable warnings
282 return print $fh '';
6e6789b0 283}
284
285sub copy_stats {
286 my $self = shift;
287 my ($temp_filename) = @_;
288
289 my @stats = stat( $self->{fh} );
290 my $perms = $stats[2] & 07777;
291 my $uid = $stats[4];
292 my $gid = $stats[5];
293 chown( $uid, $gid, $temp_filename );
294 chmod( $perms, $temp_filename );
295}
296
460b1067 2971;
298__END__