index 14db385..cc983fe 100644 (file)
@@ -19,124 +19,42 @@ enum {
DIRREADSIZE=8192,       /* Just read a lot. Memory is cheap, lots of bandwidth,
* and RPCs are very expensive. At the same time,
* let's not yet exceed a common MSIZE. */
DIRREADSIZE=8192,       /* Just read a lot. Memory is cheap, lots of bandwidth,
* and RPCs are very expensive. At the same time,
* let's not yet exceed a common MSIZE. */
-
};

};

-static int growfd(struct fgrp *f, int fd)
+int newfd(struct chan *c, int oflags)
{
{
-
-       int n;
-       struct chan **nfd, **ofd;
-
-       if (fd < f->nfd) {
-               return 0;
-       }
-       /* want to grow by a reasonable amount (delta), but also make sure we can
-        * handle the fd we're asked for */
-       n = MAX(f->nfd, fd + 1) + DELTAFD;
-       if (n > MAXNFD)
-               n = MAXNFD;
-       if (fd >= n) {
-               set_errno(EMFILE);
-               set_errstr("Asked for FD %d, more than %d\n", fd, MAXNFD);
-               return -1;
-       }
-       nfd = kzmalloc(n * sizeof(struct chan *), 0);
-       if (nfd == NULL) {
-               return -1;
-       }
-       ofd = f->fd;
-       memmove(nfd, ofd, f->nfd * sizeof(struct chan *));
-       f->fd = nfd;
-       f->nfd = n;
-       kfree(ofd);
-       return 0;
-}
-
-int newfd(struct chan *c)
-{
-
-       int i;
-       struct fgrp *f = current->fgrp;
-
-       spin_lock(&f->lock);
-       if (f->closed) {
-               spin_unlock(&f->lock);
-               return -1;
-       }
-       /* VFS hack */
-       /* We'd like to ask it to start at f->minfd, but that would require us to
-        * know if we closed anything.  Since we share the FD numbers with the VFS,
-        * there is no way to know that. */
-#if 1  // VFS hack
-       i = get_fd(&current->open_files, 0);
-#else  // 9ns style
-       /* TODO: use a unique integer allocator */
-       for (i = f->minfd; i < f->nfd; i++)
-               if (f->fd[i] == 0)
-                       break;
-#endif
-       if (growfd(f, i) < 0) {
-               spin_unlock(&f->lock);
-               exhausted("file descriptors");
-               return -1;
-       }
-       assert(f->fd[i] == 0);
-       f->minfd = i + 1;
-       if (i > f->maxfd)
-               f->maxfd = i;
-       f->fd[i] = c;
-       spin_unlock(&f->lock);
-       return i;
+       int ret = insert_obj_fdt(&current->open_files, c, 0,
+                                oflags & O_CLOEXEC ? FD_CLOEXEC : 0,
+                                FALSE, FALSE);
+       if (ret >= 0)
+               cclose(c);
+       return ret;
}

}

-struct chan *fdtochan(struct fgrp *f, int fd, int mode, int chkmnt, int iref)
+struct chan *fdtochan(struct fd_table *fdt, int fd, int mode, int chkmnt,
+                      int iref)
{
{
-
struct chan *c;

struct chan *c;

-       c = 0;
-
-       spin_lock(&f->lock);
-       if (f->closed) {
-               spin_unlock(&f->lock);
-               error("File group closed");
+       c = lookup_fd(fdt, fd, iref, FALSE);
+       if (!c) {
+               /* We lost the info about why there was a problem (we used to track file
+                * group closed too, can add that in later). */
}
}
-       if (fd < 0 || f->maxfd < fd || (c = f->fd[fd]) == 0) {
-               spin_unlock(&f->lock);
-       }
-       if (iref)
-               chan_incref(c);
-       spin_unlock(&f->lock);
-
if (chkmnt && (c->flag & CMSG)) {
if (iref)
cclose(c);
if (chkmnt && (c->flag & CMSG)) {
if (iref)
cclose(c);
}
}
-
-       if (mode < 0 || c->mode == ORDWR) {
+       if (mode < 0)
return c;
return c;
-       }
-
-       if ((mode & OTRUNC) && IS_RDONLY(c->mode)) {
+       if ((mode & c->mode) != mode) {
if (iref)
cclose(c);
if (iref)
cclose(c);
+               error(EBADF, "FD access mode failure: chan mode 0x%x, wanted 0x%x",
+                     c->mode, mode);
}
}
-
-       /* TODO: this is probably wrong.  if you get this from a dev, in the dev's
-        * open, you are probably saving mode directly, without passing it through
-        * openmode. */
-       if ((mode & ~OTRUNC) != c->mode) {
-               warn("Trunc mode issue: mode %o, mode minus trunc %o, chan mode %o\n",
-                        mode, mode & ~OTRUNC, c->mode);
-               if (iref)
-                       cclose(c);
-       }
-
return c;
}

return c;
}

@@ -152,10 +70,12 @@ long kchanio(void *vc, void *buf, int n, int mode)
return -1;
}

return -1;
}

