9 use POSIX qw(ENOENT EISDIR EINVAL ENOSYS O_RDWR);
17 our $VERSION = '0.01';
21 Fuse::DBI - mount your database as filesystem and use it
26 Fuse::DBI->mount( ... );
28 See L<run> below for examples how to set parametars.
32 This module will use L<Fuse> module, part of C<FUSE (Filesystem in USErspace)>
33 available at L<http://sourceforge.net/projects/avf> to mount
34 your database as file system.
36 That will give you posibility to use normal file-system tools (cat, grep, vi)
37 to manipulate data in database.
39 It's actually opposite of Oracle's intention to put everything into database.
48 Mount your database as filesystem.
50 my $mnt = Fuse::DBI->mount({
51 filenames => 'select name from filenamefilenames,
53 update => 'sql update',
54 dsn => 'DBI:Pg:dbname=webgui',
55 user => 'database_user',
56 password => 'database_password'
76 carp "mount needs 'dsn' to connect to (e.g. dsn => 'DBI:Pg:dbname=test')" unless ($arg->{'dsn'});
77 carp "mount needs 'mount' as mountpoint" unless ($arg->{'mount'});
79 foreach (qw(filenames read update)) {
80 carp "mount needs '$_' SQL" unless ($arg->{$_});
83 $dbh = DBI->connect($arg->{'dsn'},$arg->{'user'},$arg->{'password'}, { AutoCommit => 0 }) || die $DBI::errstr;
85 print "start transaction\n";
86 #$dbh->begin_work || die $dbh->errstr;
88 $sth->{filenames} = $dbh->prepare($arg->{'filenames'}) || die $dbh->errstr();
90 $sth->{'read'} = $dbh->prepare($arg->{'read'}) || die $dbh->errstr();
91 $sth->{'update'} = $dbh->prepare($arg->{'update'}) || die $dbh->errstr();
93 $ctime_start = time();
97 $self->{'proc'} = Proc::Simple->new();
98 $self->{'proc'}->kill_on_destroy(1);
100 $self->{'proc'}->start( sub {
102 mountpoint=>$arg->{'mount'},
103 getattr=>\&e_getattr,
110 truncate=>\&e_truncate,
115 $self ? return $self : return undef;
120 Unmount your database as filesystem.
124 This will also kill background process which is translating
125 database to filesystem.
132 confess "no process running?" unless ($self->{'proc'});
133 $self->{'proc'}->kill;
143 # create empty filesystem
150 # cont => "File 'a'.\n",
152 # ctime => time()-2000
156 # fetch new filename list from database
157 $sth->{'filenames'}->execute() || die $sth->{'filenames'}->errstr();
159 # read them in with sesible defaults
160 while (my $row = $sth->{'filenames'}->fetchrow_hashref() ) {
161 $files{$row->{'filename'}} = {
162 size => $row->{'size'},
163 mode => $row->{'writable'} ? 0644 : 0444,
164 id => $row->{'id'} || 99,
168 foreach (split(m!/!, $row->{'filename'})) {
169 # first, entry is assumed to be file
190 print "found ",scalar(keys %files)-scalar(keys %dirs)," files, ",scalar(keys %dirs), " dirs\n";
197 $file = '.' unless length($file);
202 my ($file) = filename_fixup(shift);
204 $file = '.' unless length($file);
205 return -ENOENT() unless exists($files{$file});
206 my ($size) = $files{$file}{size} || 1;
207 my ($dev, $ino, $rdev, $blocks, $gid, $uid, $nlink, $blksize) = (0,0,0,1,0,0,1,1024);
208 my ($atime, $ctime, $mtime);
209 $atime = $ctime = $mtime = $files{$file}{ctime} || $ctime_start;
211 my ($modes) = (($files{$file}{type} || 0100)<<9) + $files{$file}{mode};
213 # 2 possible types of return values:
214 #return -ENOENT(); # or any other error you care to
215 #print(join(",",($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)),"\n");
216 return ($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks);
220 my ($dirname) = shift;
222 # return as many text filenames as you like, followed by the retval.
223 print((scalar keys %files)." files total\n");
225 foreach (keys %files) {
227 $f =~ s/^\E$dirname\Q//;
230 $out{$f}++ if (/^\E$dirname\Q/ && $f =~ /^[^\/]+$/);
232 $out{$f}++ if ($f =~ /^[^\/]+$/);
236 $out{'no files? bug?'}++;
238 print scalar keys %out," files in dir '$dirname'\n";
239 return (keys %out),0;
243 # VFS sanity check; it keeps all the necessary state, not much to do here.
244 my $file = filename_fixup(shift);
247 return -ENOENT() unless exists($files{$file});
248 return -EISDIR() unless exists($files{$file}{id});
250 if (!exists($files{$file}{cont})) {
251 $sth->{'read'}->execute($files{$file}{id}) || die $sth->{'read'}->errstr;
252 $files{$file}{cont} = $sth->{'read'}->fetchrow_array;
253 print "file '$file' content read in cache\n";
255 print "open '$file' ",length($files{$file}{cont})," bytes\n";
260 # return an error numeric, or binary/text string.
261 # (note: 0 means EOF, "0" will give a byte (ascii "0")
262 # to the reading program)
263 my ($file) = filename_fixup(shift);
264 my ($buf_len,$off) = @_;
266 return -ENOENT() unless exists($files{$file});
268 my $len = length($files{$file}{cont});
270 print "read '$file' [$len bytes] offset $off length $buf_len\n";
272 return -EINVAL() if ($off > $len);
273 return 0 if ($off == $len);
275 $buf_len = $buf_len-$off if ($off+$buf_len > $len);
277 return substr($files{$file}{cont},$off,$buf_len);
281 print "transaction rollback\n";
282 $dbh->rollback || die $dbh->errstr;
283 print "invalidate all cached content\n";
284 foreach my $f (keys %files) {
285 delete $files{$f}{cont};
287 print "begin new transaction\n";
288 $dbh->begin_work || die $dbh->errstr;
293 my $file = shift || die;
295 $files{$file}{ctime} = time();
297 if (!$sth->{'update'}->execute($files{$file}{cont},$files{$file}{id})) {
298 print "update problem: ",$sth->{'update'}->errstr;
302 if (! $dbh->commit) {
303 print "ERROR: commit problem: ",$sth->{'update'}->errstr;
307 print "updated '$file' [",$files{$file}{id},"]\n";
313 my $file = filename_fixup(shift);
314 my ($buf_len,$off) = @_;
316 return -ENOENT() unless exists($files{$file});
318 my $len = length($files{$file}{cont});
320 print "write '$file' [$len bytes] offset $off length\n";
322 $files{$file}{cont} =
323 substr($files{$file}{cont},0,$off) .
325 substr($files{$file}{cont},$off+length($buf_len));
327 if (! update_db($file)) {
330 return length($buf_len);
335 my $file = filename_fixup(shift);
338 $files{$file}{cont} = substr($files{$file}{cont},0,$size);
344 my ($atime,$mtime,$file) = @_;
345 $file = filename_fixup($file);
347 return -ENOENT() unless exists($files{$file});
349 print "utime '$file' $atime $mtime\n";
351 $files{$file}{time} = $mtime;
355 sub e_statfs { return 255, 1, 1, 1, 1, 2 }
366 C<FUSE (Filesystem in USErspace)> website
367 L<http://sourceforge.net/projects/avf>
371 Dobrica Pavlinusic, E<lt>dpavlin@rot13.orgE<gt>
373 =head1 COPYRIGHT AND LICENSE
375 Copyright (C) 2004 by Dobrica Pavlinusic
377 This library is free software; you can redistribute it and/or modify
378 it under the same terms as Perl itself, either Perl version 5.8.4 or,
379 at your option, any later version of Perl 5 you may have available.