blob: 5fde0b90340f710668ed846323eb8d826eb8fcee [file] [log] [blame]
#include <vfs.h>
#include <kfs.h>
#include <slab.h>
#include <kmalloc.h>
#include <kref.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <error.h>
#include <cpio.h>
#include <pmap.h>
#include <smp.h>
#include <fcall.h>
//extern uint32_t kerndate;
uint32_t kerndate = 0;
void
mkqid(struct qid *q, int64_t path, uint32_t vers, int type)
{
q->type = type;
q->vers = vers;
q->path = path;
}
void
devdir(struct chan *c, struct qid qid, char *n, int64_t length, char *user,
long perm, struct dir *db)
{
db->name = n;
if (c->flag & CMSG)
qid.type |= QTMOUNT;
db->qid = qid;
/*
* When called via devwalk c->dev is NULL
* until the walk succeeds.
*/
if (c->dev != NULL)
db->type = c->dev->dc;
else
db->type = -1;
db->dev = c->devno;
db->mode = perm;
db->mode |= qid.type << 24;
db->atime = 0; //seconds
db->mtime = kerndate;
db->length = length;
db->uid = user;
db->gid = eve;
db->muid = user;
}
/*
* (here, struct devgen is the prototype; devgen is the function in dev.c.)
*
* a struct devgen is expected to return the directory entry for ".."
* if you pass it s==DEVDOTDOT (-1). otherwise...
*
* there are two contradictory rules.
*
* (i) if c is a directory, a struct devgen is expected to list its children
* as you iterate s.
*
* (ii) whether or not c is a directory, a struct devgen is expected to list
* its siblings as you iterate s.
*
* devgen always returns the list of children in the root
* directory. thus it follows (i) when c is the root and (ii) otherwise.
* many other struct devgens follow (i) when c is a directory and (ii) otherwise.
*
* devwalk assumes (i). it knows that devgen breaks (i)
* for children that are themselves directories, and explicitly catches them.
*
* devstat assumes (ii). if the struct devgen in question follows (i)
* for this particular c, devstat will not find the necessary info.
* with our particular struct devgen functions, this happens only for
* directories, so devstat makes something assuming
* c->name, c->qid, eve, DMDIR|0555.
*
* devdirread assumes (i). the callers have to make sure
* that the struct devgen satisfies (i) for the chan being read.
*/
/*
* the zeroth element of the table MUST be the directory itself for ..
*/
int
devgen(struct chan *c, char *name, struct dirtab *tab, int ntab, int i,
struct dir *dp)
{
if (tab == 0)
return -1;
if (i == DEVDOTDOT) {
/* nothing */
} else if (name) {
for (i = 1; i < ntab; i++)
if (strcmp(tab[i].name, name) == 0)
break;
if (i == ntab)
return -1;
tab += i;
} else {
/* skip over the first element, that for . itself */
i++;
if (i >= ntab)
return -1;
tab += i;
}
devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
return 1;
}
void devreset()
{
}
void devinit()
{
}
void devshutdown()
{
}
struct chan *devattach(int dc, char *spec)
{
struct chan *c;
char *buf;
int len = strlen(spec) + 2 + 1; /* 2 for #c, 1 for \0 */
/*
* There are no error checks here because
* this can only be called from the driver of dc
* which pretty much guarantees devtabget will
* succeed.
*/
c = newchan();
mkqid(&c->qid, 0, 0, QTDIR);
c->dev = devtabget(dc, 0);
if (spec == NULL)
spec = "";
buf = kzmalloc(len, KMALLOC_WAIT);
snprintf(buf, len, "#%c%s", dc, spec);
c->path = newpath(buf);
kfree(buf);
return c;
}
struct chan *devclone(struct chan *c)
{
struct chan *nc;
if (c->flag & COPEN) {
panic("devclone: file of type %c already open\n",
c->dev != NULL ? c->dev->dc : -1);
}
nc = newchan();
/*
* The caller fills dev in if and when necessary.
nc->dev = NULL; //XDYNXX
*/
nc->devno = c->devno;
nc->mode = c->mode;
nc->qid = c->qid;
nc->offset = c->offset;
nc->umh = NULL;
nc->aux = c->aux;
nc->mqid = c->mqid;
nc->mc = c->mc;
return nc;
}
struct walkqid *devwalk(struct chan *c, struct chan *nc, char **name, int nname,
struct dirtab *tab, int ntab, devgen_t * gen)
{
ERRSTACK(2);
int i, j;
volatile int alloc; /* to keep waserror from optimizing this out */
struct walkqid *wq;
char *n;
struct dir dir;
if (nname > 0)
isdir(c);
alloc = 0;
wq = kzmalloc(sizeof(struct walkqid) + (nname) * sizeof(struct qid),
KMALLOC_WAIT);
if (waserror()) {
if (alloc && wq->clone != NULL)
cclose(wq->clone);
kfree(wq);
poperror();
return NULL;
}
if (nc == NULL) {
nc = devclone(c);
/*
* nc->dev remains NULL for now. //XDYNX
*/
alloc = 1;
}
wq->clone = nc;
dir.qid.path = 0;
for (j = 0; j < nname; j++) {
if (!(nc->qid.type & QTDIR)) {
if (j == 0)
error(Enotdir);
goto Done;
}
n = name[j];
if (strcmp(n, ".") == 0) {
Accept:
wq->qid[wq->nqid++] = nc->qid;
continue;
}
if (strcmp(n, "..") == 0) {
/*
* Use c->dev->name in the error because
* nc->dev should be NULL here.
*/
if ((*gen) (nc, NULL, tab, ntab, DEVDOTDOT, &dir) != 1) {
printk("devgen walk .. in dev%s %#llux broken\n",
c->dev->name, nc->qid.path);
error("broken devgen");
}
nc->qid = dir.qid;
goto Accept;
}
/*
* Ugly problem: If we're using devgen, make sure we're
* walking the directory itself, represented by the first
* entry in the table, and not trying to step into a sub-
* directory of the table, e.g. /net/net. struct devgen itself
* should take care of the problem, but it doesn't have
* the necessary information (that we're doing a walk).
*/
if (gen == devgen && nc->qid.path != tab[0].qid.path)
goto Notfound;
dir.qid.path = 0;
for (i = 0;; i++) {
switch ((*gen) (nc, n, tab, ntab, i, &dir)) {
case -1:
Notfound:
if (j == 0)
error(Enonexist);
set_errstr(Enonexist);
goto Done;
case 0:
continue;
case 1:
printd("DEVWALK looking for %s, found %s, path %p", n,
dir.name, dir.qid.path);
if (strcmp(n, dir.name) == 0) {
printd(" MATCH!\n");
nc->qid = dir.qid;
goto Accept;
}
printd("\n");
continue;
}
}
}
/*
* We processed at least one name, so will return some data.
* If we didn't process all nname entries succesfully, we drop
* the cloned channel and return just the struct qids of the walks.
*/
Done:
poperror();
if (wq->nqid < nname) {
if (alloc)
cclose(wq->clone);
wq->clone = NULL;
} else if (wq->clone) {
/* attach cloned channel to same device */
//what goes here: //XDYNX
// ->dev must be NULL because can't walk an open chan, right?
// what about ref count on dev?
wq->clone->dev = c->dev;
//if(wq->clone->dev) //XDYNX
// devtabincr(wq->clone->dev);
}
return wq;
}
long
devstat(struct chan *c, uint8_t * db, long n, struct dirtab *tab, int ntab,
devgen_t * gen)
{
int i;
struct dir dir;
char *p, *elem;
printd("\nFresh Devstat, chan path %p\n", c->qid.path);
dir.qid.path = 0;
for (i = 0;; i++) {
switch ((*gen) (c, NULL, tab, ntab, i, &dir)) {
case -1:
if (c->qid.type & QTDIR) {
if (c->path == NULL)
elem = "???";
else if (strcmp(c->path->s, "/") == 0)
elem = "/";
else
for (elem = p = c->path->s; *p; p++)
if (*p == '/')
elem = p + 1;
devdir(c, c->qid, elem, 0, eve, DMDIR | 0555, &dir);
n = convD2M(&dir, db, n);
if (n == 0)
error(Ebadarg);
return n;
}
error(Enonexist);
case 0:
printd("DEVSTAT got 0\n");
break;
case 1:
printd("DEVSTAT gen returns path %p name %s, want path %p\n",
dir.qid.path, dir.name, c->qid.path);
if (c->qid.path == dir.qid.path) {
if (c->flag & CMSG)
dir.mode |= DMMOUNT;
n = convD2M(&dir, db, n);
if (n == 0)
error(Ebadarg);
return n;
}
break;
}
}
}
long
devdirread(struct chan *c, char *d, long n, struct dirtab *tab, int ntab,
devgen_t * gen)
{
long m, dsz;
struct dir dir;
dir.qid.path = 0;
for (m = 0; m < n; c->dri++) {
switch ((*gen) (c, NULL, tab, ntab, c->dri, &dir)) {
case -1:
return m;
case 0:
break;
case 1:
dsz = convD2M(&dir, (uint8_t *) d, n - m);
if (dsz <= BIT16SZ) { /* <= not < because this isn't stat; read is stuck */
if (m == 0)
error(Eshort);
return m;
}
m += dsz;
d += dsz;
break;
}
}
return m;
}
/*
* error(Eperm) if open permission not granted for up->user.
*/
void devpermcheck(char *fileuid, int perm, int omode)
{
int t;
static int access[] = { 0400, 0200, 0600, 0100 };
if (strcmp(current->user, fileuid) == 0)
perm <<= 0;
else if (strcmp(current->user, eve) == 0)
perm <<= 3;
else
perm <<= 6;
t = access[omode & 3];
if ((t & perm) != t)
error(Eperm);
}
struct chan *devopen(struct chan *c, int omode, struct dirtab *tab, int ntab,
devgen_t * gen)
{
int i;
struct dir dir;
dir.qid.path = 0;
for (i = 0;; i++) {
switch ((*gen) (c, NULL, tab, ntab, i, &dir)) {
case -1:
goto Return;
case 0:
break;
case 1:
if (c->qid.path == dir.qid.path) {
devpermcheck(dir.uid, dir.mode, omode);
goto Return;
}
break;
}
}
Return:
c->offset = 0;
if ((c->qid.type & QTDIR) && omode != OREAD)
error(Eperm);
c->mode = openmode(omode);
c->flag |= COPEN;
return c;
}
void devcreate(struct chan *a, char *b, int c, int d)
{
error(Eperm);
}
/* no analog in akaros yet. */
struct block *devbread(struct chan *c, long n, int64_t offset)
{
ERRSTACK(2);
struct block *bp;
bp = allocb(n);
if (bp == 0)
error(Enomem);
if (waserror()) {
freeb(bp);
nexterror();
}
bp->wp += c->dev->read(c, bp->wp, n, offset);
poperror();
return bp;
}
long
devbwrite(struct chan *c, struct block *bp, int64_t offset)
{
ERRSTACK(2);
long n;
if (waserror()) {
freeb(bp);
nexterror();
}
n = c->dev->write(c, bp->rp, BLEN(bp), offset);
poperror();
freeb(bp);
return n;
}
void devremove(struct chan *c)
{
error(Eperm);
}
long devwstat(struct chan *c, uint8_t * a, long b)
{
error(Eperm);
return 0;
}
void devpower(int onoff)
{
error(Eperm);
}
int devconfig(int a, char *b, void *v)
{
error(Eperm);
return 0;
}
char *devchaninfo(struct chan *chan, char *ret, size_t ret_l)
{
snprintf(ret, ret_l, "qid.path: %p, qid.type: %02x\n", chan->qid.path,
chan->qid.type);
return ret;
}