/[fuse_dbi]/fuse-couchdb/DBI.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /fuse-couchdb/DBI.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 36 by dpavlin, Tue Nov 16 15:34:25 2004 UTC revision 62 by dpavlin, Wed Aug 2 21:53:30 2006 UTC
# Line 12  use DBI; Line 12  use DBI;
12  use Carp;  use Carp;
13  use Data::Dumper;  use Data::Dumper;
14    
15    our $VERSION = '0.09_1';
16    
17  our $VERSION = '0.05';  # block size for this filesystem
18    use constant BLOCK => 1024;
19    
20  =head1 NAME  =head1 NAME
21    
# Line 109  running. Implementation is experimental. Line 111  running. Implementation is experimental.
111    
112  =back  =back
113    
114    There is also alternative way which can generate C<read> and C<update>
115    queries on the fly:
116    
117      my $mnt = Fuse::DBI->mount({
118            'filenames' => 'select id,filename,size,writable from files',
119            'read' => sub {
120                    my ($path,$file) = @_;
121                    return( 'select content from files where id = ?', $file->{row}->{id} );
122            },
123            'update' => sub {
124                    my ($path,$file) = @_;
125                    return( 'update files set content = ? where id = ?', $file->{row}->{id} );
126            },
127            'dsn' => 'DBI:Pg:dbname=test_db',
128            'user' => 'database_user',
129            'password' => 'database_password',
130            'invalidate' => sub { ... },
131      });
132    
133  =cut  =cut
134    
135  my $dbh;  my $dbh;
# Line 132  sub mount { Line 153  sub mount {
153    
154          print Dumper($arg);          print Dumper($arg);
155    
156            unless ($self->fuse_module_loaded) {
157                    print STDERR "no fuse module loaded. Trying sudo modprobe fuse!\n";
158                    system "sudo modprobe fuse" || die "can't modprobe fuse using sudo!\n";
159            }
160    
161          carp "mount needs 'dsn' to connect to (e.g. dsn => 'DBI:Pg:dbname=test')" unless ($arg->{'dsn'});          carp "mount needs 'dsn' to connect to (e.g. dsn => 'DBI:Pg:dbname=test')" unless ($arg->{'dsn'});
162          carp "mount needs 'mount' as mountpoint" unless ($arg->{'mount'});          carp "mount needs 'mount' as mountpoint" unless ($arg->{'mount'});
163    
# Line 148  sub mount { Line 174  sub mount {
174    
175          my $pid;          my $pid;
176          if ($arg->{'fork'}) {          if ($arg->{'fork'}) {
                 $self->{'mounted'} = 1;  
177                  $pid = fork();                  $pid = fork();
178                  die "fork() failed: $!" unless defined $pid;                  die "fork() failed: $!" unless defined $pid;
179                  # child will return to caller                  # child will return to caller
180                  if ($pid) {                  if ($pid) {
181                          return $self;                          my $counter = 4;
182                            while ($counter && ! $self->is_mounted) {
183                                    select(undef, undef, undef, 0.5);
184                                    $counter--;
185                            }
186                            if ($self->is_mounted) {
187                                    return $self;
188                            } else {
189                                    return undef;
190                            }
191                  }                  }
192          }          }
193    
# Line 161  sub mount { Line 195  sub mount {
195    
196          $sth->{'filenames'} = $dbh->prepare($arg->{'filenames'}) || die $dbh->errstr();          $sth->{'filenames'} = $dbh->prepare($arg->{'filenames'}) || die $dbh->errstr();
197    
         $sth->{'read'} = $dbh->prepare($arg->{'read'}) || die $dbh->errstr();  
         $sth->{'update'} = $dbh->prepare($arg->{'update'}) || die $dbh->errstr();  
   