-       if (IS_RDONLY(mode))
r = devtab[c->type].read(c, buf, n, c->offset);
r = devtab[c->type].read(c, buf, n, c->offset);
-       else
+       else if (mode == O_WRITE)
r = devtab[c->type].write(c, buf, n, c->offset);
r = devtab[c->type].write(c, buf, n, c->offset);
+       else
+               error(EFAIL, "kchanio: use only O_READ xor O_WRITE");

spin_lock(&c->lock);
c->offset += r;

spin_lock(&c->lock);
c->offset += r;
@@ -172,51 +92,27 @@ int openmode(uint32_t omode)
/* this is the old plan9 style.  i think they want to turn exec into read,
* and strip off anything higher, and just return the RD/WR style bits.  not
* stuff like ORCLOSE.  the lack of OEXCL might be a bug on their part (it's
/* this is the old plan9 style.  i think they want to turn exec into read,
* and strip off anything higher, and just return the RD/WR style bits.  not
* stuff like ORCLOSE.  the lack of OEXCL might be a bug on their part (it's
-        * the only one of their non-RW-related flags that isn't masked out) */
+        * the only one of their non-RW-related flags that isn't masked out).
+        *
+        * Note that we no longer convert OEXEC/O_EXEC to O_READ, and instead return
+        * just the O_ACCMODE bits. */
if (o >= (OTRUNC | OCEXEC | ORCLOSE | OEXEC))
if (o >= (OTRUNC | OCEXEC | ORCLOSE | OEXEC))
+               error(EINVAL, NULL);
o &= ~(OTRUNC | OCEXEC | ORCLOSE);
if (o > OEXEC)
o &= ~(OTRUNC | OCEXEC | ORCLOSE);
if (o > OEXEC)
+               error(EINVAL, NULL);
if (o == OEXEC)
return o;
#endif
/* no error checking (we have a shitload of flags anyway), and we return the
* basic access modes (RD/WR/ETC) */
if (o == OEXEC)
return o;
#endif
/* no error checking (we have a shitload of flags anyway), and we return the
* basic access modes (RD/WR/ETC) */
-       if (omode == O_EXEC) {
-       return O_RDONLY;
-       }
return omode & O_ACCMODE;
}

return omode & O_ACCMODE;
}

-void fdclose(struct fgrp *f, int fd)
+void fdclose(struct fd_table *fdt, int fd)
{
{
-
-       int i;
-       struct chan *c;
-
-       spin_lock(&f->lock);
-       if (f->closed) {
-               spin_unlock(&f->lock);
-               return;
-       }
-       c = f->fd[fd];
-       if (c == 0) {
-               /* can happen for users with shared fd tables */
-               spin_unlock(&f->lock);
-               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;
-       /* VFS hack: give the FD back to VFS */
-       put_fd(&current->open_files, fd);
-       spin_unlock(&f->lock);
-       cclose(c);
+       close_fd(fdt, fd);
}

int syschdir(char *path)
}

int syschdir(char *path)
@@ -238,30 +134,26 @@ int syschdir(char *path)
return 0;
}

return 0;
}

