File Coverage

blib/lib/Filesys/Virtual/Async/Dispatcher.pm
Criterion Covered Total %
statement 26 397 6.5
branch 1 138 0.7
condition 0 24 0.0
subroutine 9 46 19.5
pod 33 34 97.0
total 69 639 10.8


line stmt bran cond sub pod time code
1             # Declare our package
2             package Filesys::Virtual::Async::Dispatcher;
3 1     1   1150 use strict; use warnings;
  1     1   2  
  1         48  
  1         8  
  1         2  
  1         44  
4              
5             # Initialize our version
6 1     1   17 use vars qw( $VERSION );
  1         3  
  1         83  
7             $VERSION = '0.02';
8              
9             # set our superclass
10 1     1   7 use base 'Filesys::Virtual::Async';
  1         2  
  1         1347  
11              
12             # get some handy stuff
13 1     1   1772 use File::Spec;
  1         2  
  1         33  
14              
15             # get the refaddr of our FHs
16 1     1   7 use Scalar::Util qw( refaddr openhandle );
  1         1  
  1         98  
17              
18             # get some system constants
19 1     1   6 use Errno qw( :POSIX ); # ENOENT EISDIR etc
  1         2  
  1         582  
20 1     1   6 use Fcntl qw( :DEFAULT :mode :seek ); # S_IFREG S_IFDIR, O_SYNC O_LARGEFILE etc
  1         2  
  1         830  