198    
199          $self->{'sth'} = $sth;          $self->{'sth'} = $sth;
200    
201          $self->{'read_filenames'} = sub { $self->read_filenames };          $self->{'read_filenames'} = sub { $self->read_filenames };
202          $self->read_filenames;          $self->read_filenames;
203    
204          $self->{'mounted'} = 1 unless ($arg->{'fork'});          foreach my $op (qw/read update/) {
205                    if (ref($arg->{ $op }) ne 'CODE') {
206                            $self->{ $op . '_ref' } = sub {
207                                    my $row = shift;
208                                    return ($arg->{ $op }, $row->{'id'});
209                            }
210                    } else {
211                            $self->{ $op . '_ref' } = $arg->{ $op };
212                    }
213            }
214    
215          $fuse_self = \$self;          $fuse_self = \$self;
216    
# Line 186  sub mount { Line 226  sub mount {
226                  truncate=>\&e_truncate,                  truncate=>\&e_truncate,
227                  unlink=>\&e_unlink,                  unlink=>\&e_unlink,
228                  rmdir=>\&e_unlink,                  rmdir=>\&e_unlink,
229                  debug=>0,                  debug=>1,
230          );          );
231                    
         $self->{'mounted'} = 0;  
   
232          exit(0) if ($arg->{'fork'});          exit(0) if ($arg->{'fork'});
233    
234          return 1;          return 1;
235    
236  };  };
237    
238    =head2 is_mounted
239    
240    Check if fuse filesystem is mounted
241    
242      if ($mnt->is_mounted) { ... }
243    
244    =cut
245    
246    sub is_mounted {
247            my $self = shift;
248    
249            my $mounted = 0;
250            my $mount = $self->{'mount'} || confess "can't find mount point!";
251            if (open(MTAB, "/etc/mtab")) {
252                    while(<MTAB>) {
253                            $mounted = 1 if (/ $mount fuse /i);
254                    }
255                    close(MTAB);
256            } else {
257                    warn "can't open /etc/mtab: $!";
258            }
259    
260            return $mounted;
261    }
262    
263    
264  =head2 umount  =head2 umount
265    
266  Unmount your database as filesystem.  Unmount your database as filesystem.
# Line 211  database to filesystem. Line 275  database to filesystem.
275  sub umount {  sub umount {
276          my $self = shift;          my $self = shift;
277    
278          if ($self->{'mounted'}) {          if ($self->{'mount'} && $self->is_mounted) {
279                  system "fusermount -u ".$self->{'mount'} || warn "umount error: $!" && return 0;                  system "( fusermount -u ".$self->{'mount'}." 2>&1 ) >/dev/null";
280                    if ($self->is_mounted) {
281                            system "sudo umount ".$self->{'mount'} ||
282                            return 0;
283                    }
284                    return 1;
285          }          }
286    
287          return 1;          return 0;
288  }  }
289    
290  $SIG{'INT'} = sub {  $SIG{'INT'} = sub {
291          print STDERR "umount called by SIG INT\n";          if ($fuse_self && $$fuse_self->umount) {
292          umount;                  print STDERR "umount called by SIG INT\n";
293            }
294    };
295    
296    $SIG{'QUIT'} = sub {
297            if ($fuse_self && $$fuse_self->umount) {
298                    print STDERR "umount called by SIG QUIT\n";
299            }
300  };  };
301    
302  sub DESTROY {  sub DESTROY {
303          my $self = shift;          my $self = shift;
304          return if (! $self->{'mounted'});          if ($self->umount) {
305          print STDERR "umount called by DESTROY\n";                  print STDERR "umount called by DESTROY\n";
306          $self->umount;          }
307  }  }
308    
309  =head2 fuse_module_loaded  =head2 fuse_module_loaded
# Line 251  sub fuse_module_loaded { Line 327  sub fuse_module_loaded {
327          }          }
328  }  }
329    
330  my %files;  my $files;
 my %dirs;  
