ref: 3866717cbb020199d58171c1c0cdd7382a74ee82
dir: /emu/port/sysfile.c/
#include "dat.h"
#include "fns.h"
#include "error.h"
#include "kernel.h"
static int
growfd(Fgrp *f, int fd)
{
int n;
Chan **nfd, **ofd;
if(fd < f->nfd)
return 0;
n = f->nfd+DELTAFD;
if(n > MAXNFD)
n = MAXNFD;
if(fd >= n)
return -1;
nfd = malloc(n*sizeof(Chan*));
if(nfd == nil)
return -1;
ofd = f->fd;
memmove(nfd, ofd, f->nfd*sizeof(Chan *));
f->fd = nfd;
f->nfd = n;
free(ofd);
return 0;
}
static int
newfd(Chan *c)
{
int i;
Fgrp *f = up->env->fgrp;
lock(&f->l);
for(i=f->minfd; i<f->nfd; i++)
if(f->fd[i] == 0)
break;
if(i >= f->nfd && growfd(f, i) < 0){
unlock(&f->l);
exhausted("file descriptors");
return -1;
}
f->minfd = i + 1;
if(i > f->maxfd)
f->maxfd = i;
f->fd[i] = c;
unlock(&f->l);
return i;
}
Chan*
fdtochan(Fgrp *f, int fd, int mode, int chkmnt, int iref)
{
Chan *c;
c = 0;
lock(&f->l);
if(fd<0 || f->maxfd<fd || (c = f->fd[fd])==0) {
unlock(&f->l);
error(Ebadfd);
}
if(iref)
incref(&c->r);
unlock(&f->l);
if(chkmnt && (c->flag&CMSG))
goto bad;
if(mode<0 || c->mode==ORDWR)
return c;
if((mode&OTRUNC) && c->mode==OREAD)
goto bad;
if((mode&~OTRUNC) != c->mode)
goto bad;
return c;
bad:
if(iref)
cclose(c);
error(Ebadusefd);
return nil;
}
long
kchanio(void *vc, void *buf, int n, int mode)
{
int r;
Chan *c;
c = vc;
if(waserror())
return -1;
if(mode == OREAD)
r = devtab[c->type]->read(c, buf, n, c->offset);
else
r = devtab[c->type]->write(c, buf, n, c->offset);
lock(&c->l);
c->offset += r;
unlock(&c->l);
poperror();
return r;
}
int
openmode(ulong o)
{
if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC))
error(Ebadarg);
o &= ~(OTRUNC|OCEXEC|ORCLOSE);
if(o > OEXEC)
error(Ebadarg);
if(o == OEXEC)
return OREAD;
return o;
}
static void
fdclose(Fgrp *f, int fd)
{
int i;
Chan *c;
lock(&f->l);
c = f->fd[fd];
if(c == 0) {
/* can happen for users with shared fd tables */
unlock(&f->l);
return;
}
f->fd[fd] = 0;
if(fd == f->maxfd)
for(i=fd; --i>=0 && f->fd[i]==0; )
f->maxfd = i;
if(fd < f->minfd)
f->minfd = fd;
unlock(&f->l);
cclose(c);
}
int
kchdir(char *path)
{
Chan *c;
Pgrp *pg;
if(waserror())
return -1;
c = namec(path, Atodir, 0, 0);
pg = up->env->pgrp;
cclose(pg->dot);
pg->dot = c;
poperror();
return 0;
}
int
kfgrpclose(Fgrp *f, int fd)
{
if(waserror())
return -1;
/*
* Take no reference on the chan because we don't really need the
* data structure, and are calling fdtochan only for error checks.
* fdclose takes care of processes racing through here.
*/
fdtochan(f, fd, -1, 0, 0);
fdclose(f, fd);
poperror();
return 0;
}
int
kclose(int fd)
{
return kfgrpclose(up->env->fgrp, fd);
}
int
kcreate(char *path, int mode, ulong perm)
{
int fd;
volatile struct { Chan *c; } c;
c.c = nil;
if(waserror()) {
cclose(c.c);
return -1;
}
openmode(mode&~OEXCL); /* error check only; OEXCL okay here */
c.c = namec(path, Acreate, mode, perm);
fd = newfd(c.c);
if(fd < 0)
error(Enofd);
poperror();
return fd;
}
int
kdup(int old, int new)
{
int fd;
Chan *oc;
Fgrp *f = up->env->fgrp;
volatile struct { Chan *c; } c;
if(waserror())
return -1;
c.c = fdtochan(up->env->fgrp, old, -1, 0, 1);
if(c.c->qid.type & QTAUTH)
error(Eperm);
fd = new;
if(fd != -1) {
lock(&f->l);
if(fd < 0 || growfd(f, fd) < 0) {
unlock(&f->l);
cclose(c.c);
error(Ebadfd);
}
if(fd > f->maxfd)
f->maxfd = fd;
oc = f->fd[fd];
f->fd[fd] = c.c;
unlock(&f->l);
if(oc != 0)
cclose(oc);
}
else {
if(waserror()) {
cclose(c.c);
nexterror();
}
fd = newfd(c.c);
if(fd < 0)
error(Enofd);
poperror();
}
poperror();
return fd;
}
int
kfstat(int fd, uchar *buf, int n)
{
volatile struct { Chan *c; } c;
c.c = nil;
if(waserror()) {
cclose(c.c);
return -1;
}
c.c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
devtab[c.c->type]->stat(c.c, buf, n);
poperror();
cclose(c.c);
return n;
}
char*
kfd2path(int fd)
{
Chan *c;
char *s;
if(waserror())
return nil;
c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
s = nil;
if(c->name != nil){
s = malloc(c->name->len+1);
if(s == nil){
cclose(c);
error(Enomem);
}
memmove(s, c->name->s, c->name->len+1);
cclose(c);
}
poperror();
return s;
}
int
kfauth(int fd, char *aname)
{
Chan *c, *ac;
if(waserror())
return -1;
validname(aname, 1);
c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
if(waserror()){
cclose(c);
nexterror();
}
ac = mntauth(c, aname);
/* at this point ac is responsible for keeping c alive */
poperror(); /* c */
cclose(c);
if(waserror()){
cclose(ac);
nexterror();
}
fd = newfd(ac);
if(fd < 0)
error(Enofd);
poperror(); /* ac */
poperror();
return fd;
}
int
kfversion(int fd, uint msize, char *vers, uint arglen)
{
int m;
Chan *c;
if(waserror())
return -1;
/* check there's a NUL in the version string */
if(arglen==0 || memchr(vers, 0, arglen)==0)
error(Ebadarg);
c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
if(waserror()){
cclose(c);
nexterror();
}
m = mntversion(c, vers, msize, arglen);
poperror();
cclose(c);
poperror();
return m;
}
int
kpipe(int fd[2])
{
Dev *d;
Fgrp *f;
Chan *c[2];
static char *names[] = {"data", "data1"};
f = up->env->fgrp;
d = devtab[devno('|', 0)];
c[0] = namec("#|", Atodir, 0, 0);
c[1] = 0;
fd[0] = -1;
fd[1] = -1;
if(waserror()) {
if(c[0] != 0)
cclose(c[0]);
if(c[1] != 0)
cclose(c[1]);
if(fd[0] >= 0)
f->fd[fd[0]]=0;
if(fd[1] >= 0)
f->fd[fd[1]]=0;
return -1;
}
c[1] = cclone(c[0]);
if(walk(&c[0], &names[0], 1, 1, nil) < 0)
error(Egreg);
if(walk(&c[1], &names[1], 1, 1, nil) < 0)
error(Egreg);
c[0] = d->open(c[0], ORDWR);
c[1] = d->open(c[1], ORDWR);
fd[0] = newfd(c[0]);
if(fd[0] < 0)
error(Enofd);
fd[1] = newfd(c[1]);
if(fd[1] < 0)
error(Enofd);
poperror();
return 0;
}
int
kfwstat(int fd, uchar *buf, int n)
{
volatile struct { Chan *c; } c;
c.c = nil;
if(waserror()) {
cclose(c.c);
return -1;
}
validstat(buf, n);
c.c = fdtochan(up->env->fgrp, fd, -1, 1, 1);
n = devtab[c.c->type]->wstat(c.c, buf, n);
poperror();
cclose(c.c);
return n;
}
long
bindmount(Chan *c, char *old, int flag, char *spec)
{
int ret;
volatile struct { Chan *c; } c1;
if(flag>MMASK || (flag&MORDER) == (MBEFORE|MAFTER))
error(Ebadarg);
c1.c = namec(old, Amount, 0, 0);
if(waserror()){
cclose(c1.c);
nexterror();
}
ret = cmount(c, c1.c, flag, spec);
poperror();
cclose(c1.c);
return ret;
}
int
kbind(char *new, char *old, int flags)
{
long r;
volatile struct { Chan *c; } c0;
c0.c = nil;
if(waserror()) {
cclose(c0.c);
return -1;
}
c0.c = namec(new, Abind, 0, 0);
r = bindmount(c0.c, old, flags, "");
poperror();
cclose(c0.c);
return r;
}
int
kmount(int fd, int afd, char *old, int flags, char *spec)
{
long r;
volatile struct { Chan *c; } c0;
volatile struct { Chan *c; } bc;
volatile struct { Chan *c; } ac;
Mntparam mntparam;
ac.c = nil;
bc.c = nil;
c0.c = nil;
if(waserror()) {
cclose(ac.c);
cclose(bc.c);
cclose(c0.c);
return -1;
}
bc.c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
if(afd >= 0)
ac.c = fdtochan(up->env->fgrp, afd, ORDWR, 0, 1);
mntparam.chan = bc.c;
mntparam.authchan = ac.c;
mntparam.spec = spec;
mntparam.flags = flags;
c0.c = devtab[devno('M', 0)]->attach((char*)&mntparam);
r = bindmount(c0.c, old, flags, spec);
poperror();
cclose(ac.c);
cclose(bc.c);
cclose(c0.c);
return r;
}
int
kunmount(char *old, char *new)
{
volatile struct { Chan *c; } cmount;
volatile struct { Chan *c; } cmounted;
cmount.c = nil;
cmounted.c = nil;
if(waserror()) {
cclose(cmount.c);
cclose(cmounted.c);
return -1;
}
cmount.c = namec(new, Amount, 0, 0);
if(old != nil && old[0] != '\0') {
/*
* This has to be namec(..., Aopen, ...) because
* if arg[0] is something like /srv/cs or /fd/0,
* opening it is the only way to get at the real
* Chan underneath.
*/
cmounted.c = namec(old, Aopen, OREAD, 0);
}
cunmount(cmount.c, cmounted.c);
poperror();
cclose(cmount.c);
cclose(cmounted.c);
return 0;
}
int
kopen(char *path, int mode)
{
int fd;
volatile struct { Chan *c; } c;
if(waserror())
return -1;
openmode(mode); /* error check only */
c.c = namec(path, Aopen, mode, 0);
if(waserror()){
cclose(c.c);
nexterror();
}
fd = newfd(c.c);
if(fd < 0)
error(Enofd);
poperror();
poperror();
return fd;
}
long
unionread(Chan *c, void *va, long n)
{
int i;
long nr;
Mhead *m;
Mount *mount;
qlock(&c->umqlock);
m = c->umh;
rlock(&m->lock);
mount = m->mount;
/* bring mount in sync with c->uri and c->umc */
for(i = 0; mount != nil && i < c->uri; i++)
mount = mount->next;
nr = 0;
while(mount != nil) {
/* Error causes component of union to be skipped */
if(mount->to && !waserror()) {
if(c->umc == nil){
c->umc = cclone(mount->to);
c->umc = devtab[c->umc->type]->open(c->umc, OREAD);
}
nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset);
if(nr < 0)
nr = 0; /* dev.c can return -1 */
c->umc->offset += nr;
poperror();
}
if(nr > 0)
break;
/* Advance to next element */
c->uri++;
if(c->umc) {
cclose(c->umc);
c->umc = nil;
}
mount = mount->next;
}
runlock(&m->lock);
qunlock(&c->umqlock);
return nr;
}
static void
unionrewind(Chan *c)
{
qlock(&c->umqlock);
c->uri = 0;
if(c->umc){
cclose(c->umc);
c->umc = nil;
}
qunlock(&c->umqlock);
}
static long
rread(int fd, void *va, long n, vlong *offp)
{
int dir;
Lock *cl;
volatile struct { Chan *c; } c;
vlong off;
if(waserror())
return -1;
c.c = fdtochan(up->env->fgrp, fd, OREAD, 1, 1);
if(waserror()){
cclose(c.c);
nexterror();
}
if(n < 0)
error(Etoosmall);
dir = c.c->qid.type & QTDIR;
if(dir && c.c->umh)
n = unionread(c.c, va, n);
else{
cl = &c.c->l;
if(offp == nil){
lock(cl); /* lock for vlong assignment */
off = c.c->offset;
unlock(cl);
}else
off = *offp;
if(off < 0)
error(Enegoff);
if(off == 0){
if(offp == nil){
lock(cl);
c.c->offset = 0;
c.c->dri = 0;
unlock(cl);
}
unionrewind(c.c);
}
n = devtab[c.c->type]->read(c.c, va, n, off);
lock(cl);
c.c->offset += n;
unlock(cl);
}
poperror();
cclose(c.c);
poperror();
return n;
}
long
kread(int fd, void *va, long n)
{
return rread(fd, va, n, nil);
}
long
kpread(int fd, void *va, long n, vlong off)
{
return rread(fd, va, n, &off);
}
int
kremove(char *path)
{
volatile struct { Chan *c; } c;
if(waserror())
return -1;
c.c = namec(path, Aremove, 0, 0);
if(waserror()){
c.c->type = 0; /* see below */
cclose(c.c);
nexterror();
}
devtab[c.c->type]->remove(c.c);
/*
* Remove clunks the fid, but we need to recover the Chan
* so fake it up. rootclose() is known to be a nop.
*/
c.c->type = 0;
poperror();
cclose(c.c);
poperror();
return 0;
}
vlong
kseek(int fd, vlong off, int whence)
{
Dir *dir;
Chan *c;
if(waserror())
return -1;
c = fdtochan(up->env->fgrp, fd, -1, 1, 1);
if(waserror()) {
cclose(c);
nexterror();
}
if(devtab[c->type]->dc == '|')
error(Eisstream);
switch(whence) {
case 0:
if(c->qid.type & QTDIR){
if(off != 0)
error(Eisdir);
unionrewind(c);
}else if(off < 0)
error(Enegoff);
lock(&c->l); /* lock for vlong assignment */
c->offset = off;
unlock(&c->l);
break;
case 1:
if(c->qid.type & QTDIR)
error(Eisdir);
lock(&c->l); /* lock for read/write update */
off += c->offset;
if(off < 0){
unlock(&c->l);
error(Enegoff);
}
c->offset = off;
unlock(&c->l);
break;
case 2:
if(c->qid.type & QTDIR)
error(Eisdir);
dir = chandirstat(c);
if(dir == nil)
error("internal error: stat error in seek");
off += dir->length;
free(dir);
if(off < 0)
error(Enegoff);
lock(&c->l); /* lock for read/write update */
c->offset = off;
unlock(&c->l);
break;
default:
error(Ebadarg);
break;
}
poperror();
c->dri = 0;
cclose(c);
poperror();
return off;
}
void
validstat(uchar *s, int n)
{
int m;
char buf[64];
if(statcheck(s, n) < 0)
error(Ebadstat);
/* verify that name entry is acceptable */
s += STATFIXLEN - 4*BIT16SZ; /* location of first string */
/*
* s now points at count for first string.
* if it's too long, let the server decide; this is
* only for his protection anyway. otherwise
* we'd have to allocate and waserror.
*/
m = GBIT16(s);
s += BIT16SZ;
if(m+1 > sizeof buf)
return;
memmove(buf, s, m);
buf[m] = '\0';
/* name could be '/' */
if(strcmp(buf, "/") != 0)
validname(buf, 0);
}
int
kstat(char *path, uchar *buf, int n)
{
volatile struct { Chan *c; } c;
c.c = nil;
if(waserror()){
cclose(c.c);
return -1;
}
c.c = namec(path, Aaccess, 0, 0);
devtab[c.c->type]->stat(c.c, buf, n);
poperror();
cclose(c.c);
return 0;
}
static long
rwrite(int fd, void *va, long n, vlong *offp)
{
Lock *cl;
volatile struct { Chan *c; } c;
vlong off;
long m;
if(waserror())
return -1;
c.c = fdtochan(up->env->fgrp, fd, OWRITE, 1, 1);
if(waserror()){
cclose(c.c);
nexterror();
}
if(c.c->qid.type & QTDIR)
error(Eisdir);
if(n < 0)
error(Etoosmall);
cl = &c.c->l;
if(offp == nil){
lock(cl);
off = c.c->offset;
c.c->offset += n;
unlock(cl);
}else
off = *offp;
if(waserror()){
if(offp == nil){
lock(cl);
c.c->offset -= n;
unlock(cl);
}
nexterror();
}
if(off < 0)
error(Enegoff);
m = devtab[c.c->type]->write(c.c, va, n, off);
poperror();
if(offp == nil && m < n){
lock(cl);
c.c->offset -= n - m;
unlock(cl);
}
poperror();
cclose(c.c);
poperror();
return m;
}
long
kwrite(int fd, void *va, long n)
{
return rwrite(fd, va, n, nil);
}
long
kpwrite(int fd, void *va, long n, vlong off)
{
return rwrite(fd, va, n, &off);
}
int
kwstat(char *path, uchar *buf, int n)
{
volatile struct { Chan *c; } c;
c.c = nil;
if(waserror()){
cclose(c.c);
return -1;
}
validstat(buf, n);
c.c = namec(path, Aaccess, 0, 0);
n = devtab[c.c->type]->wstat(c.c, buf, n);
poperror();
cclose(c.c);
return n;
}
enum
{
DIRSIZE = STATFIXLEN + 32 * 4,
DIRREADLIM = 2048, /* should handle the largest reasonable directory entry */
};
Dir*
chandirstat(Chan *c)
{
Dir *d;
uchar *buf;
int n, nd, i;
nd = DIRSIZE;
for(i=0; i<2; i++){ /* should work by the second try */
d = smalloc(sizeof(Dir) + nd);
buf = (uchar*)&d[1];
if(waserror()){
free(d);
return nil;
}
n = devtab[c->type]->stat(c, buf, nd);
poperror();
if(n < BIT16SZ){
free(d);
return nil;
}
nd = GBIT16((uchar*)buf) + BIT16SZ; /* size needed to store whole stat buffer including count */
if(nd <= n){
convM2D(buf, n, d, (char*)&d[1]);
return d;
}
/* else sizeof(Dir)+nd is plenty */
free(d);
}
return nil;
}
Dir*
kdirstat(char *name)
{
volatile struct {Chan *c;} c;
Dir *d;
c.c = nil;
if(waserror()){
cclose(c.c);
return nil;
}
c.c = namec(name, Aaccess, 0, 0);
d = chandirstat(c.c);
poperror();
cclose(c.c);
return d;
}
Dir*
kdirfstat(int fd)
{
volatile struct { Chan *c; } c;
Dir *d;
c.c = nil;
if(waserror()) {
cclose(c.c);
return nil;
}
c.c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
d = chandirstat(c.c);
poperror();
cclose(c.c);
return d;
}
int
kdirwstat(char *name, Dir *dir)
{
uchar *buf;
int r;
r = sizeD2M(dir);
buf = smalloc(r);
convD2M(dir, buf, r);
r = kwstat(name, buf, r);
free(buf);
return r < 0? r: 0;
}
int
kdirfwstat(int fd, Dir *dir)
{
uchar *buf;
int r;
r = sizeD2M(dir);
buf = smalloc(r);
convD2M(dir, buf, r);
r = kfwstat(fd, buf, r);
free(buf);
return r < 0? r: 0;
}
static long
dirpackage(uchar *buf, long ts, Dir **d)
{
char *s;
long ss, i, n, nn, m;
*d = nil;
if(ts <= 0)
return ts;
/*
* first find number of all stats, check they look like stats, & size all associated strings
*/
ss = 0;
n = 0;
for(i = 0; i < ts; i += m){
m = BIT16SZ + GBIT16(&buf[i]);
if(statcheck(&buf[i], m) < 0)
break;
ss += m;
n++;
}
if(i != ts)
error("bad directory format");
*d = malloc(n * sizeof(Dir) + ss);
if(*d == nil)
error(Enomem);
/*
* then convert all buffers
*/
s = (char*)*d + n * sizeof(Dir);
nn = 0;
for(i = 0; i < ts; i += m){
m = BIT16SZ + GBIT16((uchar*)&buf[i]);
if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){
free(*d);
*d = nil;
error("bad directory entry");
}
nn++;
s += m;
}
return nn;
}
long
kdirread(int fd, Dir **d)
{
uchar *buf;
long ts;
*d = nil;
if(waserror())
return -1;
buf = malloc(DIRREADLIM);
if(buf == nil)
error(Enomem);
if(waserror()){
free(buf);
nexterror();
}
ts = kread(fd, buf, DIRREADLIM);
if(ts > 0)
ts = dirpackage(buf, ts, d);
poperror();
free(buf);
poperror();
return ts;
}
int
kiounit(int fd)
{
Chan *c;
int n;
c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
if(waserror()){
cclose(c);
return 0; /* n.b. */
}
n = c->iounit;
poperror();
cclose(c);
return n;
}