read_exactly_n()
[akaros.git] / kern / drivers / dev / mnt.c
index 55d4943..4a92f03 100644 (file)
 
 #define MAXRPC (IOHDRSZ+8192)
 
+static __inline int isxdigit(int c)
+{
+       if ((c >= '0') && (c <= '9'))
+               return 1;
+       if ((c >= 'a') && (c <= 'f'))
+               return 1;
+       if ((c >= 'A') && (c <= 'F'))
+               return 1;
+       return 0;
+}
+
 struct mntrpc {
        struct chan *c;                         /* Channel for whom we are working */
        struct mntrpc *list;            /* Free/pending list */
@@ -293,7 +304,7 @@ struct chan *mntauth(struct chan *c, char *spec)
 
        c->qid = r->reply.aqid;
        c->mchan = m->c;
-       kref_get(&m->c->ref, 1);
+       chan_incref(m->c);
        c->mqid = c->qid;
        c->mode = ORDWR;
 
@@ -359,7 +370,7 @@ static struct chan *mntattach(char *muxattach)
 
        c->qid = r->reply.qid;
        c->mchan = m->c;
-       kref_get(&m->c->ref, 1);
+       chan_incref(m->c);
        c->mqid = c->qid;
 
        poperror();     /* r */
@@ -454,7 +465,7 @@ static struct walkqid *mntwalk(struct chan *c, struct chan *nc, char **name,
                if (wq->clone != c) {
                        wq->clone->type = c->type;
                        wq->clone->mchan = c->mchan;
-                       kref_get(&c->mchan->ref, 1);
+                       chan_incref(c->mchan);
                }
                if (r->reply.nwqid > 0)
                        wq->clone->qid = r->reply.wqid[r->reply.nwqid - 1];
@@ -495,7 +506,7 @@ static int mntstat(struct chan *c, uint8_t * dp, int n)
        } else {
                n = r->reply.nstat;
                memmove(dp, r->reply.stat, n);
-               validstat(dp, n);
+               validstat(dp, n, 0);
                mntdirfix(dp, c);
        }
        poperror();
@@ -643,10 +654,17 @@ static int mntwstat(struct chan *c, uint8_t * dp, int n)
        return n;
 }
 
+/* the servers should either return units of whole directory entries
+ * OR support seeking to an arbitrary place. One or other.
+ * Both are fine, but at least one is a minimum.
+ * If the return a partial result, but more than one result,
+ * we'll return a shorter read and the next offset will be aligned
+ */
 static long mntread(struct chan *c, void *buf, long n, int64_t off)
 {
        uint8_t *p, *e;
        int nc, cache, isdir, dirlen;
+       int numdirent = 0;
 
        isdir = 0;
        cache = c->flag & CCACHE;
@@ -671,16 +689,24 @@ static long mntread(struct chan *c, void *buf, long n, int64_t off)
        }
 
        n = mntrdwr(Tread, c, buf, n, off);
+
        if (isdir) {
                for (e = &p[n]; p + BIT16SZ < e; p += dirlen) {
                        dirlen = BIT16SZ + GBIT16(p);
-                       if (p + dirlen > e)
+                       if (p + dirlen > e){
                                break;
-                       validstat(p, dirlen);
+                       }
+                       validstat(p, dirlen, 0);
                        mntdirfix(p, c);
+                       numdirent += dirlen;
+               }
+               if (p != e) {
+                       //error(Esbadstat);
+                       /* not really. Maybe the server supports
+                        * arbitrary seek like go9p now does.
+                        */
+                       n = numdirent;
                }
-               if (p != e)
-                       error(Esbadstat);
        }
        return n;
 }
@@ -746,6 +772,7 @@ void mountrpc(struct mnt *m, struct mntrpc *r)
 {
        char *sn, *cn;
        int t;
+       char *e;
 
        r->reply.tag = 0;
        r->reply.type = Tmax;   /* can't ever be a valid message type */
@@ -755,7 +782,20 @@ void mountrpc(struct mnt *m, struct mntrpc *r)
        t = r->reply.type;
        switch (t) {
                case Rerror:
-                       error(r->reply.ename);
+                       /* in Akaros mode, first four characters
+                        * are errno.
+                        */
+                       e = r->reply.ename;
+                       /* If it is in the format "XXXX <at least one char>" */
+                       if ((strlen(e) > 5) && isxdigit(e[0]) &&
+                               isxdigit(e[1]) &&
+                               isxdigit(e[2]) &&
+                               isxdigit(e[3])) {
+                               int errno = strtoul(e, NULL, 16);
+                               set_errno(errno);
+                               error(&r->reply.ename[5]);
+                       } else
+                               error(r->reply.ename);
                case Rflush:
                        error(Eintr);
                default:
@@ -783,7 +823,13 @@ void mountio(struct mnt *m, struct mntrpc *r)
        while (waserror()) {
                if (m->rip == current)
                        mntgate(m);
-               if (strcmp(current_errstr(), Eintr) != 0) {
+               /* Syscall aborts are like Plan 9 Eintr.  For those, we need to change
+                * the old request to a flsh (mntflushalloc) and try again.  We'll
+                * always try to flush, and you can't get out until the flush either
+                * succeeds or errors out with a non-abort/Eintr error. */
+               if (strcmp(current_errstr(), "syscall aborted") &&
+                   strcmp(current_errstr(), Eintr)) {
+                       /* all other errors (not abort or Eintr) */
                        mntflushfree(m, r);
                        nexterror();
                }
@@ -897,6 +943,8 @@ int mntrpcread(struct mnt *m, struct mntrpc *r)
        *l = NULL;
        do {
                b = qremove(m->q);
+               /* TODO: have better block helpers for this and the memmove below */
+               b = linearizeblock(b);
                if (hlen > 0) {
                        b->rp += hlen;
                        len -= hlen;
@@ -1055,6 +1103,7 @@ struct mntrpc *mntralloc(struct chan *c, uint32_t msize)
                        spin_unlock(&mntalloc.l);
                        exhausted("mount rpc header");
                }
+               rendez_init(&new->r);
                /*
                 * The header is split from the data buffer as
                 * mountmux may swap the buffer with another header.