-int fgrpclose(struct fgrp *f, int fd)
+int sysclose(int fd)
{
ERRSTACK(1);
{
ERRSTACK(1);
+       struct fd_table *fdt = &current->open_files;
+
if (waserror()) {
poperror();
return -1;
}
if (waserror()) {
poperror();
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.
*/
/*
* 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);
+       fdtochan(fdt, fd, -1, 0, 0);
+       fdclose(fdt, fd);
poperror();
return 0;
}

poperror();
return 0;
}

-int sysclose(int fd)
-{
-       return fgrpclose(current->fgrp, fd);
-}
-
int syscreate(char *path, int mode, uint32_t perm)
{
ERRSTACK(2);
int syscreate(char *path, int mode, uint32_t perm)
{
ERRSTACK(2);
@@ -273,120 +165,69 @@ int syscreate(char *path, int mode, uint32_t perm)
return -1;
}

return -1;
}

-       openmode(mode & ~OEXCL);        /* error check only; OEXCL okay here */
+       openmode(mode & ~O_EXCL);       /* error check only; OEXCL okay here */
c = namec(path, Acreate, mode, perm);
if (waserror()) {
cclose(c);
nexterror();
}
c = namec(path, Acreate, mode, perm);
if (waserror()) {
cclose(c);
nexterror();
}
-       fd = newfd(c);
+       fd = newfd(c, mode);    /* 9ns mode is the O_FLAGS and perm is glibc mode */
if (fd < 0)
if (fd < 0)
-               error(Enofd);
+               error(fd, NULL);
poperror();

poperror();
return fd;
}

poperror();

poperror();
return fd;
}

-// This is in need of rework but for now just copy and convert.
-int sysdup(int old, int new)
+int sysdup(int old)
{
{
-       ERRSTACK(2);
+       ERRSTACK(1);
int fd;
int fd;
-       struct chan *c, *oc;
-       struct fgrp *f = current->fgrp;
+       struct chan *c;

if (waserror()) {
poperror();
return -1;
}

if (waserror()) {
poperror();
return -1;
}
-
-       c = fdtochan(current->fgrp, old, -1, 0, 1);
+       c = fdtochan(&current->open_files, old, -1, 0, 1);
if (c->qid.type & QTAUTH) {
cclose(c);
if (c->qid.type & QTAUTH) {
cclose(c);
-               error(Eperm);
-       }
-       fd = new;
-       if (fd != -1) {
-               /* ideally we'll be done with the VFS before we fix this */
-               /* double check the ccloses when you fix this */
-               panic("Need to sync with the VFS");
-               spin_lock(&f->lock);
-               if (f->closed) {
-                       spin_unlock(&f->lock);
-                       cclose(c);
-                       return -1;
-               }
-               if (fd < 0 || growfd(f, fd) < 0) {
-                       spin_unlock(&f->lock);
-                       cclose(c);
-               }
-               if (fd > f->maxfd)
-                       f->maxfd = fd;
-               oc = f->fd[fd];
-               f->fd[fd] = c;
-               spin_unlock(&f->lock);
-               if (oc)
-                       cclose(oc);
-       } else {
-               if (waserror()) {
-                       cclose(c);
-                       nexterror();
-               }
-               fd = newfd(c);
-               if (fd < 0)
-                       error(Enofd);
-               poperror();
+               error(EPERM, NULL);
+       }
+       fd = newfd(c, 0);
+       if (fd < 0) {
+               cclose(c);
+               error(fd, NULL);
}
poperror();
return fd;
}

}
poperror();
return fd;
}

-/* Could pass in the fgrp instead of the proc, but we need the to_proc for now
- * so we can claim a VFS FD */
+/* Could pass in the fdt instead of the proc, but we used to need the to_proc
+ * for now so we can claim a VFS FD.  Careful, we don't close the old chan. */
int sys_dup_to(struct proc *from_proc, unsigned int from_fd,
struct proc *to_proc, unsigned int to_fd)
{
ERRSTACK(1);
int sys_dup_to(struct proc *from_proc, unsigned int from_fd,
struct proc *to_proc, unsigned int to_fd)
{
ERRSTACK(1);
-       struct chan *c, *old_chan;
-       struct fgrp *to_fgrp = to_proc->fgrp;
+       int ret;
+       struct chan *c;

if (waserror()) {
poperror();
return -1;
}

if (waserror()) {
poperror();
return -1;
}
-
-       c = fdtochan(from_proc->fgrp, from_fd, -1, 0, 1);
+       c = fdtochan(&from_proc->open_files, from_fd, -1, 0, 1);
if (c->qid.type & QTAUTH) {
cclose(c);
if (c->qid.type & QTAUTH) {
cclose(c);
-               error(Eperm);
-       }
-
-       spin_lock(&to_fgrp->lock);
-       if (to_fgrp->closed) {
-               spin_unlock(&to_fgrp->lock);
-               cclose(c);
-               error("Can't dup, FGRP closed");
-       }
-       if (claim_fd(&to_proc->open_files, to_fd)) {
-               spin_unlock(&to_fgrp->lock);
-               cclose(c);
-               error("Can't claim FD %d", to_fd);
-       }
-       if (growfd(to_fgrp, to_fd) < 0) {
-               spin_unlock(&to_fgrp->lock);
-               cclose(c);
-               error("Couldn't grow, to_fd %d", to_fd);
+               error(EPERM, NULL);
}
}
-       if (to_fd > to_fgrp->maxfd)
-               to_fgrp->maxfd = to_fd;
-       old_chan = to_fgrp->fd[to_fd];
-       to_fgrp->fd[to_fd] = c;
-       spin_unlock(&to_fgrp->lock);
-       if (old_chan)
-               cclose(old_chan);
-
+       ret = insert_obj_fdt(&to_proc->open_files, c, to_fd, 0, TRUE, FALSE);
+       /* drop the ref from fdtochan.  if insert succeeded, there is one other ref
+        * stored in the FDT */
+       cclose(c);
+       if (ret < 0)
+               error(EFAIL, "Can't insert FD %d into FDG", to_fd);
poperror();
return 0;
}
poperror();
return 0;
}
@@ -401,13 +242,13 @@ char *sysfd2path(int fd)
poperror();
return NULL;
}
poperror();
return NULL;
}
-       c = fdtochan(current->fgrp, fd, -1, 0, 1);
+       c = fdtochan(&current->open_files, fd, -1, 0, 1);
s = NULL;
if (c->name != NULL) {
s = kzmalloc(c->name->len + 1, 0);
if (s == NULL) {
cclose(c);
s = NULL;
if (c->name != NULL) {
s = kzmalloc(c->name->len + 1, 0);
if (s == NULL) {
cclose(c);
-                       error(Enomem);
+                       error(ENOMEM, NULL);
}
memmove(s, c->name->s, c->name->len + 1);
}
}
memmove(s, c->name->s, c->name->len + 1);
}
@@ -427,7 +268,7 @@ int sysfauth(int fd, char *aname)
}

validname(aname, 0);
}

validname(aname, 0);
-       c = fdtochan(current->fgrp, fd, ORDWR, 0, 1);
+       c = fdtochan(&current->open_files, fd, O_RDWR, 0, 1);
if (waserror()) {
cclose(c);
nexterror();
if (waserror()) {
cclose(c);
nexterror();
@@ -444,9 +285,9 @@ int sysfauth(int fd, char *aname)
nexterror();
}

nexterror();
}

-       fd = newfd(ac);
+       fd = newfd(ac, 0);
if (fd < 0)
if (fd < 0)
-               error(Enofd);
+               error(fd, NULL);
poperror();     /* ac */

