Fuse-TM
view release on metacpan or search on metacpan
lib/Fuse/TM.pm view on Meta::CPAN
if (ref($o{tm}) !~ /^TM::(Materialized|Synchronizable|Serializable)/);
my $now=$o{tm}->mtime||time;
my $self=
{
tm=>$o{tm},
output=>$o{output},
outputdriver=>$o{outputdriver},
debug=>$o{debug},
rw=>$o{rw},
autocreate=>($o{rw} && $o{autocreate}),
hide=>$o{hide},
mtime=>$now,
};
return bless $self,$class;
}
=pod
=item mount()
$tmfs->mount("/some/mount/dir");
Calls Fuse to mount the filesystem at the specified mount point, which must
be an existing directory. This function blocks until the filesystem is
unmounted. If read-write mode is enabled, then the updated map will be saved
on unmount. mount will croak() if problems are encountered.
=back
=cut
sub mount
{
my ($self,$mountpt)=@_;
Carp::croak("invalid object argument\n") if (!ref($self) || !$self->isa(__PACKAGE__));
Carp::croak("invalid mountpoint \"$mountpt\"\n")
if (!$mountpt || !-d $mountpt);
$self->{tm}->sync_in;
$self->debug("done reading, starting indexing\n");
new TM::Index::Match($self->{tm});
new TM::Index::Characteristics($self->{tm});
$self->debug("building fs datastructure\n");
$self->maptofs;
$self->{mountpoint}=$mountpt;
$self->debug("ready.\nmounting on $mountpt\n");
$mountpt=realpath($mountpt); # absolutize the mountpoint, for symlink lookups later
Fuse::main(mountpoint=>$mountpt,
debug=>$self->{debug}>1?1:0,
getattr=>sub { return $self->tm_getattr(@_); },
getdir=>sub { return $self->tm_getdir(@_); },
read=>sub { return $self->tm_read(@_); },
readlink=>sub { return $self->tm_readlink(@_); },
$self->{rw}?
( write=>sub { return $self->tm_write(@_);},
release=>sub { return $self->tm_release(@_); },
chmod=>sub { return $self->tm_dummy(@_); },
chown=>sub { return $self->tm_dummy(@_); },
utime=>sub { return $self->tm_dummy(@_); },
truncate=>sub { return $self->tm_truncate(@_); },
rename=>sub { return $self->tm_rename(@_); },
mkdir=>sub { return $self->tm_mkdir(@_); },
symlink=>sub { return $self->tm_symlink(@_); },
unlink=>sub { return $self->tm_unlink(@_); },
rmdir=>sub { return $self->tm_rmdir(@_); },
mknod=>sub { return $self->tm_mknod(@_); }
):()
);
if ($self->{rw})
{
$self->debug("unmounting, r/w-mode\n");
if (keys %{$self->{dirty}})
{
my $d=$self->{outputdriver};
if ($d)
{
$self->debug("changing output driver to '$d'");
Class::Trait->apply($self->{tm},$d);
}
my $n=$self->{output};
if ($n)
{
$self->debug("changing output to '$n'\n");
$self->{tm}->url($n);
}
$self->debug("writing map out\n");
$self->{tm}->sync_out;
$self->debug("done.\n");
}
else
{
$self->debug("no dirty data needs syncing, done.\n");
}
}
else
{
$self->debug("unmounted, r/o-mode, done.\n");
}
return 0;
}
# converts map content into fs-like structure, stores resulting data in self->{fsdata}
# this conversion of the whole map into an fs-like tree happens because:
# . repeating tm->match calls is prohibitively expensive (regardless of tm indices)
# . we need to keep track of assocs anyway, as they are not externally addressable
# . the collection of topic/assoc data needs to be done for every lookup
# datastructure: /topics/tn/thingie/whatever => hashref or arrayref
# hashref: this is a dir, hash: files as keys
# arrayref: this is a leaf file, array: [value(ref),tmlid,stat]
# for dirs we use a dummy "." file with value = dir content arrayref.
# tmlid if a/v is name of corresponding toplet/assertion, stat is statarray
# for assocs: /assocs/atype/number/role/players, otherwise the same
sub maptofs
{
my ($self)=@_;
lib/Fuse/TM.pm view on Meta::CPAN
$inv="type";
}
# typedir
elsif (@rest==1)
{
return -ENOTEMPTY() if (keys(%{$obj})!=1); # the . entry
}
else
{
$self->debug("dud rmdir call, path $path");
return -EPERM(); # something bad happened
}
# get rid of involvements, not for typedir-removal: involvements apply
# only to specific assocs
if ($inv)
{
my ($compare,$node);
if ($inv eq "type")
{
$node=$self->{fsdata}->{$td}->{$self->lid2name($parent->{"."}->[1])}; # localname is assoc tag,
# parent (or .type child) has the type lid
$compare=$obj->{"."}->[1];
}
else
{
$node=$self->{fsdata}->{$td}->{$localname}; # localname is role, which is the topic we look for
$compare=$parent->{"."}->[1];
}
&Carp::croak("internal inconsistency: assoc has no type name ".Dumper($obj))
if (ref($node) ne "HASH");
map { delete $node->{"involved"}->{$_}
if (/^\d:$inv$/ && $node->{"involved"}->{$_}->[1] eq $compare); }
(keys %{$node->{"involved"}});
$self->scandir($node->{"involved"});
}
# common code: nuke the fs object, rescan parent dir
delete $parent->{$localname};
$self->scandir($parent);
return 0;
}
elsif ($rest[-1] eq $td)
{
# fixme: lowprio: should i allow a dummy removal of the aspect dirs and files?
# that way, one could safely rm -rf and a stray rmdir would
# only topics< themselves are removable, none of their aspect-dirs
if (@rest eq 2 && grep($localname eq $_,qw(instance isa name oc involved)))
{
$self->debug("dummy rmdir of $path");
return 0;
}
return -EPERM() if (@rest!=1);
return -EBUSY() if (!$self->remove_rename($localname,undef));
return 0;
}
return -EPERM();
}
# for utime, chmod, chown and other, nonimplemented but commonly used functions
sub tm_dummy
{
my ($self)=@_;
Carp::croak("invalid object argument\n") if (!ref($self) || !$self->isa(__PACKAGE__));
$self->debug("dummy called with args ".Dumper(\@_)."\n");
return 0;
}
=pod
=head1 LIMITATIONS
=head2 Filesystem Attributes
File ownership is not modelled at all: all files and dirs belong to uid 0, root.
Permissions are not modelled fully: in read-only mode all files are mode 0444 and
dirs 0555. In read-write mode all files are mode 0644 and dirs 0755.
Mode or ownership changes are not supported.
=head2 Write Support
Until the filesystem is unmounted any modifications are kept in memory only.
Overwriting files is only allowed for names, occurrences and subject indicators.
Attempts to write to other files will result in EPERM errors.
Creating new files or renaming them is only allowed for names and occurrences.
Attempting others will result in an EPERM. Renaming works for names or occurrences
if they stay with the same topic, or for topics. Nothing else can be renamed and
EFAULT errors will result if you try.
Renaming of topics is possible (and all involved others are updated accordingly).
Neither fixed topic characteristic dirs (e.g. oc, name, isa) nor
association instance dirs can be renamed.
(Re)Naming something with an unacceptable name will cause ENOENT errors.
Symbolic links cannot point outside the current map's filesystem,
with the sole exception being a subject locator URI symlink.
Removing a topic is not possible if it is used as an association type
or association role: EBUSY will be the result if you try. To remove an
association type topic first all association instances must be removed.
Removing of a topic clears up all involvements: if used as a scope the scope
will be changed to universal, if used as a type the type will be cleared, if
used as role player then this player vanishes.
=head1 SEE ALSO
L<TM(3)>, L<tmfs(1)>
=head1 AUTHOR
Alexander Zangerl, <alphazulu@cpan.org>
=head1 COPYRIGHT AND LICENSE
( run in 1.293 second using v1.01-cache-2.11-cpan-71847e10f99 )