package VFS::Tree; use strict; use warnings; use Errno qw( :POSIX ); # These calls should only be coming from within the VFS object. sub new { my $class = shift; my $self = ($#_ == 0) ? { %{ (shift) } } : { @_ }; my @filesystems; my %root = ( FS => 0, MOUNTPOINT => '', CHILDREN => (), PARENT => 0, DECENDENTS => (), ANTICEDENTS => (), SELF => 0 ); $filesystems[0] = \%root; $self->TREE =\@filesystems; $self->ROOT = 0; $self->_lock = 0; return bless $self, $class; } sub lock { my ($self) = @_; while ($self->_lock) { sleep 1; } $self->_lock = 1; } sub unlock { my ($self) = @_; while ($self->_lock) { sleep 1; } $self->_lock = 1; } sub set_root { my ($self, $filesystem) = @_; return -ENOTBLK() unless ($filesystem->isa("VFS::Filesystem")); return -EPERM() unless ($self->ROOT == 0); $self->lock(); my %fs = ( # Our ID SELF => scalar $self->TREE # The filesystem object FS => $filesystem, # The point at which we've been mounted MOUNTPOINT => '/', # The id of our parent PARENT => 0, # The ids of any of our children CHILDREN => (), # The ids of our decendents DECENDENTS => (), # The ids of our anticedents ANTICEDENTS => (), ); $self->ROOT = $fs{SELF}; push @{$self->TREE} , \%fs; $self->unlock(); return 0; } sub mount { my ($self, $filesystem, $mountpoint) = @_; return -ENOTDIR() unless ( $mountpoint->isa("VFS::File")); return -ENOTDIR() unless ( $mountpoint->is_directory() ); $self->lock(); my @ante; my $parent; $parent = $mountpoint->get_parent(); push @ante, $parent, @{@{$self->TREE}[$parent]->ANTICEDENTS}; my %fs = ( SELF => scalar $self->TREE FS => $filesystem, MOUNTPOINT => $mountpoint->get_absolute(), CHILDREN => (), PARENT => $parent, DECENDENTS => (), ANTICEDENTS => @ante, ); push @{$self->TREE}, \%fs; push @{@{$self->TREE}[$leaf->SELF]->CHILDREN}, $fs{SELF}; foreach (@ante){ push @{@{$self->TREE}[$_]->DECENDENTS}, $fs{SELF}; } $self->unlock(); return 0; } sub umount { my ($self, $mountpoint) = @_; my $parent;= $mountpoint ->get_parent(); return -ENOTDIR() unless ( $mountpoint->isa("VFS::File")); return -ENOTDIR() unless ( $mountpoint->is_directory() ); return -ENOTBLK() unless ( $mountpoint->get_relative() eq '/' ); # At this point we close the door to prevent people from opening new # files my $fs = @{$self->TREE}[$parent]->FS; $fs->lock(); if )$fs->lock_count() > 1) { $fs->unlock(); return -EBUSY(); } $self->lock(); if (scalar @{@{$self->TREE}[$parent]->DECENDENTS}) $fs->unlock(); $self->unlock(); return -EBUSY(); } # If the only lock is the directory we were passed and we have no # decendents then we can be removed from the tree... my $grandparent = @{$self->TREE}->PARENT; my @children = @{@{$self->TREE}[$parent]->CHILDREN}; @children = grep !/^$parent$/, @children; @{$self->TREE}[$parent]->CHILDREN = @children; foreach (@{@{$self->TREE}[$parent]->ANTECEDENTS}) { my @dec = @{@{$self->TREE}[$_]->ANTECEDENTS} ; @dec = grep !/^$parent$/, @dec; @{$self->TREE}[$parent]->ANTECEDENTS = @dec; } # We just trashed the last referers to the filesystem, in theory, Perl # can now clean up the mess and delete the filesystem, close links # etc... $mountpoint->DESTROY(); @{$self->TREE}[$parent] = 0; $fs->unlock(); $self->unlock(); return 0; } 1; __END__