poperror();
poperror();     /* ac */

poperror();
@@ -467,9 +308,9 @@ int sysfversion(int fd, unsigned int msize, char *vers, unsigned int arglen)

/* check there's a NUL in the version string */
if (arglen == 0 || memchr(vers, 0, arglen) == 0)

/* check there's a NUL in the version string */
if (arglen == 0 || memchr(vers, 0, arglen) == 0)
+               error(EINVAL, NULL);

-       c = fdtochan(current->fgrp, fd, ORDWR, 0, 1);
+       c = fdtochan(&current->open_files, fd, O_RDWR, 0, 1);
if (waserror()) {
cclose(c);
nexterror();
if (waserror()) {
cclose(c);
nexterror();
@@ -488,48 +329,44 @@ int syspipe(int fd)
{
ERRSTACK(1);
struct dev *d;
{
ERRSTACK(1);
struct dev *d;
-       struct fgrp *f;
struct chan *c;
static char *names[] = { "data", "data1" };

struct chan *c;
static char *names[] = { "data", "data1" };

-       f = current->fgrp;
-
-       d = &devtab[devno('|', 0)];
-       c = namec("#|", Atodir, 0, 0);
+       d = &devtab[devno("pipe", 0)];
+       c = 0;
c = 0;
fd = -1;
fd = -1;
if (waserror()) {
c = 0;
fd = -1;
fd = -1;
if (waserror()) {
-               if (c != 0)
+               /* need to remove from the fd table and make sure the chan is closed
+                * exactly once.  if fd[i] >= 0, then the fd is valid (or it was!) and
+                * the fd table has the only ref (newfd() currently decrefs/consumes the
+                * reference).  cclose() doesn't care if you pass it 0 (like kfree()). */
+               if (fd >= 0)
+                       close_fd(&current->open_files, fd);
+               else
cclose(c);
cclose(c);
-               if (c != 0)
+               if (fd >= 0)
+                       close_fd(&current->open_files, fd);
+               else
cclose(c);
cclose(c);
-               if (fd >= 0) {
-                       /* VFS hack */
-                       f->fd[fd] = 0;
-                       put_fd(&current->open_files, fd);
-               }
-               if (fd >= 0) {
-                       /* VFS hack */
-                       f->fd[fd] = 0;
-                       put_fd(&current->open_files, fd);
-               }
poperror();
return -1;
}
poperror();
return -1;
}
+       c = namec("#pipe", Atodir, 0, 0);
c = cclone(c);
c = cclone(c);
-       if (walk(&c, &names, 1, 1, NULL) < 0)
-               error(Egreg);
-       if (walk(&c, &names, 1, 1, NULL) < 0)
-               error(Egreg);
-       c = d->open(c, ORDWR);
-       c = d->open(c, ORDWR);
-       fd = newfd(c);
+       if (walk(&c, &names, 1, FALSE, NULL) < 0)
+               error(EINVAL, NULL);
+       if (walk(&c, &names, 1, FALSE, NULL) < 0)
+               error(EINVAL, NULL);
+       c = d->open(c, O_RDWR);
+       c = d->open(c, O_RDWR);
+       fd = newfd(c, 0);
if (fd < 0)
if (fd < 0)
-               error(Enofd);
-       fd = newfd(c);
+               error(fd, NULL);
+       fd = newfd(c, 0);
if (fd < 0)
if (fd < 0)
-               error(Enofd);
+               error(fd, NULL);
poperror();
return 0;
}
poperror();
return 0;
}
@@ -545,7 +382,7 @@ int sysfwstat(int fd, uint8_t * buf, int n)
}

validstat(buf, n, 0);
}

validstat(buf, n, 0);
-       c = fdtochan(current->fgrp, fd, -1, 1, 1);
+       c = fdtochan(&current->open_files, fd, -1, 1, 1);
if (waserror()) {
cclose(c);
nexterror();
if (waserror()) {
cclose(c);
nexterror();
@@ -565,7 +402,7 @@ long bindmount(struct chan *c, char *old, int flag, char *spec)
struct chan *c1;

if (flag > MMASK || (flag & MORDER) == (MBEFORE | MAFTER))
struct chan *c1;

if (flag > MMASK || (flag & MORDER) == (MBEFORE | MAFTER))
+               error(EINVAL, NULL);

c1 = namec(old, Amount, 0, 0);
if (waserror()) {

c1 = namec(old, Amount, 0, 0);
if (waserror()) {
@@ -628,14 +465,14 @@ int sysmount(int fd, int afd, char *old, int flags, char *spec)
poperror();
return -1;
}
poperror();
return -1;
}
-       bc.c = fdtochan(current->fgrp, fd, ORDWR, 0, 1);
+       bc.c = fdtochan(&current->open_files, fd, O_RDWR, 0, 1);
if (afd >= 0)
if (afd >= 0)
-               ac.c = fdtochan(current->fgrp, afd, ORDWR, 0, 1);
+               ac.c = fdtochan(&current->open_files, afd, O_RDWR, 0, 1);
mntparam.chan = bc.c;
mntparam.authchan = ac.c;
mntparam.spec = spec;
mntparam.flags = flags;
mntparam.chan = bc.c;
mntparam.authchan = ac.c;
mntparam.spec = spec;
mntparam.flags = flags;
-       c0.c = devtab[devno('M', 0)].attach((char *)&mntparam);
+       c0.c = devtab[devno("mnt", 0)].attach((char *)&mntparam);

r = bindmount(c0.c, old, flags, spec);
poperror();

r = bindmount(c0.c, old, flags, spec);
poperror();
@@ -646,7 +483,7 @@ int sysmount(int fd, int afd, char *old, int flags, char *spec)
return r;
}

return r;
}

