1 package Fuse
::InMemory
;
7 # create a new instance
11 'root' => Fuse
::InMemory
::File
->new(
13 'mode' => S_IFDIR
|0755,
21 my ($self, @args) = @_;
22 local @ARGV = @args if @args;
26 'mountopts' => 'default_permissions,allow_other',
28 # specified arguments supercede the defaults
30 'debug' => \
$fuse_args{'debug'},
31 'o=s' => \
$fuse_args{'mountopts'},
32 ) or Getopt
::Long
::HelpMessage
(2);
33 $fuse_args{'mountpoint'} = shift @ARGV unless defined $fuse_args{'mountpoint'};
34 # install Fuse callbacks
35 for my $f (keys %{*{'Fuse::InMemory::'}}) {
36 if($f =~ /^fuse_([a-z]+)$/) {
37 $fuse_args{$1} = eval 'sub { my $before = $self->check_before("'.$1.'", @_); return $before if defined $before; return $self->check_after("'.$1.'",$self->fuse_'.$1.'(@_)); }';
41 Fuse
::main
(%fuse_args);
44 # run the before filter if there is one
46 my ($self, $cb, @args) = @_;
47 my $check = $self->{'before_'.$cb};
48 return $check->($self, $cb, @args) if ref $check;
52 # run the after filter if there is one
54 my ($self, $cb, @args) = @_;
55 my $check = $self->{'after_'.$cb};
56 return $check->($self, $cb, @args) if ref $check;
57 return $args[0] unless wantarray;
61 # locate a file object by path name, optionally create it if a replacement is specified
63 my ($self, $filename, $replacement) = @_;
64 my $res = $self->{'root'};
67 my @parts = split /\//, $filename;
69 my $dir = shift @parts;
71 return -ENOENT
() unless ref $res;
72 return -ENOTDIR
() unless $res->is_directory();
74 $res = $res->get_file($dir);
76 if(defined $replacement && !ref $res && defined $last) {
78 $last->add_file($res);
80 $res = Fuse
::InMemory
::File
->new(path
=> $filename, exists => 0) unless ref $res;
84 # add magic to a file by path name
86 my($self, $filename, $reader, $writer, $truncater, $flusher) = @_;
87 my $file = $self->find_file($filename);
88 return $file unless ref $file;
89 $file->{'reader'} = $reader;
90 $file->{'writer'} = $writer;
91 $file->{'flusher'} = $flusher;
92 $file->{'truncater'} = $truncater;
98 my ($self, $filename) = @_;
99 my $file = $self->find_file($filename);
100 return $file unless ref $file;
101 return -ENOENT
() unless $file->exists();
102 # print "getattr $filename\n";
106 my @ret = $file->getattr();
112 my ($self, $filename) = @_;
113 my $file = $self->find_file($filename);
114 return $file unless ref $file;
115 my $ret = $file->is_symlink();
116 $ret = -EINVAL
() unless $ret;
122 my ($self, $filename) = @_;
123 my $file = $self->find_file($filename);
124 return $file unless ref $file;
125 return -ENOTDIR
() unless $file->is_directory();
126 return $file->getdir();
131 my ($self, $filename, $mode, $device) = @_;
132 my $file = $self->find_file($filename);
133 return $file unless ref $file;
134 return -EEXIST
() if $file->exists();
135 $file->create($self);
141 my ($self, $filename, $mode) = @_;
142 my $file = $self->find_file($filename);
143 return $file unless ref $file;
144 return -EEXIST
() if $file->exists();
145 $file->set_mode(S_IFDIR
| $mode);
146 $file->create($self);
152 my ($self, $filename) = @_;
153 my $file = $self->find_file($filename);
154 return $file unless ref $file;
155 return -ENOENT
() unless $file->exists();
157 $file->remove($self);
163 my ($self, $filename) = @_;
164 my $file = $self->find_file($filename);
165 return $file unless ref $file;
166 return -ENOENT
() unless $file->exists();
167 return -ENOTDIR
() unless $file->is_directory();
168 return -ENOTEMPTY
() unless $file->is_empty();
169 $file->remove($self);
175 my ($self, $filename, $linkname) = @_;
176 my $file = $self->find_file($linkname);
177 return $file unless ref $file;
178 return -EEXIST
() if $file->exists();
179 $file->make_symlink($filename);
180 $file->create($self);
186 my ($self, $filename, $newname) = @_;
187 my $file = $self->find_file($filename);
188 my $newn = $self->find_file($newname);
189 return $file unless ref $file;
190 return $newn unless ref $newn;
191 return -EEXIST
() if $newn->exists();
192 $file->moved($newname);
193 $self->find_file($newname, $file);
199 my ($self, $filename, $linkname) = @_;
200 my $file = $self->find_file($linkname);
201 my $dest = $self->find_file($filename);
202 return $file unless ref $file;
203 return $dest unless ref $dest;
204 return -EEXIST
() if $file->exists();
205 return -EPERM
() unless $dest->exists();
206 $file->create($self);
207 return $file->make_hardlink($dest);
212 my ($self, $filename, $mode) = @_;
213 my $file = $self->find_file($filename);
214 return $file unless ref $file;
215 return $file->chmod($mode);
220 my ($self, $filename, $uid, $gid) = @_;
221 my $file = $self->find_file($filename);
222 return $file unless ref $file;
223 return $file->chown($uid, $gid);
228 my ($self, $filename, $offset) = @_;
229 my $file = $self->find_file($filename);
230 return $file unless ref $file;
231 return -ENOENT
() unless $file->exists();
232 return $file->truncate();
237 my ($self, $filename, $atime, $mtime) = @_;
238 my $file = $self->find_file($filename);
239 return $file unless ref $file;
240 return -ENOENT
() unless $file->exists();
241 $file->set_amtime($atime, $mtime);
247 my ($self, $filename, $flags) = @_;
248 my $file = $self->find_file($filename);
249 return $file unless ref $file;
250 return -ENOENT
() unless $file->exists();
258 my ($self, $filename, $size, $offset) = @_;
259 my $file = $self->find_file($filename);
260 return $file unless ref $file;
261 return $file->read($size, $offset);
266 my ($self, $filename, $buffer, $offset) = @_;
267 my $file = $self->find_file($filename);
268 return $file unless ref $file;
269 return $file->write($buffer, $offset);
275 return (0, 1, 1, 1, 1, 1);
276 # return $namelen, $files, $files_free, $blocks, $blocks_avail, $blocksize
282 my ($self, $filename) = @_;
283 my $file = $self->find_file($filename);
284 $file->flush() if ref $file;
290 my ($self, $filename, $flags) = @_;
291 my $file = $self->find_file($filename);
293 $file->flush() if ref $file;
299 my ($self, $filename, $flags) = @_;
300 my $file = $self->find_file($filename);
301 $file->flush() if ref $file;
307 my ($self, $filename, $ext_name, $ext_val, $flags) = @_;
308 my $file = $self->find_file($filename);
309 return $file unless ref $file;
310 return -EEXIST
() if ($flags & XATTR_CREATE
()) && exists $file->{'xattr'}->{$ext_name};
311 # return -ENOATTR() if ($flags & XATTR_REPLACE()) && exists $file->{'xattr'}->{$ext_name};
312 $ext_val = 0 unless defined $ext_val;
313 $file->{'xattr'}->{$ext_name} = $ext_val;
319 my ($self, $filename, $ext_name) = @_;
320 my $file = $self->find_file($filename);
321 return $file unless ref $file;
322 # return -ENOATTR() unless exists $file->{'xattr'}->{$ext_name};
323 return $file->{'xattr'}->{$ext_name};
328 my ($self, $filename) = @_;
329 my $file = $self->find_file($filename);
330 return $file unless ref $file;
331 return (keys %{$file->{'xattr'}}), 0;
334 # removexattr callback
335 sub fuse_removexattr
{
336 my ($self, $filename, $ext_name) = @_;
337 delete $file->{'xattr'}->{$ext_name};
341 # class representing a single file or directory
342 package Fuse
::InMemory
::File
;
343 use Errno qw
/ :POSIX /;
346 # create a new instance with sensible defaults
352 'exists' => 1, # zero if only a temporary file object
353 'data' => \
$tmp, # reference to empty scalar var for contents of the file
354 'attr' => [ # file attributes
355 0, # device number of file system
357 S_IFREG
|0644, # file mode
358 1, # number of hard links to this file
359 $>, # user ID of owner
360 $)+0, # group ID of owner
361 0, # device identifier for special nodes
369 'xattr' => { # extended attributes
373 while(my $a = shift) {
377 $self->{'attr'}->[2] = $b;
382 return bless $self, $class;
385 # read $size bytes from $offset
387 my ($self, $size, $offset) = @_;
388 return '' if $offset >= $self->{'attr'}->[7];
389 my $diff = $offset + $size - $self->{'attr'}->[7];
390 $size -= $diff if $diff > 0;
391 if($self->{'reader'}) {
393 return $self->{'reader'}->($self, $size, $offset);
395 return substr(${$self->{'data'}}, $offset, $size);
399 # write $buffer to $offset in this file
401 my ($self, $buffer, $offset) = @_;
402 if($self->{'writer'}) {
404 return $self->{'writer'}->($self, $buffer, $offset);
406 my $extend = $offset - length(${$self->{'data'}});
407 ${$self->{'data'}} .= (' ' x
$extend) if $extend > 0;
408 substr(${$self->{'data'}}, $offset, length($buffer)) = $buffer;
409 $self->set_size(length(${$self->{'data'}}));
410 return length($buffer);
416 my ($self, $buffer, $offset) = @_;
417 if($self->{'truncater'}) {
419 return $self->{'truncater'}->($self);
421 if($self->{'attr'}->[3] <= 1) {
422 # if only one link, create new scalar for contents, saves memory
424 $self->{'data'} = \
$tmp;
426 substr(${$self->{'data'}},0) = '';
436 if($self->{'flusher'}) {
438 $self->{'flusher'}->($self);
442 # return the path of this file
445 return $self->{'path'};
448 # return the name of this file only
451 my $p = $self->{'path'};
456 # return true if this file actually exists
459 return $self->{'exists'};
462 # set the size of this file to $size
464 my ($self, $size) = @_;
465 $self->{'attr'}->[7] = $size;
470 # set the mode of this file to $mode
472 my ($self, $mode) = @_;
473 $self->{'attr'}->[2] = $mode;
478 # set the permissions of this file to $mode
480 my ($self, $mode) = @_;
481 $self->{'attr'}->[2] = ($self->{'attr'}->[2] & S_IFMT
()) | $mode;
486 # set owner of this file to $uid and group to $gid
488 my ($self, $uid, $gid) = @_;
489 $self->{'attr'}->[4] = $uid;
490 $self->{'attr'}->[5] = $gid;
495 # set atime to $atime and mtime to $mtime
497 my ($self, $atime, $mtime) = @_;
498 $self->{'attr'}->[8] = $atime;
499 $self->{'attr'}->[9] = $mtime;
503 # make this file into a symlink pointing to $dest
505 my ($self, $dest) = @_;
506 $self->{'attr'}->[2] = S_IFLNK
|0777;
507 $self->{'symlink'} = $dest;
511 # make this file into a hard link pointing to $dest
512 # destination must be within the same file system
514 my ($self, $dest) = @_;
515 $self->{'attr'} = $dest->{'attr'};
516 $self->{'data'} = $dest->{'data'};
517 $self->{'xattr'} = $dest->{'xattr'};
518 $self->{'attr'}->[3]++;
522 # return true if this file is actually a symlink
525 return $self->{'symlink'} if ($self->{'attr'}->[2] & S_IFLNK
) != 0;
529 # return true if this file is a directory
532 return ($self->{'attr'}->[2] & S_IFDIR
) != 0;
535 # return true if this is a directory and also empty
538 return -ENOTDIR
() unless $self->is_directory();
540 $self->{'contents'} = {} unless exists $self->{'contents'};
541 return 0 if scalar(keys %{$self->{'contents'}}) > 0;
545 # remove the child $child from this directory
547 my ($self, $child) = @_;
548 return -ENOTDIR
() unless $self->is_directory();
549 $self->{'contents'} = {} unless exists $self->{'contents'};
550 delete $self->{'contents'}->{$child->filename()};
555 # get the file identified by name $file within this directory
557 my ($self, $file) = @_;
558 return undef unless $self->is_directory();
560 $self->{'contents'} = {} unless exists $self->{'contents'};
561 my $ret = $self->{'contents'}->{$file};
562 return -ENOENT
() unless defined $ret;
566 # add the file $file to this directory
568 my ($self, $file) = @_;
569 $self->{'contents'}->{$file->filename()} = $file;
570 $file->{'parent'} = $self;
574 # return the list of this directory's contents plus . and ..
577 return undef unless $self->is_directory();
579 return '.', '..', keys %{$self->{'contents'}}, 0;
582 # create this file and put it into the file system $fs
584 my ($self, $fs) = @_;
585 $fs->find_file($self->{'path'}, $self);
586 $self->{'exists'} = 1;
591 my ($self, $fs) = @_;
592 $self->{'exists'} = 0;
593 $self->{'attr'}->[3]--;
594 $self->{'parent'}->remove_child($self);
595 $self->truncate() if $self->{'attr'}->[3] < 1;
599 # do stuff necessary after this file was moved about in the file system, $name contains the new path
601 my ($self, $name) = @_;
602 $self->{'parent'}->remove_child($self);
603 $self->{'path'} = $name;
607 # return the attributes of this file
611 my @ret = @
{$self->{'attr'}};
612 if($self->is_directory()) {
613 # nlink is 1 + number of subdirectories for directories
614 $ret[3] += scalar(keys %{$self->{'contents'}});
624 Fuse::InMemory - extensible RAM file system
629 my $fs = Fuse::InMemory->new();
634 This is a complete RAM file system written in perl, using Fuse.
635 By default, all file content is kept in scalar buffers. Attributes
636 and extended attributes are stored separately, and mostly
637 supported. This means you can use chmod, chown, setfacl and all
638 that on files in this file system, although the usual restrictions
639 on permissions apply - i.e. for example, if you disable permission
640 checking, you can create a setuid-root executable as a normal user,
641 but when you run it, it won't run with root privileges.
643 =head2 EXPORTED SYMBOLS
651 Creates a new RAM file system.
653 my $fs = Fuse::InMemory->new();
655 =head2 INSTANCE METHODS
657 =head3 main([arguments])
659 Configures the file system and enters the Fuse main loop.
660 When run without arguments, options will be taken from the
661 command line. This includes the mount point.
665 It is also possible to pass a list of arguments to main, to be
666 used instead of the command line:
672 $fs->main('/mnt', '-o', '');
674 =head3 add_magic(file,reader,writer,truncater,flusher)
676 Add magic to a file. The file must already exist and is
677 identified by its full path within the mounted file system.
678 All remaining arguments are coderefs.
682 Daniel Fischer, E<lt>df@erinye.comE<gt>