plan9fox/sys/src/9/port/sysfile.c
cinap_lenrek 0b33b3b8ad kernel: implement per file descriptor OCEXEC flag, reject ORCLOSE when opening /fd, /srv and /shr
The OCEXEC flag used to be maintained per channel,
making it shared between all the file desciptors.

This has a unexpected side effects with regard to
channel passing drivers such as devdup (/fd),
devsrv (/srv) and devshr (/shr).

For example, opening a /srv file with OCEXEC
makes it impossible to be remounted by exportfs
as it internally does a exec() to mount and
re-export it. There is no way to reset the flag.

This change makes the OCEXEC flag per file descriptor,
so a open with the OCEXEC flag only affects the fd
group of the calling process, and not the channel
itself.

On rfork(RFFDG), the per file descriptor flags get
copied.

On dup(), the per file descriptor flags are reset.

The second modification is that /fd, /srv and /shr
should reject the ORCLOSE flag, as the files that
are returned have already been opend.
2020-12-13 16:04:09 +01:00

1411 lines
24 KiB
C

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
/*
* The sys*() routines needn't poperror() as they return directly to syscall().
*/
static void
unlockfgrp(Fgrp *f)
{
int ex;
ex = f->exceed;
f->exceed = 0;
unlock(f);
if(ex)
pprint("warning: process exceeds %d file descriptors\n", ex);
}
int
growfd(Fgrp *f, int fd) /* fd is always >= 0 */
{
Chan **newfd, **oldfd;
uchar *newflag, *oldflag;
int nfd;
nfd = f->nfd;
if(fd < nfd)
return 0;
if(fd >= nfd+DELTAFD)
return -1; /* out of range */
/*
* Unbounded allocation is unwise; besides, there are only 16 bits
* of fid in 9P
*/
if(nfd >= 5000){
Exhausted:
print("no free file descriptors\n");
return -1;
}
oldfd = f->fd;
oldflag = f->flag;
newfd = malloc((nfd+DELTAFD)*sizeof(newfd[0]));
if(newfd == nil)
goto Exhausted;
memmove(newfd, oldfd, nfd*sizeof(newfd[0]));
newflag = malloc((nfd+DELTAFD)*sizeof(newflag[0]));
if(newflag == nil){
free(newfd);
goto Exhausted;
}
memmove(newflag, oldflag, nfd*sizeof(newflag[0]));
f->fd = newfd;
f->flag = newflag;
f->nfd = nfd+DELTAFD;
if(fd > f->maxfd){
if(fd/100 > f->maxfd/100)
f->exceed = (fd/100)*100;
f->maxfd = fd;
}
free(oldfd);
free(oldflag);
return 1;
}
/*
* this assumes that the fgrp is locked
*/
int
findfreefd(Fgrp *f, int start)
{
int fd;
for(fd=start; fd<f->nfd; fd++)
if(f->fd[fd] == nil)
break;
if(fd >= f->nfd && growfd(f, fd) < 0)
return -1;
return fd;
}
int
newfd(Chan *c, int mode)
{
int fd, flag;
Fgrp *f;
f = up->fgrp;
lock(f);
fd = findfreefd(f, 0);
if(fd < 0){
unlockfgrp(f);
return -1;
}
if(fd > f->maxfd)
f->maxfd = fd;
f->fd[fd] = c;
/* per file-descriptor flags */
flag = 0;
if(mode & OCEXEC)
flag |= CCEXEC;
f->flag[fd] = flag;
unlockfgrp(f);
return fd;
}
int
newfd2(int fd[2], Chan *c[2])
{
Fgrp *f;
f = up->fgrp;
lock(f);
fd[0] = findfreefd(f, 0);
if(fd[0] < 0){
unlockfgrp(f);
return -1;
}
fd[1] = findfreefd(f, fd[0]+1);
if(fd[1] < 0){
unlockfgrp(f);
return -1;
}
if(fd[1] > f->maxfd)
f->maxfd = fd[1];
f->fd[fd[0]] = c[0];
f->fd[fd[1]] = c[1];
f->flag[fd[0]] = 0;
f->flag[fd[1]] = 0;
unlockfgrp(f);
return 0;
}
Chan*
fdtochan(int fd, int mode, int chkmnt, int iref)
{
Chan *c;
Fgrp *f;
c = nil;
f = up->fgrp;
lock(f);
if(fd<0 || f->nfd<=fd || (c = f->fd[fd])==nil) {
unlock(f);
error(Ebadfd);
}
if(iref)
incref(c);
unlock(f);
if(chkmnt && (c->flag&CMSG)) {
if(iref)
cclose(c);
error(Ebadusefd);
}
if(mode<0 || c->mode==ORDWR)
return c;
if((mode&OTRUNC) && c->mode==OREAD) {
if(iref)
cclose(c);
error(Ebadusefd);
}
if((mode&~OTRUNC) != c->mode) {
if(iref)
cclose(c);
error(Ebadusefd);
}
return c;
}
int
openmode(ulong o)
{
o &= ~(OTRUNC|OCEXEC|ORCLOSE);
if(o > OEXEC)
error(Ebadarg);
if(o == OEXEC)
return OREAD;
return o;
}
uintptr
sysfd2path(va_list list)
{
Chan *c;
char *buf;
uint len;
int fd;
fd = va_arg(list, int);
buf = va_arg(list, char*);
len = va_arg(list, uint);
validaddr((uintptr)buf, len, 1);
c = fdtochan(fd, -1, 0, 1);
snprint(buf, len, "%s", chanpath(c));
cclose(c);
return 0;
}
uintptr
syspipe(va_list list)
{
static char *datastr[] = {"data", "data1"};
int fd[2], *ufd;
Chan *c[2];
ufd = va_arg(list, int*);
validaddr((uintptr)ufd, sizeof(fd), 1);
evenaddr((uintptr)ufd);
ufd[0] = ufd[1] = fd[0] = fd[1] = -1;
c[0] = namec("#|", Atodir, 0, 0);
c[1] = nil;
if(waserror()){
cclose(c[0]);
if(c[1] != nil)
cclose(c[1]);
nexterror();
}
c[1] = cclone(c[0]);
if(walk(&c[0], datastr+0, 1, 1, nil) < 0)
error(Egreg);
if(walk(&c[1], datastr+1, 1, 1, nil) < 0)
error(Egreg);
c[0] = devtab[c[0]->type]->open(c[0], ORDWR);
c[1] = devtab[c[1]->type]->open(c[1], ORDWR);
if(newfd2(fd, c) < 0)
error(Enofd);
ufd[0] = fd[0];
ufd[1] = fd[1];
poperror();
return 0;
}
uintptr
sysdup(va_list list)
{
int fd;
Chan *c, *oc;
Fgrp *f = up->fgrp;
fd = va_arg(list, int);
/*
* Close after dup'ing, so date > #d/1 works
*/
c = fdtochan(fd, -1, 0, 1);
fd = va_arg(list, int);
if(fd != -1){
lock(f);
if(fd<0 || growfd(f, fd)<0) {
unlockfgrp(f);
cclose(c);
error(Ebadfd);
}
if(fd > f->maxfd)
f->maxfd = fd;
oc = f->fd[fd];
f->fd[fd] = c;
f->flag[fd] = 0;
unlockfgrp(f);
if(oc != nil)
cclose(oc);
}else{
if(waserror()) {
cclose(c);
nexterror();
}
fd = newfd(c, 0);
if(fd < 0)
error(Enofd);
poperror();
}
return (uintptr)fd;
}
uintptr
sysopen(va_list list)
{
int fd;
Chan *c;
char *name;
ulong mode;
name = va_arg(list, char*);
mode = va_arg(list, ulong);
openmode(mode); /* error check only */
validaddr((uintptr)name, 1, 0);
c = namec(name, Aopen, mode, 0);
if(waserror()){
cclose(c);
nexterror();
}
fd = newfd(c, mode);
if(fd < 0)
error(Enofd);
poperror();
return (uintptr)fd;
}
void
fdclose(int fd, int flag)
{
Chan *c;
Fgrp *f = up->fgrp;
lock(f);
c = fd <= f->maxfd ? f->fd[fd] : nil;
if(c == nil || (flag != 0 && ((f->flag[fd]|c->flag)&flag) == 0)){
unlock(f);
return;
}
f->fd[fd] = nil;
if(fd == f->maxfd){
while(fd > 0 && f->fd[fd] == nil)
f->maxfd = --fd;
}
unlock(f);
cclose(c);
}
uintptr
sysclose(va_list list)
{
int fd;
fd = va_arg(list, int);
fdtochan(fd, -1, 0, 0);
fdclose(fd, 0);
return 0;
}
long
unionread(Chan *c, void *va, long n)
{
int i;
long nr;
Mhead *m;
Mount *mount;
eqlock(&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 != nil && !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);
c->umc->offset += nr;
poperror();
}
if(nr > 0)
break;
/* Advance to next element */
c->uri++;
if(c->umc != nil){
cclose(c->umc);
c->umc = nil;
}
mount = mount->next;
}
runlock(&m->lock);
qunlock(&c->umqlock);
return nr;
}
static void
unionrewind(Chan *c)
{
eqlock(&c->umqlock);
c->uri = 0;
if(c->umc != nil){
cclose(c->umc);
c->umc = nil;
}
qunlock(&c->umqlock);
}
static int
dirfixed(uchar *p, uchar *e, Dir *d)
{
int len;
len = GBIT16(p)+BIT16SZ;
if(p + len > e)
return -1;
p += BIT16SZ; /* ignore size */
d->type = devno(GBIT16(p), 1);
p += BIT16SZ;
d->dev = GBIT32(p);
p += BIT32SZ;
d->qid.type = GBIT8(p);
p += BIT8SZ;
d->qid.vers = GBIT32(p);
p += BIT32SZ;
d->qid.path = GBIT64(p);
p += BIT64SZ;
d->mode = GBIT32(p);
p += BIT32SZ;
d->atime = GBIT32(p);
p += BIT32SZ;
d->mtime = GBIT32(p);
p += BIT32SZ;
d->length = GBIT64(p);
return len;
}
static char*
dirname(uchar *p, int *n)
{
p += BIT16SZ+BIT16SZ+BIT32SZ+BIT8SZ+BIT32SZ+BIT64SZ
+ BIT32SZ+BIT32SZ+BIT32SZ+BIT64SZ;
*n = GBIT16(p);
return (char*)p+BIT16SZ;
}
static long
dirsetname(char *name, int len, uchar *p, long n, long maxn)
{
char *oname;
int olen;
long nn;
if(n == BIT16SZ)
return BIT16SZ;
oname = dirname(p, &olen);
nn = n+len-olen;
PBIT16(p, nn-BIT16SZ);
if(nn > maxn)
return BIT16SZ;
if(len != olen)
memmove(oname+len, oname+olen, p+n-(uchar*)(oname+olen));
PBIT16((uchar*)(oname-2), len);
memmove(oname, name, len);
return nn;
}
/*
* Mountfix might have caused the fixed results of the directory read
* to overflow the buffer. Catch the overflow in c->dirrock.
*/
static void
mountrock(Chan *c, uchar *p, uchar **pe)
{
uchar *e, *r;
int len, n;
e = *pe;
/* find last directory entry */
for(;;){
len = BIT16SZ+GBIT16(p);
if(p+len >= e)
break;
p += len;
}
/* save it away */
qlock(&c->rockqlock);
if(c->nrock+len > c->mrock){
n = ROUND(c->nrock+len, 1024);
r = smalloc(n);
memmove(r, c->dirrock, c->nrock);
free(c->dirrock);
c->dirrock = r;
c->mrock = n;
}
memmove(c->dirrock+c->nrock, p, len);
c->nrock += len;
qunlock(&c->rockqlock);
/* drop it */
*pe = p;
}
/*
* Satisfy a directory read with the results saved in c->dirrock.
*/
static int
mountrockread(Chan *c, uchar *op, long n, long *nn)
{
long dirlen;
uchar *rp, *erp, *ep, *p;
/* common case */
if(c->nrock == 0)
return 0;
/* copy out what we can */
qlock(&c->rockqlock);
rp = c->dirrock;
erp = rp+c->nrock;
p = op;
ep = p+n;
while(rp+BIT16SZ <= erp){
dirlen = BIT16SZ+GBIT16(rp);
if(p+dirlen > ep)
break;
memmove(p, rp, dirlen);
p += dirlen;
rp += dirlen;
}
if(p == op){
qunlock(&c->rockqlock);
return 0;
}
/* shift the rest */
if(rp != erp)
memmove(c->dirrock, rp, erp-rp);
c->nrock = erp - rp;
*nn = p - op;
qunlock(&c->rockqlock);
return 1;
}
static void
mountrewind(Chan *c)
{
c->nrock = 0;
}
/*
* Rewrite the results of a directory read to reflect current
* name space bindings and mounts. Specifically, replace
* directory entries for bind and mount points with the results
* of statting what is mounted there. Except leave the old names.
*/
static long
mountfix(Chan *c, uchar *op, long n, long maxn)
{
char *name;
int nbuf, nname;
Chan *nc;
Mhead *mh;
Mount *m;
uchar *p;
int dirlen, rest;
long l;
uchar *buf, *e;
Dir d;
p = op;
buf = nil;
nbuf = 0;
for(e=&p[n]; p+BIT16SZ<e; p+=dirlen){
dirlen = dirfixed(p, e, &d);
if(dirlen < 0)
break;
nc = nil;
mh = nil;
if(findmount(&nc, &mh, d.type, d.dev, d.qid)){
/*
* If it's a union directory and the original is
* in the union, don't rewrite anything.
*/
for(m = mh->mount; m != nil; m = m->next)
if(eqchantdqid(m->to, d.type, d.dev, d.qid, 1))
goto Norewrite;
name = dirname(p, &nname);
/*
* Do the stat but fix the name. If it fails, leave old entry.
* BUG: If it fails because there isn't room for the entry,
* what can we do? Nothing, really. Might as well skip it.
*/
if(buf == nil){
buf = smalloc(4096);
nbuf = 4096;
}
if(waserror())
goto Norewrite;
l = devtab[nc->type]->stat(nc, buf, nbuf);
l = dirsetname(name, nname, buf, l, nbuf);
if(l == BIT16SZ)
error("dirsetname");
poperror();
/*
* Shift data in buffer to accomodate new entry,
* possibly overflowing into rock.
*/
rest = e - (p+dirlen);
if(l > dirlen){
while(p+l+rest > op+maxn){
mountrock(c, p, &e);
if(e == p){
dirlen = 0;
goto Norewrite;
}
rest = e - (p+dirlen);
}
}
if(l != dirlen){
memmove(p+l, p+dirlen, rest);
dirlen = l;
e = p+dirlen+rest;
}
/*
* Rewrite directory entry.
*/
memmove(p, buf, l);
Norewrite:
cclose(nc);
putmhead(mh);
}
}
if(buf != nil)
free(buf);
if(p != e)
error("oops in rockfix");
return e-op;
}
static long
read(int fd, uchar *p, long n, vlong *offp)
{
long nn, nnn;
Chan *c;
vlong off;
validaddr((uintptr)p, n, 1);
c = fdtochan(fd, OREAD, 1, 1);
if(waserror()){
cclose(c);
nexterror();
}
/*
* The offset is passed through on directories, normally.
* Sysseek complains, but pread is used by servers like exportfs,
* that shouldn't need to worry about this issue.
*
* Notice that c->devoffset is the offset that c's dev is seeing.
* The number of bytes read on this fd (c->offset) may be different
* due to rewritings in rockfix.
*/
if(offp == nil) /* use and maintain channel's offset */
off = c->offset;
else
off = *offp;
if(off < 0)
error(Enegoff);
if(off == 0){ /* rewind to the beginning of the directory */
if(offp == nil || (c->qid.type & QTDIR)){
c->offset = 0;
c->devoffset = 0;
}
mountrewind(c);
unionrewind(c);
}
if(c->qid.type & QTDIR){
if(mountrockread(c, p, n, &nn)){
/* do nothing: mountrockread filled buffer */
}else if(c->umh != nil)
nn = unionread(c, p, n);
else{
if(off != c->offset)
error(Edirseek);
nn = devtab[c->type]->read(c, p, n, c->devoffset);
}
nnn = mountfix(c, p, nn, n);
}else
nnn = nn = devtab[c->type]->read(c, p, n, off);
if(offp == nil || (c->qid.type & QTDIR)){
lock(c);
c->devoffset += nn;
c->offset += nnn;
unlock(c);
}
poperror();
cclose(c);
return nnn;
}
uintptr
sys_read(va_list list)
{
int fd;
void *buf;
long len;
fd = va_arg(list, int);
buf = va_arg(list, void*);
len = va_arg(list, long);
return (uintptr)read(fd, buf, len, nil);
}
uintptr
syspread(va_list list)
{
int fd;
void *buf;
long len;
vlong off, *offp;
fd = va_arg(list, int);
buf = va_arg(list, void*);
len = va_arg(list, long);
off = va_arg(list, vlong);
if(off != ~0ULL)
offp = &off;
else
offp = nil;
return (uintptr)read(fd, buf, len, offp);
}
static long
write(int fd, void *buf, long len, vlong *offp)
{
Chan *c;
long m, n;
vlong off;
validaddr((uintptr)buf, len, 0);
n = 0;
c = fdtochan(fd, OWRITE, 1, 1);
if(waserror()) {
if(offp == nil){
lock(c);
c->offset -= n;
unlock(c);
}
cclose(c);
nexterror();
}
if(c->qid.type & QTDIR)
error(Eisdir);
n = len;
if(offp == nil){ /* use and maintain channel's offset */
lock(c);
off = c->offset;
c->offset += n;
unlock(c);
}else
off = *offp;
if(off < 0)
error(Enegoff);
m = devtab[c->type]->write(c, buf, n, off);
if(offp == nil && m < n){
lock(c);
c->offset -= n - m;
unlock(c);
}
poperror();
cclose(c);
return m;
}
uintptr
sys_write(va_list list)
{
int fd;
void *buf;
long len;
fd = va_arg(list, int);
buf = va_arg(list, void*);
len = va_arg(list, long);
return (uintptr)write(fd, buf, len, nil);
}
uintptr
syspwrite(va_list list)
{
int fd;
void *buf;
long len;
vlong off, *offp;
fd = va_arg(list, int);
buf = va_arg(list, void*);
len = va_arg(list, long);
off = va_arg(list, vlong);
if(off != ~0ULL)
offp = &off;
else
offp = nil;
return (uintptr)write(fd, buf, len, offp);
}
static vlong
sseek(int fd, vlong o, int type)
{
Chan *c;
uchar buf[sizeof(Dir)+100];
Dir dir;
int n;
vlong off;
c = fdtochan(fd, -1, 1, 1);
if(waserror()){
cclose(c);
nexterror();
}
if(devtab[c->type]->dc == L'|')
error(Eisstream);
off = 0;
switch(type){
case 0:
off = o;
if((c->qid.type & QTDIR) && off != 0)
error(Eisdir);
if(off < 0)
error(Enegoff);
c->offset = off;
break;
case 1:
if(c->qid.type & QTDIR)
error(Eisdir);
lock(c); /* lock for read/write update */
off = o + c->offset;
if(off < 0){
unlock(c);
error(Enegoff);
}
c->offset = off;
unlock(c);
break;
case 2:
if(c->qid.type & QTDIR)
error(Eisdir);
n = devtab[c->type]->stat(c, buf, sizeof buf);
if(convM2D(buf, n, &dir, nil) == 0)
error("internal error: stat error in seek");
off = dir.length + o;
if(off < 0)
error(Enegoff);
c->offset = off;
break;
default:
error(Ebadarg);
}
c->uri = 0;
c->dri = 0;
cclose(c);
poperror();
return off;
}
uintptr
sysseek(va_list list)
{
int fd, t;
vlong n, *v;
v = va_arg(list, vlong*);
evenaddr((uintptr)v);
validaddr((uintptr)v, sizeof(vlong), 1);
fd = va_arg(list, int);
n = va_arg(list, vlong);
t = va_arg(list, int);
*v = sseek(fd, n, t);
return 0;
}
uintptr
sysoseek(va_list list)
{
int fd, t;
long n;
fd = va_arg(list, int);
n = va_arg(list, long);
t = va_arg(list, int);
return (uintptr)sseek(fd, n, t);
}
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);
}
static char*
pathlast(Path *p)
{
char *s;
if(p == nil)
return nil;
if(p->len == 0)
return nil;
s = strrchr(p->s, '/');
if(s != nil)
return s+1;
return p->s;
}
uintptr
sysfstat(va_list list)
{
Chan *c;
int fd;
uint l;
uchar *s;
fd = va_arg(list, int);
s = va_arg(list, uchar*);
l = va_arg(list, uint);
validaddr((uintptr)s, l, 1);
c = fdtochan(fd, -1, 0, 1);
if(waserror()) {
cclose(c);
nexterror();
}
l = devtab[c->type]->stat(c, s, l);
poperror();
cclose(c);
return l;
}
uintptr
sysstat(va_list list)
{
char *name;
Chan *c;
uint l, r;
uchar *s;
name = va_arg(list, char*);
s = va_arg(list, uchar*);
l = va_arg(list, uint);
validaddr((uintptr)s, l, 1);
validaddr((uintptr)name, 1, 0);
c = namec(name, Aaccess, 0, 0);
if(waserror()){
cclose(c);
nexterror();
}
r = devtab[c->type]->stat(c, s, l);
name = pathlast(c->path);
if(name != nil)
r = dirsetname(name, strlen(name), s, r, l);
poperror();
cclose(c);
return r;
}
uintptr
syschdir(va_list list)
{
Chan *c;
char *name;
name = va_arg(list, char*);
validaddr((uintptr)name, 1, 0);
c = namec(name, Atodir, 0, 0);
cclose(up->dot);
up->dot = c;
return 0;
}
long
bindmount(int ismount, int fd, int afd, char* arg0, char* arg1, int flag, char* spec)
{
int ret;
Chan *c0, *c1, *ac, *bc;
if((flag&~MMASK) || (flag&MORDER)==(MBEFORE|MAFTER))
error(Ebadarg);
if(ismount){
validaddr((uintptr)spec, 1, 0);
spec = validnamedup(spec, 1);
if(waserror()){
free(spec);
nexterror();
}
if(up->pgrp->noattach)
error(Enoattach);
ac = nil;
bc = fdtochan(fd, ORDWR, 0, 1);
if(waserror()) {
if(ac != nil)
cclose(ac);
cclose(bc);
nexterror();
}
if(afd >= 0)
ac = fdtochan(afd, ORDWR, 0, 1);
c0 = mntattach(bc, ac, spec, flag&MCACHE);
poperror(); /* ac bc */
if(ac != nil)
cclose(ac);
cclose(bc);
}else{
spec = nil;
validaddr((uintptr)arg0, 1, 0);
c0 = namec(arg0, Abind, 0, 0);
}
if(waserror()){
cclose(c0);
nexterror();
}
validaddr((uintptr)arg1, 1, 0);
c1 = namec(arg1, Amount, 0, 0);
if(waserror()){
cclose(c1);
nexterror();
}
ret = cmount(&c0, c1, flag, spec);
poperror();
cclose(c1);
poperror();
cclose(c0);
if(ismount){
fdclose(fd, 0);
poperror();
free(spec);
}
return ret;
}
uintptr
sysbind(va_list list)
{
char *arg0, *arg1;
int flag;
arg0 = va_arg(list, char*);
arg1 = va_arg(list, char*);
flag = va_arg(list, int);
return (uintptr)bindmount(0, -1, -1, arg0, arg1, flag, nil);
}
uintptr
sysmount(va_list list)
{
char *arg1, *spec;
int flag;
int fd, afd;
fd = va_arg(list, int);
afd = va_arg(list, int);
arg1 = va_arg(list, char*);
flag = va_arg(list, int);
spec = va_arg(list, char*);
return (uintptr)bindmount(1, fd, afd, nil, arg1, flag, spec);
}
uintptr
sys_mount(va_list list)
{
char *arg1, *spec;
int flag;
int fd;
fd = va_arg(list, int);
arg1 = va_arg(list, char*);
flag = va_arg(list, int);
spec = va_arg(list, char*);
return (uintptr)bindmount(1, fd, -1, nil, arg1, flag, spec);
}
uintptr
sysunmount(va_list list)
{
Chan *cmount, *cmounted;
char *name, *old;
name = va_arg(list, char*);
old = va_arg(list, char*);
cmounted = nil;
validaddr((uintptr)old, 1, 0);
cmount = namec(old, Amount, 0, 0);
if(waserror()) {
cclose(cmount);
if(cmounted != nil)
cclose(cmounted);
nexterror();
}
if(name != nil) {
/*
* 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.
*/
validaddr((uintptr)name, 1, 0);
cmounted = namec(name, Aopen, OREAD, 0);
}
cunmount(cmount, cmounted);
poperror();
cclose(cmount);
if(cmounted != nil)
cclose(cmounted);
return 0;
}
uintptr
syscreate(va_list list)
{
int fd, mode, perm;
char *name;
Chan *c;
name = va_arg(list, char*);
mode = va_arg(list, int);
perm = va_arg(list, int);
openmode(mode&~OEXCL); /* error check only; OEXCL okay here */
validaddr((uintptr)name, 1, 0);
c = namec(name, Acreate, mode, perm);
if(waserror()) {
cclose(c);
nexterror();
}
fd = newfd(c, mode);
if(fd < 0)
error(Enofd);
poperror();
return (uintptr)fd;
}
uintptr
sysremove(va_list list)
{
char *name;
Chan *c;
name = va_arg(list, char*);
validaddr((uintptr)name, 1, 0);
c = namec(name, Aremove, 0, 0);
/*
* Removing mount points is disallowed to avoid surprises
* (which should be removed: the mount point or the mounted Chan?).
*/
if(c->ismtpt){
cclose(c);
error(Eismtpt);
}
if(waserror()){
c->type = 0; /* see below */
cclose(c);
nexterror();
}
devtab[c->type]->remove(c);
/*
* Remove clunks the fid, but we need to recover the Chan
* so fake it up. rootclose() is known to be a nop.
*/
c->type = 0;
poperror();
cclose(c);
return 0;
}
static long
wstat(Chan *c, uchar *d, int nd)
{
long l;
int namelen;
if(waserror()){
cclose(c);
nexterror();
}
if(c->ismtpt){
/*
* Renaming mount points is disallowed to avoid surprises
* (which should be renamed? the mount point or the mounted Chan?).
*/
dirname(d, &namelen);
if(namelen)
nameerror(chanpath(c), Eismtpt);
}
l = devtab[c->type]->wstat(c, d, nd);
poperror();
cclose(c);
return l;
}
uintptr
syswstat(va_list list)
{
char *name;
uchar *s;
Chan *c;
uint l;
name = va_arg(list, char*);
s = va_arg(list, uchar*);
l = va_arg(list, uint);
validaddr((uintptr)s, l, 0);
validstat(s, l);
validaddr((uintptr)name, 1, 0);
c = namec(name, Aaccess, 0, 0);
return (uintptr)wstat(c, s, l);
}
uintptr
sysfwstat(va_list list)
{
uchar *s;
Chan *c;
uint l;
int fd;
fd = va_arg(list, int);
s = va_arg(list, uchar*);
l = va_arg(list, uint);
validaddr((uintptr)s, l, 0);
validstat(s, l);
c = fdtochan(fd, -1, 1, 1);
return (uintptr)wstat(c, s, l);
}
static void
packoldstat(uchar *buf, Dir *d)
{
uchar *p;
ulong q;
/* lay down old stat buffer - grotty code but it's temporary */
p = buf;
strncpy((char*)p, d->name, 28);
p += 28;
strncpy((char*)p, d->uid, 28);
p += 28;
strncpy((char*)p, d->gid, 28);
p += 28;
q = (ulong)d->qid.path & ~DMDIR; /* make sure doesn't accidentally look like directory */
if(d->qid.type & QTDIR) /* this is the real test of a new directory */
q |= DMDIR;
PBIT32(p, q);
p += BIT32SZ;
PBIT32(p, d->qid.vers);
p += BIT32SZ;
PBIT32(p, d->mode);
p += BIT32SZ;
PBIT32(p, d->atime);
p += BIT32SZ;
PBIT32(p, d->mtime);
p += BIT32SZ;
PBIT64(p, d->length);
p += BIT64SZ;
PBIT16(p, d->type);
p += BIT16SZ;
PBIT16(p, d->dev);
}
uintptr
sys_stat(va_list list)
{
static char old[] = "old stat system call - recompile";
Chan *c;
uint l;
uchar *s, buf[128]; /* old DIRLEN plus a little should be plenty */
char strs[128], *name;
Dir d;
name = va_arg(list, char*);
s = va_arg(list, uchar*);
validaddr((uintptr)s, 116, 1);
validaddr((uintptr)name, 1, 0);
c = namec(name, Aaccess, 0, 0);
if(waserror()){
cclose(c);
nexterror();
}
l = devtab[c->type]->stat(c, buf, sizeof buf);
/* buf contains a new stat buf; convert to old. yuck. */
if(l <= BIT16SZ) /* buffer too small; time to face reality */
error(old);
name = pathlast(c->path);
if(name != nil)
l = dirsetname(name, strlen(name), buf, l, sizeof buf);
l = convM2D(buf, l, &d, strs);
if(l == 0)
error(old);
packoldstat(s, &d);
poperror();
cclose(c);
return 0;
}
uintptr
sys_fstat(va_list list)
{
static char old[] = "old fstat system call - recompile";
Chan *c;
char *name;
uint l;
uchar *s, buf[128]; /* old DIRLEN plus a little should be plenty */
char strs[128];
Dir d;
int fd;
fd = va_arg(list, int);
s = va_arg(list, uchar*);
validaddr((uintptr)s, 116, 1);
c = fdtochan(fd, -1, 0, 1);
if(waserror()){
cclose(c);
nexterror();
}
l = devtab[c->type]->stat(c, buf, sizeof buf);
/* buf contains a new stat buf; convert to old. yuck. */
if(l <= BIT16SZ) /* buffer too small; time to face reality */
error(old);
name = pathlast(c->path);
if(name != nil)
l = dirsetname(name, strlen(name), buf, l, sizeof buf);
l = convM2D(buf, l, &d, strs);
if(l == 0)
error(old);
packoldstat(s, &d);
poperror();
cclose(c);
return 0;
}
uintptr
sys_wstat(va_list)
{
error("old wstat system call - recompile");
return (uintptr)-1;
}
uintptr
sys_fwstat(va_list)
{
error("old fwstat system call - recompile");
return (uintptr)-1;
}