-int sysunmount(char *old, char *new)
+int sysunmount(char *src_path, char *onto_path)
{
ERRSTACK(1);
volatile struct {
{
ERRSTACK(1);
volatile struct {
@@ -665,15 +502,15 @@ int sysunmount(char *old, char *new)
return -1;
}

return -1;
}

-       cmount.c = namec(new, Amount, 0, 0);
-       if (old != NULL && old != '\0') {
+       cmount.c = namec(onto_path, Amount, 0, 0);
+       if (src_path != NULL && src_path != '\0') {
/*
* This has to be namec(..., Aopen, ...) because
* if arg is something like /srv/cs or /fd/0,
* opening it is the only way to get at the real
* Chan underneath.
*/
/*
* This has to be namec(..., Aopen, ...) because
* if arg 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);
+               cmounted.c = namec(src_path, Aopen, O_READ, 0);
}

cunmount(cmount.c, cmounted.c);
}

cunmount(cmount.c, cmounted.c);
@@ -683,32 +520,40 @@ int sysunmount(char *old, char *new)
return 0;
}

return 0;
}

-int sysopen(char *path, int vfs_flags)
+int sysopenat(int fromfd, char *path, int vfs_flags)
{
{
-       ERRSTACK(2);
+       ERRSTACK(1);
int fd;
int fd;
-       struct chan *c;
+       struct chan *c = 0, *from = 0;

if (waserror()) {

if (waserror()) {
+               cclose(c);
poperror();
return -1;
}
poperror();
return -1;
}
-
openmode(vfs_flags);    /* error check only */
openmode(vfs_flags);    /* error check only */
-       c = namec(path, Aopen, vfs_flags, 0);
-       if (waserror()) {
-               cclose(c);
-               nexterror();
-       }
-       fd = newfd(c);
+       if ((path == '/') || (fromfd == AT_FDCWD)) {
+               c = namec(path, Aopen, vfs_flags, 0);
+       } else {
+               /* We don't cclose from.  namec_from will convert it to the new chan
+                * during the walk process (c).  It'll probably close from internally,
+                * and give us something new for c.  On error, namec_from will cclose
+                * from. */
+               from = fdtochan(&current->open_files, fromfd, -1, FALSE, TRUE);
+               c = namec_from(from, path, Aopen, vfs_flags, 0);
+       }
+       fd = newfd(c, vfs_flags);
if (fd < 0)
if (fd < 0)
-               error(Enofd);
-       poperror();
-
+               error(fd, NULL);
poperror();
return fd;
}

poperror();
return fd;
}

+int sysopen(char *path, int vfs_flags)
+{
+       return sysopenat(AT_FDCWD, path, vfs_flags);
+}
+
long unionread(struct chan *c, void *va, long n)
{
ERRSTACK(1);
long unionread(struct chan *c, void *va, long n)
{
ERRSTACK(1);
@@ -741,7 +586,8 @@ long unionread(struct chan *c, void *va, long n)
} else {
if (c->umc == NULL) {
c->umc = cclone(mount->to);
} else {
if (c->umc == NULL) {
c->umc = cclone(mount->to);
+                                       c->umc = devtab[c->umc->type].open(c->umc,
}

nr = devtab[c->umc->type].read(c->umc, va, n, c->umc->offset);
}

nr = devtab[c->umc->type].read(c->umc, va, n, c->umc->offset);
@@ -793,14 +639,14 @@ static long rread(int fd, void *va, long n, int64_t * offp)
return -1;
}

return -1;
}

-       c = fdtochan(current->fgrp, fd, OREAD, 1, 1);
+       c = fdtochan(&current->open_files, fd, O_READ, 1, 1);
if (waserror()) {
cclose(c);
nexterror();
}

if (n < 0)
if (waserror()) {
cclose(c);
nexterror();
}

if (n < 0)
-               error(Etoosmall);
+               error(EINVAL, NULL);

dir = c->qid.type & QTDIR;

dir = c->qid.type & QTDIR;

@@ -844,7 +690,7 @@ static long rread(int fd, void *va, long n, int64_t * offp)
} else
off = *offp;
if (off < 0)
} else
off = *offp;
if (off < 0)
-                       error(Enegoff);
+                       error(EINVAL, NULL);
if (off == 0) {
if (offp == NULL) {
spin_lock(&c->lock);
if (off == 0) {
if (offp == NULL) {
spin_lock(&c->lock);
@@ -886,6 +732,31 @@ out:
return n;
}

return n;
}

+/* Reads exactly n bytes from chan c, starting at its offset.  Can block, but if
+ * we get 0 back too soon (EOF or error), then we'll error out with Eshort.
+ * That might need a little work - if there was a previous error, then we
+ * clobbered it and only know Eshort but not why we completed early. */
+void read_exactly_n(struct chan *c, void *vp, long n)
+{
+       char *p;
+       long nn;
+       int total = 0, want = n;
+
+       p = vp;
+       while (n > 0) {
+               nn = devtab[c->type].read(c, p, n, c->offset);
+               printd("readn: Got %d@%lld\n", nn, c->offset);
+               if (nn == 0)
+                       error(EFAIL, "%s: wanted %d, got %d", Eshort, want, total);
+               spin_lock(&c->lock);
+               c->offset += nn;
+               spin_unlock(&c->lock);
+               p += nn;
+               n -= nn;
+               total += nn;
+       }
+}
+
long sysread(int fd, void *va, long n)
{
long sysread(int fd, void *va, long n)
{
@@ -936,23 +807,25 @@ int64_t sysseek(int fd, int64_t off, int whence)
return -1;
}

return -1;
}

-       c = fdtochan(current->fgrp, fd, -1, 1, 1);
+       c = fdtochan(&current->open_files, fd, -1, 1, 1);
if (waserror()) {
cclose(c);
nexterror();
}

if (waserror()) {
cclose(c);
nexterror();
}

-       if (devtab[c->type].dc == '|')
-               error(Eisstream);
+       /* TODO: WTF is this?  Is pipe magically the only device that isn't
+        * seekable? */
+       if (!strcmp(devtab[c->type].name, "pipe"))
+               error(EINVAL, NULL);

switch (whence) {
case 0:
if (c->qid.type & QTDIR) {
if (off != 0)

switch (whence) {
case 0:
if (c->qid.type & QTDIR) {
if (off != 0)
-                                       error(Eisdir);
+                                       error(EISDIR, NULL);
unionrewind(c);
} else if (off < 0)
unionrewind(c);
} else if (off < 0)
-                               error(Enegoff);
+                               error(EINVAL, NULL);
spin_lock(&c->lock);    /* lock for int64_t assignment */
c->offset = off;
spin_unlock(&c->lock);
spin_lock(&c->lock);    /* lock for int64_t assignment */
c->offset = off;
spin_unlock(&c->lock);
@@ -960,12 +833,12 @@ int64_t sysseek(int fd, int64_t off, int whence)

case 1:
if (c->qid.type & QTDIR)

case 1:
if (c->qid.type & QTDIR)
-                               error(Eisdir);
+                               error(EISDIR, NULL);
spin_lock(&c->lock);    /* lock for read/write update */
off += c->offset;
if (off < 0) {
spin_unlock(&c->lock);
spin_lock(&c->lock);    /* lock for read/write update */
off += c->offset;
if (off < 0) {
spin_unlock(&c->lock);
-                               error(Enegoff);
+                               error(EINVAL, NULL);
}
c->offset = off;
spin_unlock(&c->lock);
}
c->offset = off;
spin_unlock(&c->lock);
@@ -973,21 +846,21 @@ int64_t sysseek(int fd, int64_t off, int whence)

case 2:
if (c->qid.type & QTDIR)

case 2:
if (c->qid.type & QTDIR)
-                               error(Eisdir);
+                               error(EISDIR, NULL);
dir = chandirstat(c);
if (dir == NULL)
dir = chandirstat(c);
if (dir == NULL)
-                               error("internal error: stat error in seek");
+                               error(EFAIL, "internal error: stat error in seek");
off += dir->length;
kfree(dir);
if (off < 0)
off += dir->length;
kfree(dir);
if (off < 0)
-                               error(Enegoff);
+                               error(EINVAL, NULL);
spin_lock(&c->lock);    /* lock for read/write update */
c->offset = off;
spin_unlock(&c->lock);
break;

default:
spin_lock(&c->lock);    /* lock for read/write update */
c->offset = off;
spin_unlock(&c->lock);
break;

default:
+                       error(EINVAL, NULL);
break;
}
poperror();
break;
}
poperror();
@@ -1004,7 +877,7 @@ void validstat(uint8_t * s, int n, int slashok)
char buf;

if (statcheck(s, n) < 0)
char buf;

if (statcheck(s, n) < 0)
+               error(EINVAL, NULL);
/* verify that name entry is acceptable */
s += STATFIXLEN - 4 * BIT16SZ;  /* location of first string */
/*
/* verify that name entry is acceptable */
s += STATFIXLEN - 4 * BIT16SZ;  /* location of first string */
/*
@@ -1035,7 +908,7 @@ int sysfstat(int fd, uint8_t *buf, int n)
return -1;
}

return -1;
}

-       c = fdtochan(current->fgrp, fd, -1, 0, 1);
+       c = fdtochan(&current->open_files, fd, -1, 0, 1);
if (waserror()) {
cclose(c);
nexterror();
if (waserror()) {
cclose(c);
nexterror();
@@ -1115,24 +988,24 @@ static long rwrite(int fd, void *va, long n, int64_t * offp)
poperror();
return -1;
}
poperror();
return -1;
}
-       c = fdtochan(current->fgrp, fd, OWRITE, 1, 1);
+       c = fdtochan(&current->open_files, fd, O_WRITE, 1, 1);
if (waserror()) {
cclose(c);
nexterror();
}
if (c->qid.type & QTDIR)
if (waserror()) {
cclose(c);
nexterror();
}
if (c->qid.type & QTDIR)
-               error(Eisdir);
+               error(EISDIR, NULL);

if (n < 0)

if (n < 0)
-               error(Etoosmall);
+               error(EINVAL, NULL);

if (offp == NULL) {
/* append changes the offset to the end, and even if we fail later, this
* change will persist */

if (offp == NULL) {
/* append changes the offset to the end, and even if we fail later, this
* change will persist */
-               if (c->flag & CAPPEND) {
+               if (c->flag & O_APPEND) {
dir = chandirstat(c);
if (!dir)
dir = chandirstat(c);
if (!dir)
-                               error("internal error: stat error in append write");
+                               error(EFAIL, "internal error: stat error in append write");
spin_lock(&c->lock);    /* legacy lock for int64 assignment */
c->offset = dir->length;
spin_unlock(&c->lock);
spin_lock(&c->lock);    /* legacy lock for int64 assignment */
c->offset = dir->length;
spin_unlock(&c->lock);
@@ -1154,7 +1027,7 @@ static long rwrite(int fd, void *va, long n, int64_t * offp)
nexterror();
}
if (off < 0)
nexterror();
}
if (off < 0)
-               error(Enegoff);
+               error(EINVAL, NULL);
m = devtab[c->type].write(c, va, n, off);
poperror();

m = devtab[c->type].write(c, va, n, off);
poperror();

@@ -1274,7 +1147,7 @@ struct dir *sysdirfstat(int fd)
return NULL;
}

return NULL;
}

-       c = fdtochan(current->fgrp, fd, -1, 0, 1);
+       c = fdtochan(&current->open_files, fd, -1, 0, 1);
if (waserror()) {
cclose(c);
nexterror();
if (waserror()) {
cclose(c);
nexterror();
@@ -1340,11 +1213,11 @@ static long dirpackage(uint8_t * buf, long ts, struct kdirent **d)
}

if (i != ts)
}

if (i != ts)

*d = kzmalloc(n * sizeof(**d) + ss, 0);
if (*d == NULL)

*d = kzmalloc(n * sizeof(**d) + ss, 0);
if (*d == NULL)
-               error(Enomem);
+               error(ENOMEM, NULL);

/*
* then convert all buffers

/*
* then convert all buffers
@@ -1356,7 +1229,7 @@ static long dirpackage(uint8_t * buf, long ts, struct kdirent **d)
if (nn >= n || /*convM2D */ convM2kdirent(&buf[i], m, *d + nn, s) != m) {
kfree(*d);
*d = NULL;
if (nn >= n || /*convM2D */ convM2kdirent(&buf[i], m, *d + nn, s) != m) {
kfree(*d);
*d = NULL;
}
nn++;
s += m;
}
nn++;
s += m;
@@ -1378,7 +1251,7 @@ long sysdirread(int fd, struct kdirent **d)
}
if (buf == NULL)
}
if (buf == NULL)
-               error(Enomem);
+               error(ENOMEM, NULL);
if (waserror()) {
kfree(buf);
nexterror();
if (waserror()) {
kfree(buf);
nexterror();
@@ -1398,7 +1271,7 @@ int sysiounit(int fd)
struct chan *c;
int n;

struct chan *c;
int n;

-       c = fdtochan(current->fgrp, fd, -1, 0, 1);
+       c = fdtochan(&current->open_files, fd, -1, 0, 1);
if (waserror()) {
cclose(c);
poperror();
if (waserror()) {
cclose(c);
poperror();
@@ -1410,43 +1283,6 @@ int sysiounit(int fd)
return n;
}

return n;
}

-/* Notes on concurrency:
- * - Can't hold spinlocks while we call cclose, since it might sleep eventually.
- * - We're called from proc_destroy, so we could have concurrent openers trying
- *   to add to the group (other syscalls), hence the "closed" flag.
- * - dot and slash chans are dealt with in proc_free.  its difficult to close
- *   and zero those with concurrent syscalls, since those are a source of krefs.
- * - the memory is freed in proc_free().  need to wait to do it, since we can
- *   have concurrent accesses to fgrp before free.
- * - Once we lock and set closed, no further additions can happen.  To simplify
- *   our closes, we also allow multiple calls to this func (though that should
- *   never happen with the current code). */
-void close_9ns_files(struct proc *p, bool only_cloexec)
-{
-
-       struct fgrp *f = p->fgrp;
-
-       spin_lock(&f->lock);
-       if (f->closed) {
-               spin_unlock(&f->lock);
-               warn("Unexpected double-close");
-               return;
-       }
-       if (!only_cloexec)
-               f->closed = TRUE;
-       spin_unlock(&f->lock);
-
-       /* maxfd is a legit val, not a +1 */
-       for (int i = 0; i <= f->maxfd; i++) {
-               if (!f->fd[i])
-                       continue;
-               if (only_cloexec && !(f->fd[i]->flag & CCEXEC))
-                       continue;
-               cclose(f->fd[i]);
-               f->fd[i] = 0;
-       }
-}
-
void print_chaninfo(struct chan *c)
{

void print_chaninfo(struct chan *c)
{

@@ -1466,24 +1302,9 @@ void print_chaninfo(struct chan *c)
printk("\n");
}

printk("\n");
}

-void print_9ns_files(struct proc *p)
-{
-
-       struct fgrp *f = p->fgrp;
-       spin_lock(&f->lock);
-       printk("9ns files for proc %d:\n", p->pid);
-       /* maxfd is a legit val, not a +1 */
-       for (int i = 0; i <= f->maxfd; i++) {
-               if (!f->fd[i])
-                       continue;
-               printk("\t9fs %4d, ", i);
-               print_chaninfo(f->fd[i]);
-       }
-       spin_unlock(&f->lock);
-}
-
-/* TODO: 9ns ns inheritance flags: Shared, copied, or empty.  Looks like we're
- * copying the fgrp, and sharing the pgrp. */
+/* TODO: 9ns ns inheritance flags: Shared, copied, or empty.  The old fgrp is
+ * managed by the fd_table, which is handled outside this function.  We share
+ * the pgrp. */
int plan9setup(struct proc *new_proc, struct proc *parent, int flags)
{

int plan9setup(struct proc *new_proc, struct proc *parent, int flags)
{

@@ -1497,31 +1318,24 @@ int plan9setup(struct proc *new_proc, struct proc *parent, int flags)
}
if (!parent) {
/* We are probably spawned by the kernel directly, and have no parent to
}
if (!parent) {
/* We are probably spawned by the kernel directly, and have no parent to
-                * inherit from.  Be sure to set up fgrp/pgrp before calling namec().
+                * inherit from.
*
* TODO: One problem is namec wants a current set for things like
* genbuf.  So we'll use new_proc for this bootstrapping.  Note
* switch_to() also loads the cr3. */
*
* TODO: One problem is namec wants a current set for things like
* genbuf.  So we'll use new_proc for this bootstrapping.  Note
* switch_to() also loads the cr3. */
-               new_proc->fgrp = newfgrp();
new_proc->pgrp = newpgrp();
old_current = switch_to(new_proc);
new_proc->pgrp = newpgrp();
old_current = switch_to(new_proc);
-               new_proc->slash = namec("#r", Atodir, 0, 0);
+               new_proc->slash = namec("#root", Atodir, 0, 0);
if (!new_proc->slash)
panic("no root device");
switch_back(new_proc, old_current);
if (!new_proc->slash)
panic("no root device");
switch_back(new_proc, old_current);
-               /* Want the name to be "/" instead of "#r" */
+               /* Want the name to be "/" instead of "#root" */
cnameclose(new_proc->slash->name);
new_proc->slash->name = newcname("/");
new_proc->dot = cclone(new_proc->slash);
poperror();
return 0;
}
cnameclose(new_proc->slash->name);
new_proc->slash->name = newcname("/");
new_proc->dot = cclone(new_proc->slash);
poperror();
return 0;
}
-       /* When we use the old fgrp, we have copy semantics: do not change this
-        * without revisiting proc_destroy, close_9ns_files, and closefgrp. */
-       if (flags & PROC_DUP_FGRP)
-               new_proc->fgrp = dupfgrp(new_proc, parent->fgrp);
-       else
-               new_proc->fgrp = newfgrp();
/* Shared semantics */
kref_get(&parent->pgrp->ref, 1);
new_proc->pgrp = parent->pgrp;
/* Shared semantics */
kref_get(&parent->pgrp->ref, 1);
new_proc->pgrp = parent->pgrp;
@@ -1581,17 +1395,22 @@ int fd_getfl(int fd)
poperror();
return -1;
}
poperror();
return -1;
}
-       c = fdtochan(current->fgrp, fd, -1, 0, 1);
+       c = fdtochan(&current->open_files, fd, -1, 0, 1);