21              
22             # Set some constants
23             BEGIN {
24 1 50   1   5 if ( ! defined &DEBUG ) { *DEBUG = sub () { 0 } }
  1         21338  
25             }
26              
27             # creates a new instance
28             sub new {
29 0     0 1   my $class = shift;
30              
31             # The options hash
32 0           my %opt;
33              
34             # Support passing in a hash ref or a regular hash
35 0 0 0       if ( ( @_ & 1 ) and ref $_[0] and ref( $_[0] ) eq 'HASH' ) {
      0        
36 0           %opt = %{ $_[0] };
  0            
37             } else {
38             # Sanity checking
39 0 0         if ( @_ & 1 ) {
40 0           die __PACKAGE__ . ' requires an even number of options passed to new()';
41             }
42              
43 0           %opt = @_;
44             }
45              
46             # lowercase keys
47 0           %opt = map { lc($_) => $opt{$_} } keys %opt;
  0            
48              
49             # set the rootfs
50 0 0 0       if ( ! exists $opt{'rootfs'} or ! defined $opt{'rootfs'} or ! ref $opt{'rootfs'} ) {
51 0           die __PACKAGE__ . ' needs rootfs defined to bootstrap';
52             } else {
53             # make sure it's the proper object
54 0 0         if ( ! $opt{'rootfs'}->isa( 'Filesys::Virtual::Async' ) ) {
55 0           die 'rootfs is not a valid ::Async subclass';
56             }
57             }
58              
59             # create our instance
60 0   0       my $self = {
61             'cwd' => $opt{'rootfs'}->cwd || File::Spec->rootdir(),
62             'mounts' => {},
63             'mountstree' => {},
64             'fhmap' => {},
65             };
66 0           bless $self, $class;
67              
68             # initialize the first mount
69 0           $self->mount( File::Spec->rootdir(), $opt{'rootfs'} );
70              
71 0           return $self;
72             }
73              
74             sub mount {
75 0     0 1   my( $self, $path, $vfs ) = @_;
76              
77             # sanity
78 0 0         if ( ! defined $path ) {
79 0           if ( DEBUG ) {
80             warn 'invalid path';
81             }
82 0           return 0;
83             } else {
84             # sanitize the path
85 0           $path = File::Spec->canonpath( $path );
86             }
87              
88             # make sure it's a valid subclass
89 0 0 0       if ( ! defined $vfs or ! ref $vfs or ! $vfs->isa( 'Filesys::Virtual::Async' ) ) {
      0        
90 0           if ( DEBUG ) {
91             warn 'vfs is not a valid ::Async subclass';
92             }
93 0           return 0;
94             }
95              
96             # FIXME Does the directory exist?
97             # this is insane... we need a callback to stat() and see if it exists!
98             # for now, we blindly mount, ha!
99              
100             # Is that path taken?
101 0 0         if ( exists $self->{'mounts'}->{ $path } ) {
102 0           if ( DEBUG ) {
103             warn 'unable to mount over another mount';
104             }
105 0           return 0;
106             }
107              
108             # Split up the path
109 0           my @dirs;
110 0 0         if ( $path eq File::Spec->rootdir() ) {
111 0           push( @dirs, File::Spec->rootdir() );
112             } else {
113 0           @dirs = File::Spec->splitdir( $path );
114 0 0         if ( @dirs ) {
115 0           $dirs[0] = File::Spec->rootdir();
116             } else {
117 0           if ( DEBUG ) {
118             warn 'path is not a valid directory name';
119             }
120 0           return 0;
121             }
122             }
123              
124             # store it!
125 0           if ( DEBUG ) {
126             warn "mounting '$path' with $vfs";
127             }
128 0           $self->{'mounts'}->{ $path } = $vfs;
129              
130             # build the tree
131 0           my $curpos = $self->{'mountstree'};
132 0           foreach my $dir ( @dirs ) {
133             ## no critic ( ProhibitAccessOfPrivateData )
134 0 0         $curpos->{ $dir } = {} if not exists $curpos->{ $dir };
135 0           $curpos = $curpos->{ $dir };
136             }
137              
138 0           return 1;
139             }
140              
141             sub umount {
142 0     0 1   my( $self, $path ) = @_;
143              
144             # sanity
145 0 0         if ( ! defined $path ) {
146 0           if ( DEBUG ) {
147             warn 'invalid path';
148             }
149 0           return 0;
150             } else {
151             # sanitize the path
152 0           $path = File::Spec->canonpath( $path );
153             }
154              
155             # unable to umount the rootfs, hah!
156 0 0         if ( $path eq File::Spec->rootdir() ) {
157 0           if ( DEBUG ) {
158             warn 'unable to umount the rootfs';
159             }
160 0           return 0;
161             }
162              
163             # is the path mounted?
164 0 0         if ( ! exists $self->{'mounts'}->{ $path } ) {
165 0           if ( DEBUG ) {
166             warn "directory '$path' is not mounted";
167             }
168 0           return 0;
169             }
170              
171             # are there any mounts under this one?
172 0           my @matches = grep { $_ =~ /^$path/ } ( keys %{ $self->{'mounts'} } );
  0            
  0            
173 0 0         if ( @matches > 1 ) {
174 0           if ( DEBUG ) {
175             warn "unable to umount '$path' as there are more mounts inside it";
176             }
177 0           return 0;
178             }
179              
180 0           if ( DEBUG ) {
181             warn "umounting '$path'";
182             }
183              
184             # unmount it!
185 0           delete $self->{'mounts'}->{ $path };
186              
187             # clean up the tree
188 0           my @dirs = File::Spec->splitdir( $path );
189 0           shift( @dirs ); # get rid of the root entry which is always '' for me
190 0           my $curpath = File::Spec->rootdir();
191 0           my $curpos = $self->{'mountstree'}->{ $curpath };
192 0           foreach my $dir ( @dirs ) {
193 0           $curpath = File::Spec->catdir( $curpath, $dir );
194 0 0         if ( ! exists $self->{'mounts'}->{ $curpath } ) {
195             # yay, reached end of tree
196 0           delete $curpos->{ $dir }; ## no critic ( ProhibitAccessOfPrivateData )
197 0           last;
198             } else {
199 0           $curpos = $curpos->{ $dir }; ## no critic ( ProhibitAccessOfPrivateData )
200             }
201             }
202              
203 0           return 1;
204             }
205              
206             sub _findmount {
207 0     0     my( $self, $path ) = @_;
208              
209             # get an absolute path
210 0           $path = File::Spec->rel2abs( $path, $self->{'cwd'} );
211              
212             # traverse the tree, searching for the "deepest" hash we can find
213 0           my @dirs = File::Spec->splitdir( $path );
214 0           shift( @dirs ); # get rid of the root entry which is always '' for me
215 0           my $curpath = File::Spec->rootdir();
216 0           my $curpos = $self->{'mountstree'}->{ $curpath };
217 0           foreach my $dir ( @dirs ) {
218             ## no critic ( ProhibitAccessOfPrivateData )
219 0 0         if ( exists $curpos->{ $dir } ) {
220 0           $curpath = File::Spec->catdir( $curpath, $dir );
221              
222             # is it the end?
223 0 0         if ( ! defined $curpos->{ $dir } ) {
224             # found our match!
225 0           last;
226             } else {
227             # continue traversing
228 0           $curpos = $curpos->{ $dir };
229             }
230             } else {
231             # found our match!
232 0           last;
233             }
234             }
235              
236             # grab the mount object
237 0 0         if ( ! exists $self->{'mounts'}->{ $curpath } ) {
238 0           die "internal inconsistency - unable to find mount path($path) curpath($curpath)";
239             }
240              
241             # figure out the relative path
242 0           my $relpath = File::Spec->catdir( File::Spec->rootdir(), File::Spec->abs2rel( $path, $curpath ) );
243              
244             # all done!
245 0           return $self->{'mounts'}->{ $curpath }, $relpath;
246             }
247              
248             sub cwd {
249 0     0 1   my( $self, $cwd, $cb ) = @_;
250              
251             # sanitize the path
252 0           $cwd = File::Spec->canonpath( $cwd );
253              
254             # Get or set?
255 0 0         if ( ! defined $cwd ) {
256 0 0         if ( defined $cb ) {
257 0           $cb->( $self->{'cwd'} );
258 0           return;
259             } else {
260 0           return $self->{'cwd'};
261             }
262             }
263              
264             # Is it the same cwd as we have?
265 0 0         if ( $cwd eq $self->{'cwd'} ) {
266 0 0         if ( defined $cb ) {
267 0           $cb->( $cwd );
268 0           return;
269             } else {
270 0           return $cwd;
271             }
272             }
273              
274             # actually change our cwd!
275 0           $self->{'cwd'} = $cwd;
276 0           my( $mount, $where ) = $self->_findmount( $cwd );
277 0 0         if ( defined $cb ) {
278 0           $mount->cwd( $where, $cb );
279 0           return;
280             } else {
281 0           return $mount->cwd( $where );
282             }
283             }
284              
285             sub root {
286             # we cannot sanely do this because we have no idea which mount to apply it to...
287 0     0 1   if ( DEBUG ) {
288             warn 'Setting root on the dispatcher has no meaning, please do it directly on the mount!';
289             }
290 0           return;
291             }
292              
293             sub _resolve_fh {
294 0     0     my $self = shift;
295 0           my $fh = shift;
296 0           my $ret = undef;
297 0 0         if ( openhandle( $fh ) ) {
298 0           $ret = refaddr( $fh );
299 0 0         if ( ! defined $ret ) {
300             # try direct stringy eval
301 0           $ret = "$fh";
302             }
303             }
304              
305 0           return $ret;
306             }
307              
308             sub dirlist {
309 0     0 0   my ( $self, $path, $withstat, $callback ) = @_;
310              
311 0           my( $mount, $where ) = $self->_findmount( $path );
312 0           $mount->dirlist( $where, $withstat, $callback );
313              
314 0           return;
315             }
316              
317             sub open {
318 0     0 1   my( $self, $path, $flags, $mode, $callback ) = @_;
319              
320             # construct our custom callback
321             my $cb = sub {
322 0     0     my $fh = shift;
323 0 0         if ( defined $fh ) {
324 0           my $mapping = $self->_resolve_fh( $fh );
325 0 0         if ( defined $mapping ) {
326 0 0         if ( exists $self->{'fhmap'}->{ $mapping } ) {
327 0           die "internal inconsistency - fh already exists in fhmap!";
328             }
329              
330             # FIXME does $path need to be relative or absolute?
331 0           $self->{'fhmap'}->{ $mapping } = $path;
332 0           $callback->( $fh );
333             } else {
334             # must be an error code
335 0           $callback->( $fh );
336             }
337             } else {
338 0           $callback->( -EIO() );
339             }
340 0           };
341              
342 0           my( $mount, $where ) = $self->_findmount( $path );
343 0           $mount->open( $where, $flags, $mode, $cb );
344              
345 0           return;
346             }
347              
348             sub close {
349 0     0 1   my( $self, $fh, $callback ) = @_;
350              
351             # get the proper mount
352 0           my $mapping = $self->_resolve_fh( $fh );
353 0 0         if ( defined $mapping ) {
354 0 0         if ( exists $self->{'fhmap'}->{ $mapping } ) {
355 0           my( $mount, undef ) = $self->_findmount( delete $self->{'fhmap'}->{ $mapping } );
356              
357 0           $mount->close( $fh, $callback );
358             } else {
359 0           die "internal inconsistency - unknown fh: $fh";
360             }
361             } else {
362 0           die "internal inconsistency - unknown fh: $fh";
363             }
364              
365 0           return;
366             }
367              
368             sub read {
369             # have to leave @_ alone so aio_read will get proper $buffer reference :(
370 0     0 1   my $self = shift;
371 0           my $fh = shift;
372              
373             # get the proper mount
374 0           my $mapping = $self->_resolve_fh( $fh );
375 0 0         if ( defined $mapping ) {
376 0 0         if ( exists $self->{'fhmap'}->{ $mapping } ) {
377 0           my( $mount, undef ) = $self->_findmount( $self->{'fhmap'}->{ $mapping } );
378              
379 0           $mount->read( $fh, $_[0], $_[1], $_[2], $_[3], $_[4] );
380             } else {
381 0           die "internal inconsistency - unknown fh: $fh";
382             }
383             } else {
384 0           die "internal inconsistency - unknown fh: $fh";
385             }
386              
387 0           return;
388             }
389              
390             sub write {
391             # have to leave @_ alone so aio_read will get proper $buffer reference :(
392 0     0 1   my $self = shift;
393 0           my $fh = shift;
394              
395             # get the proper mount
396 0           my $mapping = $self->_resolve_fh( $fh );
397 0 0         if ( defined $mapping ) {
398 0 0         if ( exists $self->{'fhmap'}->{ $mapping } ) {
399 0           my( $mount, undef ) = $self->_findmount( $self->{'fhmap'}->{ $mapping } );
400              
401 0           $mount->write( $fh, $_[0], $_[1], $_[2], $_[3], $_[4] );
402             } else {
403 0           die "internal inconsistency - unknown fh: $fh";
404             }
405             } else {
406 0           die "internal inconsistency - unknown fh: $fh";
407             }
408              
409 0           return;
410             }
411              
412             sub sendfile {
413 0     0 1   my( $self, $out_fh, $in_fh, $in_offset, $length, $callback ) = @_;
414              
415             # FIXME make sure both fh's belong to the same mount?
416             # also, which fh should we "select" from to determine mountpoint? I'm defaulting to $in_fh here...
417              
418             # get the proper mount
419 0           my $mapping = $self->_resolve_fh( $in_fh );
420 0 0         if ( defined $mapping ) {
421 0 0         if ( exists $self->{'fhmap'}->{ $mapping } ) {
422 0           my( $mount, undef ) = $self->_findmount( $self->{'fhmap'}->{ $mapping } );
423              
424 0           $mount->sendfile( $out_fh, $in_fh, $in_offset, $length, $callback );
425             } else {
426 0           die "internal inconsistency - unknown fh: $in_fh";
427             }
428             } else {
429 0           die "internal inconsistency - unknown fh: $in_fh";
430             }
431              
432 0           return;
433             }
434              
435             sub readahead {
436 0     0 1   my( $self, $fh, $offset, $length, $callback ) = @_;
437              
438             # get the proper mount
439 0           my $mapping = $self->_resolve_fh( $fh );
440 0 0         if ( defined $mapping ) {
441 0 0         if ( exists $self->{'fhmap'}->{ $mapping } ) {
442 0           my( $mount, undef ) = $self->_findmount( $self->{'fhmap'}->{ $mapping } );
443              
444 0           $mount->readahead( $fh, $offset, $length, $callback );
445             } else {
446 0           die "internal inconsistency - unknown fh: $fh";
447             }
448             } else {
449 0           die "internal inconsistency - unknown fh: $fh";
450             }
451              
452 0           return;
453             }
454              
455             sub stat {
456 0     0 1   my( $self, $fh_or_path, $callback ) = @_;
457              
458             # FIXME we don't support array mode because it would require insane amounts of munging the paths
459 0 0 0       if ( ref $fh_or_path and ref( $fh_or_path ) eq 'ARRAY' ) {
460 0           if ( DEBUG ) {
461             warn 'Passing an ARRAY to stat() is not supported by the Dispatcher!';
462             }
463 0           $callback->( undef );
464 0           return;
465             }
466              
467             # is it a fh or path?
468 0 0         if ( ref $fh_or_path ) {
469             # get the proper mount
470 0           my $mapping = $self->_resolve_fh( $fh_or_path );
471 0 0         if ( defined $mapping ) {
472 0 0         if ( exists $self->{'fhmap'}->{ $mapping } ) {
473 0           my( $mount, undef ) = $self->_findmount( $self->{'fhmap'}->{ $mapping } );
474 0           $mount->stat( $fh_or_path, $callback );
475             } else {
476 0           die "internal inconsistency - unknown fh: $fh_or_path";
477             }
478             } else {
479 0           die "internal inconsistency - unknown fh: $fh_or_path";
480             }
481             } else {
482 0           my( $mount, $where ) = $self->_findmount( $fh_or_path );
483 0           $mount->stat( $where, $callback );
484             }
485              
486 0           return;
487             }
488              
489             sub lstat {
490 0     0 1   my( $self, $fh_or_path, $callback ) = @_;
491              
492             # FIXME we don't support array mode because it would require insane amounts of munging the paths
493 0 0 0       if ( ref $fh_or_path and ref( $fh_or_path ) eq 'ARRAY' ) {
494 0           if ( DEBUG ) {
495             warn 'Passing an ARRAY to lstat() is not supported by the Dispatcher!';
496             }
497 0           $callback->( undef );
498 0           return;
499             }
500              
501             # is it a fh or path?
502 0 0         if ( ref $fh_or_path ) {
503             # get the proper mount
504 0           my $mapping = $self->_resolve_fh( $fh_or_path );
505 0 0         if ( defined $mapping ) {
506 0 0         if ( exists $self->{'fhmap'}->{ $mapping } ) {
507 0           my( $mount, undef ) = $self->_findmount( $self->{'fhmap'}->{ $mapping } );
508 0           $mount->lstat( $fh_or_path, $callback );
509             } else {
510 0           die "internal inconsistency - unknown fh: $fh_or_path";
511             }
512             } else {
513 0           die "internal inconsistency - unknown fh: $fh_or_path";
514             }
515             } else {
516 0           my( $mount, $where ) = $self->_findmount( $fh_or_path );
517 0           $mount->lstat( $where, $callback );
518             }
519              
520 0           return;
521             }
522              
523             sub utime {
524 0     0 1   my( $self, $fh_or_path, $atime, $mtime, $callback ) = @_;
525              
526             # is it a fh or path?
527 0 0         if ( ref $fh_or_path ) {
528             # get the proper mount
529 0           my $mapping = $self->_resolve_fh( $fh_or_path );
530 0 0         if ( defined $mapping ) {
531 0 0         if ( exists $self->{'fhmap'}->{ $mapping } ) {
532 0           my( $mount, undef ) = $self->_findmount( $self->{'fhmap'}->{ $mapping } );
533 0           $mount->utime( $fh_or_path, $atime, $mtime, $callback );
534             } else {
535 0           die "internal inconsistency - unknown fh: $fh_or_path";
536             }
537             } else {
538 0           die "internal inconsistency - unknown fh: $fh_or_path";
539             }
540             } else {
541 0           my( $mount, $where ) = $self->_findmount( $fh_or_path );
542 0           $mount->utime( $where, $atime, $mtime, $callback );
543             }
544              
545 0           return;
546             }
547              
548             sub chown {
549 0     0 1   my( $self, $fh_or_path, $uid, $gid, $callback ) = @_;
550              
551             # is it a fh or path?
552 0 0         if ( ref $fh_or_path ) {
553             # get the proper mount
554 0           my $mapping = $self->_resolve_fh( $fh_or_path );
555 0 0         if ( defined $mapping ) {
556 0 0         if ( exists $self->{'fhmap'}->{ $mapping } ) {
557 0           my( $mount, undef ) = $self->_findmount( $self->{'fhmap'}->{ $mapping } );
558 0           $mount->chown( $fh_or_path, $uid, $gid, $callback );
559             } else {
560 0           die "internal inconsistency - unknown fh: $fh_or_path";
561             }
562             } else {
563 0           die "internal inconsistency - unknown fh: $fh_or_path";
564             }
565             } else {
566 0           my( $mount, $where ) = $self->_findmount( $fh_or_path );
567 0           $mount->chown( $where, $uid, $gid, $callback );
568             }
569              
570 0           return;
571             }
572              
573             sub truncate {
574 0     0 1   my( $self, $fh_or_path, $offset, $callback ) = @_;
575              
576             # is it a fh or path?
577 0 0         if ( ref $fh_or_path ) {
578             # get the proper mount
579 0           my $mapping = $self->_resolve_fh( $fh_or_path );
580 0 0         if ( defined $mapping ) {
581 0 0         if ( exists $self->{'fhmap'}->{ $mapping } ) {
582 0           my( $mount, undef ) = $self->_findmount( $self->{'fhmap'}->{ $mapping } );
583 0           $mount->truncate( $fh_or_path, $offset, $callback );
584             } else {
585 0           die "internal inconsistency - unknown fh: $fh_or_path";
586             }
587             } else {
588 0           die "internal inconsistency - unknown fh: $fh_or_path";
589             }
590             } else {
591 0           my( $mount, $where ) = $self->_findmount( $fh_or_path );
592 0           $mount->truncate( $where, $offset, $callback );
593             }
594              
595 0           return;
596             }
597              
598             sub chmod {
599 0     0 1   my( $self, $fh_or_path, $mode, $callback ) = @_;
600              
601             # is it a fh or path?
602 0 0         if ( ref $fh_or_path ) {
603             # get the proper mount
604 0           my $mapping = $self->_resolve_fh( $fh_or_path );
605 0 0         if ( defined $mapping ) {
606 0 0         if ( exists $self->{'fhmap'}->{ $mapping } ) {
607 0           my( $mount, undef ) = $self->_findmount( $self->{'fhmap'}->{ $mapping } );
608 0           $mount->chmod( $fh_or_path, $mode, $callback );
609             } else {
610 0           die "internal inconsistency - unknown fh: $fh_or_path";
611             }
612             } else {
613 0           die "internal inconsistency - unknown fh: $fh_or_path";
614             }
615             } else {
616 0           my( $mount, $where ) = $self->_findmount( $fh_or_path );
617 0           $mount->chmod( $where, $mode, $callback );
618             }
619              
620 0           return;
621             }
622              
623             sub unlink {
624 0     0 1   my( $self, $path, $callback ) = @_;
625              
626 0           my( $mount, $where ) = $self->_findmount( $path );
627 0           $mount->unlink( $where, $callback );
628              
629 0           return;
630             }
631              
632             sub mknod {
633 0     0 1   my( $self, $path, $mode, $dev, $callback ) = @_;
634              
635 0           my( $mount, $where ) = $self->_findmount( $path );
636 0           $mount->mknod( $where, $mode, $dev, $callback );
637              
638 0           return;
639             }
640              
641             sub link {
642 0     0 1   my( $self, $srcpath, $dstpath, $callback ) = @_;
643              
644             # we disallow links across mounts, because it's impossible to get arbitrary mounts to cooperate :(
645 0           my( $mount, $where ) = $self->_findmount( $srcpath );
646 0           my( $mount2, $where2 ) = $self->_findmount( $dstpath );
647 0 0         if ( $mount != $mount2 ) {
648 0           if ( DEBUG ) {
649             warn 'linking across mounts is not supported by the Dispatcher!';
650             }
651 0           $callback->( -1 ); # FIXME what's the proper failure code?
652 0           return;
653             }
654              
655 0           $mount->link( $where, $where2, $callback );
656              
657 0           return;
658             }
659              
660             sub symlink {
661 0     0 1   my( $self, $srcpath, $dstpath, $callback ) = @_;
662              
663             # we disallow links across mounts, because it's impossible to get arbitrary mounts to cooperate :(
664 0           my( $mount, $where ) = $self->_findmount( $srcpath );
665 0           my( $mount2, $where2 ) = $self->_findmount( $dstpath );
666 0 0         if ( $mount != $mount2 ) {
667 0           if ( DEBUG ) {
668             warn 'linking across mounts is not supported by the Dispatcher!';
669             }
670 0           $callback->( -1 ); # FIXME what's the proper failure code?
671 0           return;
672             }
673              
674 0           $mount->symlink( $where, $where2, $callback );
675              
676 0           return;
677             }
678              
679             sub readlink {
680 0     0 1   my( $self, $path, $callback ) = @_;
681              
682 0           my( $mount, $where ) = $self->_findmount( $path );
683 0           $mount->readlink( $where, $callback );
684              
685 0           return;
686             }
687              
688             sub rename {
689 0     0 1   my( $self, $srcpath, $dstpath, $callback ) = @_;
690              
691             # FIXME we theoretically could rename across mounts by implementing it ourself, but I'm lazy now :)
692 0           my( $mount, $where ) = $self->_findmount( $srcpath );
693 0           my( $mount2, $where2 ) = $self->_findmount( $dstpath );
694 0 0         if ( $mount != $mount2 ) {
695 0           if ( DEBUG ) {
696             warn 'renaming across mounts is not supported by the Dispatcher!';
697             }
698 0           $callback->( -1 ); # FIXME what's the proper failure code?
699 0           return;
700             }
701              
702 0           $mount->rename( $where, $where2, $callback );
703              
704 0           return;
705             }
706              
707             sub mkdir {
708 0     0 1   my( $self, $path, $mode, $callback ) = @_;
709              
710 0           my( $mount, $where ) = $self->_findmount( $path );
711 0           $mount->mkdir( $where, $mode, $callback );
712              
713 0           return;
714             }
715              
716             sub rmdir {
717 0     0 1   my( $self, $path, $callback ) = @_;
718              
719 0           my( $mount, $where ) = $self->_findmount( $path );
720 0           $mount->rmdir( $where, $callback );
721              
722 0           return;
723             }
724              
725             sub readdir {
726 0     0 1   my( $self, $path, $callback ) = @_;
727              
728 0           my( $mount, $where ) = $self->_findmount( $path );
729 0           $mount->readdir( $where, $callback );
730              
731 0           return;
732             }
733              
734             sub load {
735             # have to leave @_ alone so caller will get proper $data reference :(
736 0     0 1   my $self = shift;
737 0           my $path = shift;
738              
739 0           my( $mount, $where ) = $self->_findmount( $path );
740 0           $mount->load( $where, $_[0], $_[1] );
741              
742 0           return;
743             }
744              
745             sub copy {
746 0     0 1   my( $self, $srcpath, $dstpath, $callback ) = @_;
747              
748             # FIXME we theoretically could copy across mounts by implementing it ourself, but I'm lazy now :)
749 0           my( $mount, $where ) = $self->_findmount( $srcpath );
750 0           my( $mount2, $where2 ) = $self->_findmount( $dstpath );
751 0 0         if ( $mount != $mount2 ) {
752 0           if ( DEBUG ) {
753             warn 'copying across mounts is not supported by the Dispatcher!';
754             }
755 0           $callback->( 0 );
756 0           return;
757             }
758              
759 0           $mount->copy( $where, $where2, $callback );
760              
761 0           return;
762             }
763              
764             sub move {
765 0     0 1   my( $self, $srcpath, $dstpath, $callback ) = @_;
766              
767             # FIXME we theoretically could move across mounts by implementing it ourself, but I'm lazy now :)
768 0           my( $mount, $where ) = $self->_findmount( $srcpath );
769 0           my( $mount2, $where2 ) = $self->_findmount( $dstpath );
770 0 0         if ( $mount != $mount2 ) {
771 0           if ( DEBUG ) {
772             warn 'moving across mounts is not supported by the Dispatcher!';
773             }
774 0           $callback->( -1 ); # FIXME what's the proper failure code?
775 0           return;
776             }
777              
778 0           $mount->move( $where, $where2, $callback );
779              
780 0           return;
781             }
782              
783             sub scandir {
784 0     0 1   my( $self, $path, $maxreq, $callback ) = @_;
785              
786 0           my( $mount, $where ) = $self->_findmount( $path );
787 0           $mount->scandir( $where, $maxreq, $callback );
788              
789 0           return;
790             }
791              
792             sub rmtree {
793 0     0 1   my( $self, $path, $callback ) = @_;
794              
795 0           my( $mount, $where ) = $self->_findmount( $path );
796              
797             # we disallow rmtree if there's a mount under the path ( because of complications )
798 0           my $matches = grep { $_ =~ /^$path.+/ } ( keys %{ $self->{'mounts'} } );
  0            
  0            
799 0 0         if ( $matches ) {
800 0           if ( DEBUG ) {
801             warn 'rmtree across mounts is not supported by the Dispatcher!';
802             }
803 0           $callback->( -1 ); # FIXME what's the proper failure code?
804             } else {
805 0           $mount->rmtree( $where, $callback );
806             }
807              
808 0           return;
809             }
810              
811             sub fsync {
812 0     0 1   my( $self, $fh, $callback ) = @_;
813              
814             # get the proper mount
815 0           my $mapping = $self->_resolve_fh( $fh );
816 0 0         if ( defined $mapping ) {
817 0 0         if ( exists $self->{'fhmap'}->{ $mapping } ) {
818 0           my( $mount, undef ) = $self->_findmount( $self->{'fhmap'}->{ $mapping } );
819              
820 0           $mount->fsync( $fh, $callback );
821             } else {
822 0           die "internal inconsistency - unknown fh: $fh";
823             }
824             } else {
825 0           die "internal inconsistency - unknown fh: $fh";
826             }
827              
828 0           return;
829             }
830              
831             sub fdatasync {
832 0     0 1   my( $self, $fh, $callback ) = @_;
833              
834             # get the proper mount
835 0           my $mapping = $self->_resolve_fh( $fh );
836 0 0         if ( defined $mapping ) {
837 0 0         if ( exists $self->{'fhmap'}->{ $mapping } ) {
838 0           my( $mount, undef ) = $self->_findmount( $self->{'fhmap'}->{ $mapping } );
839              
840 0           $mount->fdatasync( $fh, $callback );
841             } else {
842 0           die "internal inconsistency - unknown fh: $fh";
843             }
844             } else {
845 0           die "internal inconsistency - unknown fh: $fh";
846             }
847              
848 0           return;
849             }
850              
851             1;
852             __END__