331    
332  sub read_filenames {  sub read_filenames {
333          my $self = shift;          my $self = shift;
# Line 260  sub read_filenames { Line 335  sub read_filenames {
335          my $sth = $self->{'sth'} || die "no sth argument";          my $sth = $self->{'sth'} || die "no sth argument";
336    
337          # create empty filesystem          # create empty filesystem
338          (%files) = (          $files = {
339                  '.' => {                  '.' => {
340                          type => 0040,                          type => 0040,
341                          mode => 0755,                          mode => 0755,
342                  },                  },
343                    '..' => {
344                            type => 0040,
345                            mode => 0755,
346                    },
347          #       a => {          #       a => {
348          #               cont => "File 'a'.\n",          #               cont => "File 'a'.\n",
349          #               type => 0100,          #               type => 0100,
350          #               ctime => time()-2000          #               ctime => time()-2000
351          #       },          #       },
352          );          };
353    
354          # fetch new filename list from database          # fetch new filename list from database
355          $sth->{'filenames'}->execute() || die $sth->{'filenames'}->errstr();          $sth->{'filenames'}->execute() || die $sth->{'filenames'}->errstr();
356    
357          # read them in with sesible defaults          # read them in with sesible defaults
358          while (my $row = $sth->{'filenames'}->fetchrow_hashref() ) {          while (my $row = $sth->{'filenames'}->fetchrow_hashref() ) {
359                  $files{$row->{'filename'}} = {                  $row->{'filename'} ||= 'NULL-'.$row->{'id'};
360                    $files->{$row->{'filename'}} = {
361                          size => $row->{'size'},                          size => $row->{'size'},
362                          mode => $row->{'writable'} ? 0644 : 0444,                          mode => $row->{'writable'} ? 0644 : 0444,
363                          id => $row->{'id'} || 99,                          id => $row->{'id'} || undef,
364                            row => $row,
365                  };                  };
366    
367    
368                  my $d;                  my $d;
369                  foreach (split(m!/!, $row->{'filename'})) {                  foreach (split(m!/!, $row->{'filename'})) {
370                          # first, entry is assumed to be file                          # first, entry is assumed to be file
371                          if ($d) {                          if ($d) {
372                                  $files{$d} = {                                  $files->{$d} = {
                                                 size => $dirs{$d}++,  
373                                                  mode => 0755,                                                  mode => 0755,
374                                                  type => 0040                                                  type => 0040
375                                  };                                  };
376                                  $files{$d.'/.'} = {                                  $files->{$d.'/.'} = {
377                                                  mode => 0755,                                                  mode => 0755,
378                                                  type => 0040                                                  type => 0040
379                                  };                                  };
380                                  $files{$d.'/..'} = {                                  $files->{$d.'/..'} = {
381                                                  mode => 0755,                                                  mode => 0755,
382                                                  type => 0040                                                  type => 0040
383                                  };                                  };
# Line 306  sub read_filenames { Line 387  sub read_filenames {
387                  }                  }
388          }          }
389    
390          print "found ",scalar(keys %files)-scalar(keys %dirs)," files, ",scalar(keys %dirs), " dirs\n";          print "found ",scalar(keys %{$files})," files\n";
391  }  }
392    
393    
# Line 321  sub e_getattr { Line 402  sub e_getattr {
402          my ($file) = filename_fixup(shift);          my ($file) = filename_fixup(shift);
403          $file =~ s,^/,,;          $file =~ s,^/,,;
404          $file = '.' unless length($file);          $file = '.' unless length($file);
405          return -ENOENT() unless exists($files{$file});          return -ENOENT() unless exists($files->{$file});
406          my ($size) = $files{$file}{size} || 1;          my ($size) = $files->{$file}->{size} || 0;
407          my ($dev, $ino, $rdev, $blocks, $gid, $uid, $nlink, $blksize) = (0,0,0,1,0,0,1,1024);          my ($dev, $ino, $rdev, $blocks, $gid, $uid, $nlink, $blksize) = (0,0,0,int(($size+BLOCK-1)/BLOCK),0,0,1,BLOCK);
408          my ($atime, $ctime, $mtime);          my ($atime, $ctime, $mtime);
409          $atime = $ctime = $mtime = $files{$file}{ctime} || $ctime_start;          $atime = $ctime = $mtime = $files->{$file}->{ctime} || $ctime_start;
410    
411          my ($modes) = (($files{$file}{type} || 0100)<<9) + $files{$file}{mode};          my ($modes) = (($files->{$file}->{type} || 0100)<<9) + $files->{$file}->{mode};
412    
413          # 2 possible types of return values:          # 2 possible types of return values:
414          #return -ENOENT(); # or any other error you care to          #return -ENOENT(); # or any other error you care to
415          #print(join(",",($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)),"\n");          #print "getattr($file) ",join(",",($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)),"\n";
416          return ($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks);          return ($dev,$ino,$modes,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks);
417  }  }
418    
# Line 339  sub e_getdir { Line 420  sub e_getdir {
420          my ($dirname) = shift;          my ($dirname) = shift;
421          $dirname =~ s!^/!!;          $dirname =~ s!^/!!;
422          # return as many text filenames as you like, followed by the retval.          # return as many text filenames as you like, followed by the retval.
423          print((scalar keys %files)." files total\n");          print((scalar keys %{$files})." files total\n");
424          my %out;          my %out;
425          foreach my $f (sort keys %files) {          foreach my $f (sort keys %{$files}) {
426                  if ($dirname) {                  if ($dirname) {
427                          if ($f =~ s/^\Q$dirname\E\///) {                          if ($f =~ s/^\Q$dirname\E\///) {
428                                  $out{$f}++ if ($f =~ /^[^\/]+$/);                                  $out{$f}++ if ($f =~ /^[^\/]+$/);
# Line 359  sub e_getdir { Line 440  sub e_getdir {
440  }  }
441    
442  sub read_content {  sub read_content {
443          my ($file,$id) = @_;          my $file = shift || die "need file";
444    
445          die "read_content needs file and id" unless ($file && $id);          warn "file: $file\n", Dumper($fuse_self);
446    
447          $sth->{'read'}->execute($id) || die $sth->{'read'}->errstr;          my @args = $$fuse_self->{'read_ref'}->($files->{$file});
448          $files{$file}{cont} = $sth->{'read'}->fetchrow_array;          my $sql = shift @args || die "need SQL for $file";
449    
450            $$fuse_self->{'read_sth'}->{$sql} ||= $$fuse_self->{sth}->prepare($sql) || die $dbh->errstr();
451            my $sth = $$fuse_self->{'read_sth'}->{$sql} || die;
452    
453            $sth->execute(@args) || die $sth->errstr;
454            $files->{$file}->{cont} = $sth->fetchrow_array;
455          # I should modify ctime only if content in database changed          # I should modify ctime only if content in database changed
456          #$files{$file}{ctime} = time() unless ($files{$file}{ctime});          #$files->{$file}->{ctime} = time() unless ($files->{$file}->{ctime});
457          print "file '$file' content [",length($files{$file}{cont})," bytes] read in cache\n";          print "file '$file' content [",length($files->{$file}->{cont})," bytes] read in cache\n";
458  }  }
459    
460    
# Line 376  sub e_open { Line 463  sub e_open {
463          my $file = filename_fixup(shift);          my $file = filename_fixup(shift);
464          my $flags = shift;          my $flags = shift;
465    
466          return -ENOENT() unless exists($files{$file});          return -ENOENT() unless exists($files->{$file});
467          return -EISDIR() unless exists($files{$file}{id});          return -EISDIR() unless exists($files->{$file}->{id});
468    
469          read_content($file,$files{$file}{id}) unless exists($files{$file}{cont});          read_content($file,$files->{$file}->{id}) unless exists($files->{$file}->{cont});
470    
471          print "open '$file' ",length($files{$file}{cont})," bytes\n";          $files->{$file}->{cont} ||= '';
472            print "open '$file' ",length($files->{$file}->{cont})," bytes\n";
473          return 0;          return 0;
474  }  }
475    
# Line 392  sub e_read { Line 480  sub e_read {
480          my ($file) = filename_fixup(shift);          my ($file) = filename_fixup(shift);
481          my ($buf_len,$off) = @_;          my ($buf_len,$off) = @_;
482    
483          return -ENOENT() unless exists($files{$file});          return -ENOENT() unless exists($files->{$file});
484    
485          my $len = length($files{$file}{cont});          my $len = length($files->{$file}->{cont});
486    
487          print "read '$file' [$len bytes] offset $off length $buf_len\n";          print "read '$file' [$len bytes] offset $off length $buf_len\n";
488    
# Line 403  sub e_read { Line 491  sub e_read {
491    
492          $buf_len = $len-$off if ($len - $off < $buf_len);          $buf_len = $len-$off if ($len - $off < $buf_len);
493    
494          return substr($files{$file}{cont},$off,$buf_len);          return substr($files->{$file}->{cont},$off,$buf_len);
495  }  }
496    
497  sub clear_cont {  sub clear_cont {
498          print "transaction rollback\n";          print "transaction rollback\n";
499          $dbh->rollback || die $dbh->errstr;          $dbh->rollback || die $dbh->errstr;
500          print "invalidate all cached content\n";          print "invalidate all cached content\n";
501          foreach my $f (keys %files) {          foreach my $f (keys %{$files}) {
502                  delete $files{$f}{cont};                  delete $files->{$f}->{cont};
503                  delete $files{$f}{ctime};                  delete $files->{$f}->{ctime};
504          }          }
505          print "begin new transaction\n";          print "begin new transaction\n";
506          #$dbh->begin_work || die $dbh->errstr;          #$dbh->begin_work || die $dbh->errstr;
# Line 420  sub clear_cont { Line 508  sub clear_cont {
508    
509    
510  sub update_db {  sub update_db {
511          my $file = shift || die;          my $file = shift || die "need file";
512    
513          $files{$file}{ctime} = time();          $files->{$file}->{ctime} = time();
514    
515          my ($cont,$id) = (          my ($cont,$id) = (
516                  $files{$file}{cont},                  $files->{$file}->{cont},
517                  $files{$file}{id}                  $files->{$file}->{id}
518          );          );
519    
520          if (!$sth->{'update'}->execute($cont,$id)) {          my @args = $$fuse_self->{'update_ref'}->($files->{$file});
521                  print "update problem: ",$sth->{'update'}->errstr;          my $sql = shift @args || die "need SQL for $file";
522    
523            my $sth = $$fuse_self->{'update_sth'}->{$sql}
524                    ||= $$fuse_self->{sth}->prepare($sql)
525                    || die $dbh->errstr();
526    
527            if (!$sth->execute(@args)) {
528                    print "update problem: ",$sth->errstr;
529                  clear_cont;                  clear_cont;
530                  return 0;                  return 0;
531          } else {          } else {
532                  if (! $dbh->commit) {                  if (! $dbh->commit) {
533                          print "ERROR: commit problem: ",$sth->{'update'}->errstr;                          print "ERROR: commit problem: ",$sth->errstr;
534                          clear_cont;                          clear_cont;
535                          return 0;                          return 0;
536                  }                  }
537                  print "updated '$file' [",$files{$file}{id},"]\n";                  print "updated '$file' [",$files->{$file}->{id},"]\n";
538    
539                  $$fuse_self->{'invalidate'}->() if (ref $$fuse_self->{'invalidate'});                  $$fuse_self->{'invalidate'}->() if (ref $$fuse_self->{'invalidate'});
540          }          }
# Line 450  sub e_write { Line 545  sub e_write {
545          my $file = filename_fixup(shift);          my $file = filename_fixup(shift);
546          my ($buffer,$off) = @_;          my ($buffer,$off) = @_;
547    
548          return -ENOENT() unless exists($files{$file});          return -ENOENT() unless exists($files->{$file});
549    
550          my $cont = $files{$file}{cont};          my $cont = $files->{$file}->{cont};
551          my $len = length($cont);          my $len = length($cont);
552    
553          print "write '$file' [$len bytes] offset $off length ",length($buffer),"\n";          print "write '$file' [$len bytes] offset $off length ",length($buffer),"\n";
554    
555          $files{$file}{cont} = "";          $files->{$file}->{cont} = "";
556    
557          $files{$file}{cont} .= substr($cont,0,$off) if ($off > 0);          $files->{$file}->{cont} .= substr($cont,0,$off) if ($off > 0);
558          $files{$file}{cont} .= $buffer;          $files->{$file}->{cont} .= $buffer;
559          $files{$file}{cont} .= substr($cont,$off+length($buffer),$len-$off-length($buffer)) if ($off+length($buffer) < $len);          $files->{$file}->{cont} .= substr($cont,$off+length($buffer),$len-$off-length($buffer)) if ($off+length($buffer) < $len);
560    
561          $files{$file}{size} = length($files{$file}{cont});          $files->{$file}->{size} = length($files->{$file}->{cont});
562    
563          if (! update_db($file)) {          if (! update_db($file)) {
564                  return -ENOSYS();                  return -ENOSYS();
# Line 478  sub e_truncate { Line 573  sub e_truncate {
573    
574          print "truncate to $size\n";          print "truncate to $size\n";
575    
576          $files{$file}{cont} = substr($files{$file}{cont},0,$size);          $files->{$file}->{cont} = substr($files->{$file}->{cont},0,$size);
577          $files{$file}{size} = $size;          $files->{$file}->{size} = $size;
578          return 0          return 0
579  };  };
580    
# Line 488  sub e_utime { Line 583  sub e_utime {
583          my ($atime,$mtime,$file) = @_;          my ($atime,$mtime,$file) = @_;
584          $file = filename_fixup($file);          $file = filename_fixup($file);
585    
586          return -ENOENT() unless exists($files{$file});          return -ENOENT() unless exists($files->{$file});
587    
588          print "utime '$file' $atime $mtime\n";          print "utime '$file' $atime $mtime\n";
589    
590          $files{$file}{time} = $mtime;          $files->{$file}->{time} = $mtime;
591          return 0;          return 0;
592  }  }
593    
594  sub e_statfs { return 255, 1, 1, 1, 1, 2 }  sub e_statfs {
595    
596            my $size = 0;
597            my $inodes = 0;
598    
599            foreach my $f (keys %{$files}) {
600                    if ($f !~ /(^|\/)\.\.?$/) {
601                            $size += $files->{$f}->{size} || 0;
602                            $inodes++;
603                    }
604                    print "$inodes: $f [$size]\n";
605            }
606    
607            $size = int(($size+BLOCK-1)/BLOCK);
608    
609            my @ret = (255, $inodes, 1, $size, $size-1, BLOCK);
610    
611            #print "statfs: ",join(",",@ret),"\n";
612    
613            return @ret;
614    }
615    
616  sub e_unlink {  sub e_unlink {
617          my $file = filename_fixup(shift);          my $file = filename_fixup(shift);
618    
619          if (exists( $dirs{$file} )) {  #       if (exists( $dirs{$file} )) {
620                  print "unlink '$file' will re-read template names\n";  #               print "unlink '$file' will re-read template names\n";
621                  print Dumper($fuse_self);  #               print Dumper($fuse_self);
622                  $$fuse_self->{'read_filenames'}->();  #               $$fuse_self->{'read_filenames'}->();
623                  return 0;  #               return 0;
624          } elsif (exists( $files{$file} )) {          if (exists( $files->{$file} )) {
625                  print "unlink '$file' will invalidate cache\n";                  print "unlink '$file' will invalidate cache\n";
626                  read_content($file,$files{$file}{id});                  read_content($file,$files->{$file}->{id});
627                  return 0;                  return 0;
628          }          }
629    
# Line 521  __END__ Line 636  __END__
636    
637  Nothing.  Nothing.
638    
639    =head1 BUGS
640    
641    Size information (C<ls -s>) is wrong. It's a problem in upstream Fuse module
642    (for which I'm to blame lately), so when it gets fixes, C<Fuse::DBI> will
643    automagically pick it up.
644    
645  =head1 SEE ALSO  =head1 SEE ALSO
646    
647  C<FUSE (Filesystem in USErspace)> website  C<FUSE (Filesystem in USErspace)> website

Legend:
Removed from v.36  
changed lines
  Added in v.62

  ViewVC Help
Powered by ViewVC 1.1.26