ret = c->mode;

ret = c->mode;
-       if (c->flag & CAPPEND)
-               ret |= O_APPEND;
+       ret |= c->flag & CEXTERNAL_FLAGS;

cclose(c);
poperror();
return ret;
}

cclose(c);
poperror();
return ret;
}

+static bool cexternal_flags_differ(int set1, int set2, int flags)
+{
+       flags &= CEXTERNAL_FLAGS;
+       return (set1 & flags) ^ (set2 & flags);
+}
+
int fd_setfl(int fd, int flags)
{
ERRSTACK(1);
int fd_setfl(int fd, int flags)
{
ERRSTACK(1);
@@ -1601,11 +1420,14 @@ int fd_setfl(int fd, int flags)
poperror();
return -1;
}
poperror();
return -1;
}
-       c = fdtochan(current->fgrp, fd, -1, 0, 1);
-
-       if (flags & O_APPEND)
-               c->flag |= CAPPEND;
-
+       c = fdtochan(&current->open_files, fd, -1, 0, 1);
+       if (cexternal_flags_differ(flags, c->flag, O_CLOEXEC)) {
+               /* TODO: The whole CCEXEC / O_CLOEXEC on 9ns needs work */
+               error(EINVAL, "can't toggle O_CLOEXEC with setfl");
+       }
+       if (cexternal_flags_differ(flags, c->flag, O_PATH))
+               error(EINVAL, "can't toggle O_PATH with setfl");
+       c->flag = (c->flag & ~CEXTERNAL_FLAGS) | (flags & CEXTERNAL_FLAGS);
cclose(c);
poperror();
return 0;
cclose(c);
poperror();
return 0;