# Copyright © 2016 by Yaroslav Kolomiiets
package Plan9;
use Symbol;
sub fsinit(*) {
my $io = Symbol::qualify_to_ref(shift, caller);
my (%fs, $fs);
$fs = \%fs;
$fs->{io} = $io;
$fs->{ref} = 1;
$fs->{freefid} = [];
fsversion($fs, 8192, VERSION9P)
or return undef;
return $fs;
}
sub fsmount(*;$) {
my $io = Symbol::qualify_to_ref(shift, caller);
my $aname = shift;
my ($fs, $fid, $uname);
$uname = $ENV{USER} || $ENV{LOGNAME} || $ENV{user} || 'none';
$fs = fsinit($io);
if(!defined $fs){
return undef;
}
$fid = fsattach($fs, undef, $uname, $aname);
fssetroot($fs, $fid);
return $fs;
}
sub fsunmount($) {
my $fs = shift;
fsclose($fs->{root});
$fs->{root} = undef;
_fsdecref($fs);
}
sub _fsdecref($) {
my $fs = shift;
lock $fs;
$fs->{ref}--;
if($fs->{ref} == 0){
close($fs->{io});
while(@{$fs->{freefid}} > 0){
pop(@{$fs->{freefid}})->{fs} = undef;
}
}
}
sub _fsgetfid($) {
my $fs = shift;
my $f;
do {
lock $fs;
if(@{$fs->{freefid}} == 0){
push(@{$fs->{freefid}}, {fid=>$fs->{nextfid}++, fs=>$fs});
}
$f = pop(@{$fs->{freefid}});
$fs->{ref}++;
};
$f->{offset} = 0;
$f->{mode} = -1;
$f->{iounit} = 0;
$f->{qid} = {path=>0, vers=>0, type=>0};
return $f;
}
sub _fsputfid($) {
my $fid = shift;
my $fs;
$fs = $fid->{fs};
do {
lock $fs;
push(@{$fs->{freefid}}, $fid);
};
_fsdecref($fs);
}
sub _fsrpc($\%) {
my ($fs, $t) = @_;
my $r;
if($::chatty9pclient){
print STDERR sprintf("-> %s\n", fcallstr(%$t));
}
writefcall($fs->{io}, %$t)
or die "writefcall: $@";
$r = readfcall($fs->{io}, $fs->{msize});
if(!defined $r){
return undef;
};
if($::chatty9pclient){
print STDERR sprintf("<- %s\n", fcallstr(%$r));
}
if($r->{type} == Rerror){
$@ = $r->{ename};
return undef;
}
if($r->{type} != $t->{type}+1){
$@ = sprintf("packet type mismatch -- tx %d rx %d", $t->{type}, $r->{type});
return undef;
}
return $r;
}
sub fsversion($;$$) {
my ($fs, $msize, $version) = @_;
my ($t, $r);
$fs->{msize} = $msize;
$t = {
type=>Tversion,
tag=>NOTAG,
version=>$version,
msize=>$msize,
};
$r = _fsrpc($fs, %$t)
or return undef;
if($r->{msize} < $fs->{msize}){
$fs->{msize} = $r->{msize};
}
$fs->{version} = $r->{version};
return $r->{msize};
}
sub fsattach($;$$$$) {
my ($fs, $afid, $uname, $aname) = @_;
my ($t, $r, $fid, $err);
$fid = _fsgetfid($fs);
$t = {
type=> Tattach,
tag=> 0,
fid=> $fid->{fid},
afid=> defined $afid? $afid->{fid} : NOFID,
uname=> $uname,
aname=> $aname,
};
$r = _fsrpc($fs, %$t);
if(!defined $r){
_fsputfid($fid);
return undef;
}
$fid->{qid} = $r->{qid};
return $fid;
}
sub fssetroot($$) {
my ($fs, $fid) = @_;
$fs->{root} = $fid;
}
sub fswalk($;@) {
my ($fid, @wname) = @_;
my ($t, $r, $wfid);
$wfid = _fsgetfid($fid->{fs});
$t = {
type => Twalk,
tag => 0,
fid => $fid->{fid},
newfid => $wfid->{fid},
wname => \@wname,
};
$r = _fsrpc($fid->{fs}, %$t);
if(!defined $r){
_fsputfid($wfid);
return undef;
}
if(@{$t->{wname}} != @{$r->{wqid}}){
if(@{$r->{wqid}} > 0){
$name = join('/', @{$t->{wname}}[0..int(@{$r->{wqid}})]);
}
$@ = sprintf("file '%s' not found", $name);
_fsputfid($wfid);
return undef;
}
if(@{$r->{wqid}} == 0){
%{$wfid->{qid}} = %{$fid->{qid}};
}else{
$wfid->{qid} = pop @{$r->{wqid}};
}
return $wfid;
}
sub fswalkp($$) {
my ($fid, $name) = @_;
my @wname;
@wname = grep {$_ ne '' and $_ ne '.'} split('/', $name);
return fswalk($fid, @wname);
}
sub fsfcreate($$$$)
{
my ($fid, $name, $mode, $perm) = @_;
my ($t, $r);
$t = {
type=> Tcreate,
tag=> 0,
fid=> $fid->{fid},
name=> $name,
mode=> $mode,
perm=> $perm,
};
$r = _fsrpc($fid->{fs}, %$t);
if(!defined $r){
return -1;
}
$fid->{mode} = $mode;
$fid->{qid} = $r->{qid};
$fid->{iounit} = $r->{iounit};
return 0;
}
sub fscreate($$$$) {
my ($fs, $name, $mode, $perm) = @_;
my (@p, $dir, $elem, $fid);
@p = split('/', $name);
$elem = pop @p;
$dir = join('/', @p);
$fid = fswalkp($fs->{root}, $dir);
if(!defined $fid){
return undef;
}
if(fsfcreate($fid, $elem, $mode, $perm) < 0){
fsclose($fid);
return undef;
}
return $fid;
}
sub fsfopen($$) {
my ($fid, $mode) = @_;
my ($t, $r);
$t = {
type=> Topen,
tag=> 0,
fid=> $fid->{fid},
mode=> $mode,
};
$r = _fsrpc($fid->{fs}, %$t);
if(!defined $r){
return -1;
}
$fid->{mode} = $mode;
$fid->{qid} = $r->{qid};
$fid->{iounit} = $r->{iounit};
return 0;
}
sub fsopen($$$) {
my ($fs, $name, $mode) = @_;
my ($fid);
$fid = fswalkp($fs->{root}, $name);
if(!defined $fid){
return undef;
}
if(fsfopen($fid, $mode) < 0){
fsclose($fid);
return undef;
}
return $fid;
}
sub fsiounit($) {
my ($fid) = @_;
if($fid->{iounit} > 0){
return $fid->{iounit};
}
return $fid->{fs}->{msize} - IOHDRSZ;
}
sub
fsclose($)
{
my $fid = shift;
my ($t, $r);
if(!defined $fid){
return;
}
$t = {
type=>Tclunk,
tag=>0,
fid=>$fid->{fid}
};
_fsrpc($fid->{fs}, %$t);
_fsputfid($fid);
}
sub fsfremove($) {
my $fid = shift;
my ($t, $r);
if(!defined $fid){
return;
}
$t = {
type=>Tremove,
tag=>0,
fid=>$fid->{fid}
};
$r = _fsrpc($fid->{fs}, %$t);
if(!defined $r){
return -1;
}
_fsputfid($fid);
return 0;
}
sub fsremove($$) {
my $fs = shift;
my $name = shift;
my $fid;
$fid = fswalkp($fs->{root}, $name);
if(!defined $fid){
return -1;
}
return fsfremove($fid);
}
sub fspwrite($$$) {
my ($fid, $data, $offset) = @_;
my ($t, $r);
$t = {
type => Twrite,
tag => 0,
fid => $fid->{fid},
data => $data,
offset => $offset,
};
$r = _fsrpc($fid->{fs}, %$t);
if(!defined $r){
return -1;
}
return $r->{count};
}
sub fswrite($$) {
my ($fid, $data) = @_;
my ($offset, $count);
do {
lock $fid;
$offset = $fid->{offset};
};
$count = fspwrite($fid, $data, $offset);
if($count > 0){
lock $fid;
$fid->{offset} += $count;
}
return $count;
}
sub fsprintf($@) {
my $fid = shift;
my $fmt = shift;
return fswrite($fid, sprintf($fmt, @_));
}
sub fspread($$$) {
my ($fid, $count, $offset) = @_;
my ($t, $r, $data);
$t = {
type => Tread,
tag => 0,
fid => $fid->{fid},
count => $count,
offset => $offset,
};
$r = _fsrpc($fid->{fs}, %$t);
if(!defined $r){
return undef;
}
return $r->{data};
}
sub fsread($$) {
my ($fid, $count) = @_;
my ($data, $offset);
do {
lock $fid;
$offset = $fid->{offset};
};
$data = fspread($fid, $count, $offset);
if(!defined $data){
return undef;
}
do {
lock $fid;
$fid->{offset} += length($data);
};
return $data;
}
sub fsdirfstat($) {
my ($fid) = @_;
my ($t, $r);
$t = {
type => Tstat,
tag => 0,
fid => $fid->{fid},
};
$r = _fsrpc($fid->{fs}, %$t);
if(!defined $r){
return undef;
}
return $r->{stat};
}
sub fsdirstat($$) {
my ($fs, $name) = @_;
my ($fid, $d);
$fid = fswalkp($fs->{root}, $name);
if(!defined $fid){
return -1;
}
$d = fsdirfstat($fid);
fsclose($fid);
return $d;
}
sub fsdirfwstat($\%) {
my ($fid, $d) = @_;
my ($t, $r);
$t = {
type => Twstat,
tag => 0,
fid => $fid->{fid},
stat => $d,
};
$r = _fsrpc($fid->{fs}, %$t);
if(!defined $r){
return -1;
}
return 0;
}
sub fsdirwstat($$\%) {
my ($fs, $name, $d) = @_;
my ($fid, $n);
$fid = fswalkp($fs->{root}, $name);
if(!defined $fid){
return -1;
}
$n = fsdirfwstat($fid, %$d);
fsclose($fid);
return $n;
}
sub fsdirread($) {
my ($fid) = @_;
my ($dirmax, $m);
$dirmax = $fid->{fs}->{msize};
$m = fsread($fid, $dirmax);
if(!defined $m){
return ();
}
return unpackdir($m);
}
sub fsdirreadall($) {
my ($fid) = @_;
my (@d, @dall);
for(;;){
@d = fsdirread($fid);
if(@d == 0){
last;
}
push(@dall, @d);
}
return @dall;
}
1;
|