9 use POSIX qw(ENOENT EISDIR EINVAL ENOSYS O_RDWR);
13 our $VERSION = '0.01';
17 Fuse::DBI - mount your database as filesystem and use it
22 Fuse::DBI->run( ... );
24 See L<run> below for examples how to set parametars.
28 This module will use L<Fuse> module, part of C<FUSE (Filesystem in USErspace)>
29 available at L<http://sourceforge.net/projects/avf> to mount
30 your database as file system.
32 That will give you posibility to use normal file-system tools (cat, grep, vi)
33 to manipulate data in database.
35 It's actually opposite of Oracle's intention to put everything into database.
44 Mount your database as filesystem.
47 filenames => 'select name from filenamefilenames,
49 update => 'sql update',
50 dsn => 'DBI:Pg:dbname=webgui',
51 user => 'database_user',
52 password => 'database_password'
66 carp "run needs 'dsn' to connect to (e.g. dsn => 'DBI:Pg:dbname=test')" unless ($arg->{'dsn'});
67 carp "run needs 'mount' as mountpoint" unless ($arg->{'mount'});
69 foreach (qw(filenames read update)) {
70 carp "run needs '$_' SQL" unless ($arg->{$_});
73 $dbh = DBI->connect($arg->{'dsn'},$arg->{'user'},$arg->{'password'}, { AutoCommit => 0 }) || die $DBI::errstr;
75 print "start transaction\n";
76 #$dbh->begin_work || die $dbh->errstr;
78 $sth->{filenames} = $dbh->prepare($arg->{'filenames'}) || die $dbh->errstr();
80 $sth->{'read'} = $dbh->prepare($arg->{'read'}) || die $dbh->errstr();
81 $sth->{'update'} = $dbh->prepare($arg->{'update'}) || die $dbh->errstr();
83 $ctime_start = time();
88 mountpoint=>$arg->{'mount'},
96 truncate=>\&e_truncate,
105 # create empty filesystem
112 # cont => "File 'a'.\n",
114 # ctime => time()-2000
118 # fetch new filename list from database
119 $sth->{'filenames'}->execute() || die $sth->{'filenames'}->errstr();
121 # read them in with sesible defaults
122 while (my $row = $sth->{'filenames'}->fetchrow_hashref() ) {
123 $files{$row->{'filename'}} = {
124 size => $row->{'size'},
125 mode => $row->{'writable'} ? 0644 : 0444,
126 id => $row->{'id'} || 99,
130 foreach (split(m!/!, $row->{'filename'})) {
131 # first, entry is assumed to be file
152 print "found ",scalar(keys %files)-scalar(keys %dirs)," files, ",scalar(keys %dirs), " dirs\n";
159 $file = '.' unless length($file);
164 my ($file) = filename_fixup(shift);
166 $file = '.' unless length($file);
167 return -ENOENT() unless exists($files{$file});
168 my ($size) = $files{$file}{size} || 1;
169 my ($dev, $ino, $rdev, $blocks, $gid, $uid, $nlink, $blksize) = (0,0,0,1,0,0,1,1024);
170 my ($atime, $ctime, $mtime);
171 $atime = $ctime = $mtime = $files{$file}{ctime} || $ctime_start;
173 my ($modes) = (($files{$file}{type} || 0100)<<9) + $files{$file}{mode};
175 # 2 possible types of return values:
176 #return -ENOENT(); # or any other error you care to
177 #print(join(",",($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)),"\n");
178 return ($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks);
182 my ($dirname) = shift;
184 # return as many text filenames as you like, followed by the retval.
185 print((scalar keys %files)." files total\n");
187 foreach (keys %files) {
189 $f =~ s/^\E$dirname\Q//;
192 $out{$f}++ if (/^\E$dirname\Q/ && $f =~ /^[^\/]+$/);
194 $out{$f}++ if ($f =~ /^[^\/]+$/);
198 $out{'no files? bug?'}++;
200 print scalar keys %out," files in dir '$dirname'\n";
201 return (keys %out),0;
205 # VFS sanity check; it keeps all the necessary state, not much to do here.
206 my $file = filename_fixup(shift);
209 return -ENOENT() unless exists($files{$file});
210 return -EISDIR() unless exists($files{$file}{id});
212 if (!exists($files{$file}{cont})) {
213 $sth->{'read'}->execute($files{$file}{id}) || die $sth->{'read'}->errstr;
214 $files{$file}{cont} = $sth->{'read'}->fetchrow_array;
215 print "file '$file' content read in cache\n";
217 print "open '$file' ",length($files{$file}{cont})," bytes\n";
222 # return an error numeric, or binary/text string.
223 # (note: 0 means EOF, "0" will give a byte (ascii "0")
224 # to the reading program)
225 my ($file) = filename_fixup(shift);
226 my ($buf_len,$off) = @_;
228 return -ENOENT() unless exists($files{$file});
230 my $len = length($files{$file}{cont});
232 print "read '$file' [$len bytes] offset $off length $buf_len\n";
234 return -EINVAL() if ($off > $len);
235 return 0 if ($off == $len);
237 $buf_len = $buf_len-$off if ($off+$buf_len > $len);
239 return substr($files{$file}{cont},$off,$buf_len);
243 print "transaction rollback\n";
244 $dbh->rollback || die $dbh->errstr;
245 print "invalidate all cached content\n";
246 foreach my $f (keys %files) {
247 delete $files{$f}{cont};
249 print "begin new transaction\n";
250 $dbh->begin_work || die $dbh->errstr;
255 my $file = shift || die;
257 $files{$file}{ctime} = time();
259 if (!$sth->{'update'}->execute($files{$file}{cont},$files{$file}{id})) {
260 print "update problem: ",$sth->{'update'}->errstr;
264 if (! $dbh->commit) {
265 print "ERROR: commit problem: ",$sth->{'update'}->errstr;
269 print "updated '$file' [",$files{$file}{id},"]\n";
275 my $file = filename_fixup(shift);
276 my ($buf_len,$off) = @_;
278 return -ENOENT() unless exists($files{$file});
280 my $len = length($files{$file}{cont});
282 print "write '$file' [$len bytes] offset $off length\n";
284 $files{$file}{cont} =
285 substr($files{$file}{cont},0,$off) .
287 substr($files{$file}{cont},$off+length($buf_len));
289 if (! update_db($file)) {
292 return length($buf_len);
297 my $file = filename_fixup(shift);
300 $files{$file}{cont} = substr($files{$file}{cont},0,$size);
306 my ($atime,$mtime,$file) = @_;
307 $file = filename_fixup($file);
309 return -ENOENT() unless exists($files{$file});
311 print "utime '$file' $atime $mtime\n";
313 $files{$file}{time} = $mtime;
317 sub e_statfs { return 255, 1, 1, 1, 1, 2 }
328 C<FUSE (Filesystem in USErspace)> website
329 L<http://sourceforge.net/projects/avf>
333 Dobrica Pavlinusic, E<lt>dpavlin@rot13.orgE<gt>
335 =head1 COPYRIGHT AND LICENSE
337 Copyright (C) 2004 by Dobrica Pavlinusic
339 This library is free software; you can redistribute it and/or modify
340 it under the same terms as Perl itself, either Perl version 5.8.4 or,
341 at your option, any later version of Perl 5 you may have available.