Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include "dat.h"
#include "fns.h"
#include "iso9660.h"

static void     ireset(void);
static int      iattach(Xfile*);
static void     iclone(Xfile*, Xfile*);
static void     iwalkup(Xfile*);
static void     iwalk(Xfile*, char*);
static void     iopen(Xfile*, int);
static void     icreate(Xfile*, char*, long, int);
static long     ireaddir(Xfile*, uchar*, long, long);
static long     iread(Xfile*, char*, vlong, long);
static long     iwrite(Xfile*, char*, vlong, long);
static void     iclunk(Xfile*);
static void     iremove(Xfile*);
static void     istat(Xfile*, Dir*);
static void     iwstat(Xfile*, Dir*);

static char*    nstr(uchar*, int);
static char*    rdate(uchar*, int);
static int      getcontin(Xdata*, uchar*, uchar**);
static int      getdrec(Xfile*, void*);
static void     ungetdrec(Xfile*);
static int      opendotdot(Xfile*, Xfile*);
static int      showdrec(int, int, void*);
static long     gtime(uchar*);
static long     l16(void*);
static long     l32(void*);
static vlong    l64(void *);
static void     newdrec(Xfile*, Drec*);
static int      rzdir(Xfs*, Dir*, int, Drec*);

Xfsub   isosub =
{
        ireset, iattach, iclone, iwalkup, iwalk, iopen, icreate,
        ireaddir, iread, iwrite, iclunk, iremove, istat, iwstat
};

static vlong
fakemax(vlong len)
{
        if(len == (1UL << 31) - 1)      /* max. 9660 size? */
                len = (1ULL << 63) - 1; /* pretend it's vast */
        return len;
}

static void
ireset(void)
{}

static int
iattach(Xfile *root)
{
        Xfs *cd = root->xf;
        Iobuf *p; Voldesc *v; Isofile *fp; Drec *dp;
        int fmt, blksize, i, n, l, haveplan9;
        Iobuf *dirp;
        uchar dbuf[256];
        Drec *rd = (Drec *)dbuf;
        uchar *q, *s;

        dirp = nil;
        blksize = 0;
        fmt = 0;
        dp = nil;
        haveplan9 = 0;
        for(i=VOLDESC;i<VOLDESC+100; i++){      /* +100 for sanity */
                p = getbuf(cd->d, i);
                v = (Voldesc*)(p->iobuf);
                if(memcmp(v->byte, "\01CD001\01", 7) == 0){             /* iso */
                        if(dirp)
                                putbuf(dirp);
                        dirp = p;
                        fmt = 'z';
                        dp = (Drec*)v->z.desc.rootdir;
                        blksize = l16(v->z.desc.blksize);
                        chat("iso, blksize=%d...", blksize);

                        v = (Voldesc*)(dirp->iobuf);
                        haveplan9 = (strncmp((char*)v->z.boot.sysid, "PLAN 9", 6)==0);
                        if(haveplan9){
                                if(noplan9) {
                                        chat("ignoring plan9");
                                        haveplan9 = 0;
                                } else {
                                        fmt = '9';
                                        chat("plan9 iso...");
                                }
                        }
                        continue;
                }

                if(memcmp(&v->byte[8], "\01CDROM\01", 7) == 0){ /* high sierra */
                        if(dirp)
                                putbuf(dirp);
                        dirp = p;
                        fmt = 'r';
                        dp = (Drec*)v->r.desc.rootdir;
                        blksize = l16(v->r.desc.blksize);
                        chat("high sierra, blksize=%d...", blksize);
                        continue;
                }

                if(haveplan9==0 && !nojoliet
                && memcmp(v->byte, "\02CD001\01", 7) == 0){
chat("%d %d\n", haveplan9, nojoliet);
                        /*
                         * The right thing to do is walk the escape sequences looking
                         * for one of 25 2F 4[035], but Microsoft seems to not honor
                         * the format, which makes it hard to walk over.
                         */
                        q = v->z.desc.escapes;
                        if(q[0] == 0x25 && q[1] == 0x2F && (q[2] == 0x40 || q[2] == 0x43 || q[2] == 0x45)){     /* Joliet, it appears */
                                if(dirp)
                                        putbuf(dirp);
                                dirp = p;
                                fmt = 'J';
                                dp = (Drec*)v->z.desc.rootdir;
                                if(blksize != l16(v->z.desc.blksize))
                                        fprint(2, "warning: suspicious Joliet blocksize\n");
                                chat("joliet...");
                                continue;
                        }
                }
                putbuf(p);
                if(v->byte[0] == 0xFF)
                        break;
        }

        if(fmt == 0){
                if(dirp)
                        putbuf(dirp);
                return -1;
        }
        assert(dirp != nil);

        if(chatty)
                showdrec(2, fmt, dp);
        if(blksize > Sectorsize){
                chat("blksize too big...");
                putbuf(dirp);
                return -1;
        }
        if(waserror()){
                putbuf(dirp);
                nexterror();
        }
        root->len = sizeof(Isofile) - sizeof(Drec) + dp->reclen;
        root->ptr = fp = ealloc(root->len);

        if(haveplan9)
                root->xf->isplan9 = 1;

        fp->fmt = fmt;
        fp->blksize = blksize;
        fp->offset = 0;
        fp->doffset = 0;
        memmove(&fp->d, dp, dp->reclen);
        root->qid.path = l32(dp->addr);
        root->qid.type = QTDIR;
        putbuf(dirp);
        poperror();
        if(getdrec(root, rd) >= 0){
                n = rd->reclen-(34+rd->namelen);
                s = (uchar*)rd->name + rd->namelen;
                if((uintptr)s & 1){
                        s++;
                        n--;
                }
                if(n >= 7 && s[0] == 'S' && s[1] == 'P' && s[2] == 7 &&
                   s[3] == 1 && s[4] == 0xBE && s[5] == 0xEF){
                        root->xf->issusp = 1;
                        root->xf->suspoff = s[6];
                        n -= root->xf->suspoff;
                        s += root->xf->suspoff;
                        for(; n >= 4; s += l, n -= l){
                                l = s[2];
                                if(s[0] == 'E' && s[1] == 'R'){
                                        if(!norock && s[4] == 10 && memcmp(s+8, "RRIP_1991A", 10) == 0)
                                                root->xf->isrock = 1;
                                        break;
                                } else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){
                                        n = getcontin(root->xf->d, s, &s);
                                        continue;
                                } else if(s[0] == 'R' && s[1] == 'R'){
                                        if(!norock)
                                                root->xf->isrock = 1;
                                        break;
                                } else if(s[0] == 'S' && s[1] == 'T')
                                        break;
                        }
                }
        }
        if(root->xf->isrock)
                chat("Rock Ridge...");
        fp->offset = 0;
        fp->doffset = 0;
        return 0;
}

static void
iclone(Xfile *of, Xfile *nf)
{
        USED(of, nf);
}

static void
iwalkup(Xfile *f)
{
        vlong paddr;
        uchar dbuf[256];
        Drec *d = (Drec *)dbuf;
        Xfile pf, ppf;
        Isofile piso, ppiso;

        memset(&pf, 0, sizeof pf);
        memset(&ppf, 0, sizeof ppf);
        pf.ptr = &piso;
        ppf.ptr = &ppiso;
        if(opendotdot(f, &pf) < 0)
                error("can't open pf");
        paddr = l32(pf.ptr->d.addr);
        if(l32(f->ptr->d.addr) == paddr)
                return;
        if(opendotdot(&pf, &ppf) < 0)
                error("can't open ppf");
        while(getdrec(&ppf, d) >= 0){
                if(l32(d->addr) == paddr){
                        newdrec(f, d);
                        f->qid.path = paddr;
                        f->qid.type = QTDIR;
                        return;
                }
        }
        error("can't find addr of ..");
}

static int
casestrcmp(int isplan9, char *a, char *b)
{
        if(isplan9)
                return strcmp(a, b);
        return cistrcmp(a, b);
}

static void
iwalk(Xfile *f, char *name)
{
        Isofile *ip = f->ptr;
        uchar dbuf[256];
        char nbuf[4*Maxname];
        Drec *d = (Drec*)dbuf;
        Dir dir;
        char *p;
        int len, vers, dvers;

        vers = -1;
        if(p = strchr(name, ';')) {     /* assign = */
                len = p-name;
                if(len >= Maxname)
                        len = Maxname-1;
                memmove(nbuf, name, len);
                vers = strtoul(p+1, 0, 10);
                name = nbuf;
        }
/*
        len = strlen(name);
        if(len >= Maxname){
                len = Maxname-1;
                if(name != nbuf){
                        memmove(nbuf, name, len);
                        name = nbuf;
                }
                name[len] = 0;
        }
*/

        chat("%d \"%s\"...", strlen(name), name);
        ip->offset = 0;
        setnames(&dir, nbuf);
        while(getdrec(f, d) >= 0) {
                dvers = rzdir(f->xf, &dir, ip->fmt, d);
                if(casestrcmp(f->xf->isplan9||f->xf->isrock, name, dir.name) != 0)
                        continue;
                newdrec(f, d);
                f->qid.path = dir.qid.path;
                f->qid.type = dir.qid.type;
                USED(dvers);
                return;
        }
        USED(vers);
        error(Enonexist);
}

static void
iopen(Xfile *f, int mode)
{
        mode &= ~OCEXEC;
        if(mode != OREAD && mode != OEXEC)
                error(Eperm);
        f->ptr->offset = 0;
        f->ptr->doffset = 0;
}

static void
icreate(Xfile *f, char *name, long perm, int mode)
{
        USED(f, name, perm, mode);
        error(Eperm);
}

static long
ireaddir(Xfile *f, uchar *buf, long offset, long count)
{
        Isofile *ip = f->ptr;
        Dir d;
        char names[4*Maxname];
        uchar dbuf[256];
        Drec *drec = (Drec *)dbuf;
        int n, rcnt;

        if(offset==0){
                ip->offset = 0;
                ip->doffset = 0;
        }else if(offset != ip->doffset)
                error("seek in directory not allowed");

        rcnt = 0;
        setnames(&d, names);
        while(rcnt < count && getdrec(f, drec) >= 0){
                if(drec->namelen == 1){
                        if(drec->name[0] == 0)
                                continue;
                        if(drec->name[0] == 1)
                                continue;
                }
                rzdir(f->xf, &d, ip->fmt, drec);
                d.qid.vers = f->qid.vers;
                if((n = convD2M(&d, buf+rcnt, count-rcnt)) <= BIT16SZ){
                        ungetdrec(f);
                        break;
                }
                rcnt += n;
        }
        ip->doffset += rcnt;
        return rcnt;
}

static long
iread(Xfile *f, char *buf, vlong offset, long count)
{
        int n, o, rcnt = 0;
        vlong size, addr;
        Isofile *ip = f->ptr;
        Iobuf *p;

        size = fakemax(l32(ip->d.size));
        if(offset >= size)
                return 0;
        if(offset+count > size)
                count = size - offset;
        addr = ((vlong)l32(ip->d.addr) + ip->d.attrlen)*ip->blksize + offset;
        o = addr % Sectorsize;
        addr /= Sectorsize;
        /*chat("d.addr=%ld, addr=%lld, o=%d...", l32(ip->d.addr), addr, o);*/
        n = Sectorsize - o;

        while(count > 0){
                if(n > count)
                        n = count;
                p = getbuf(f->xf->d, addr);
                memmove(&buf[rcnt], &p->iobuf[o], n);
                putbuf(p);
                count -= n;
                rcnt += n;
                ++addr;
                o = 0;
                n = Sectorsize;
        }
        return rcnt;
}

static long
iwrite(Xfile *f, char *buf, vlong offset, long count)
{
        USED(f, buf, offset, count);
        error(Eperm);
        return 0;
}

static void
iclunk(Xfile *f)
{
        USED(f);
}

static void
iremove(Xfile *f)
{
        USED(f);
        error(Eperm);
}

static void
istat(Xfile *f, Dir *d)
{
        Isofile *ip = f->ptr;

        rzdir(f->xf, d, ip->fmt, &ip->d);
        d->qid.vers = f->qid.vers;
        if(d->qid.path==f->xf->rootqid.path){
                d->qid.path = 0;
                d->qid.type = QTDIR;
        }
}

static void
iwstat(Xfile *f, Dir *d)
{
        USED(f, d);
        error(Eperm);
}

static int
showdrec(int fd, int fmt, void *x)
{
        Drec *d = (Drec *)x;
        int namelen;
        int syslen;

        if(d->reclen == 0)
                return 0;
        fprint(fd, "%d %d %ld %ld ",
                d->reclen, d->attrlen, l32(d->addr), l32(d->size));
        fprint(fd, "%s 0x%2.2x %d %d %ld ",
                rdate(d->date, fmt), (fmt=='z' ? d->flags : d->r_flags),
                d->unitsize, d->gapsize, l16(d->vseqno));
        fprint(fd, "%d %s", d->namelen, nstr(d->name, d->namelen));
        if(fmt != 'J'){
                namelen = d->namelen + (1-(d->namelen&1));
                syslen = d->reclen - 33 - namelen;
                if(syslen != 0)
                        fprint(fd, " %s", nstr(&d->name[namelen], syslen));
        }
        fprint(fd, "\n");
        return d->reclen + (d->reclen&1);
}

static void
newdrec(Xfile *f, Drec *dp)
{
        Isofile *x = f->ptr;
        Isofile *n;
        int len;

        len = sizeof(Isofile) - sizeof(Drec) + dp->reclen;
        n = ealloc(len);
        n->fmt = x->fmt;
        n->blksize = x->blksize;
        n->offset = 0;
        n->doffset = 0;
        memmove(&n->d, dp, dp->reclen);
        free(x);
        f->ptr = n;
        f->len = len;
}

static void
ungetdrec(Xfile *f)
{
        Isofile *ip = f->ptr;

        if(ip->offset >= ip->odelta){
                ip->offset -= ip->odelta;
                ip->odelta = 0;
        }
}

static int
getdrec(Xfile *f, void *buf)
{
        Isofile *ip = f->ptr;
        int len = 0, boff = 0;
        vlong addr;
        uvlong size;
        Iobuf *p = 0;

        if(!ip)
                return -1;
        size = fakemax(l32(ip->d.size));
        while(ip->offset < size){
                addr = ((vlong)l32(ip->d.addr)+ip->d.attrlen)*ip->blksize + ip->offset;
                boff = addr % Sectorsize;
                if(boff > Sectorsize-34){
                        ip->offset += Sectorsize-boff;
                        continue;
                }
                p = getbuf(f->xf->d, addr/Sectorsize);
                len = p->iobuf[boff];
                if(len >= 34)
                        break;
                putbuf(p);
                p = 0;
                ip->offset += Sectorsize-boff;
        }
        if(p) {
                memmove(buf, &p->iobuf[boff], len);
                putbuf(p);
                ip->odelta = len + (len&1);
                ip->offset += ip->odelta;
                return 0;
        }
        return -1;
}

static int
opendotdot(Xfile *f, Xfile *pf)
{
        uchar dbuf[256];
        Drec *d = (Drec *)dbuf;
        Isofile *ip = f->ptr, *pip = pf->ptr;

        ip->offset = 0;
        if(getdrec(f, d) < 0){
                chat("opendotdot: getdrec(.) failed...");
                return -1;
        }
        if(d->namelen != 1 || d->name[0] != 0){
                chat("opendotdot: no . entry...");
                return -1;
        }
        if(l32(d->addr) != l32(ip->d.addr)){
                chat("opendotdot: bad . address...");
                return -1;
        }
        if(getdrec(f, d) < 0){
                chat("opendotdot: getdrec(..) failed...");
                return -1;
        }
        if(d->namelen != 1 || d->name[0] != 1){
                chat("opendotdot: no .. entry...");
                return -1;
        }

        pf->xf = f->xf;
        pip->fmt = ip->fmt;
        pip->blksize = ip->blksize;
        pip->offset = 0;
        pip->doffset = 0;
        pip->d = *d;
        return 0;
}

enum {
        Hname = 1,
        Hmode = 2,
};

static int
rzdir(Xfs *fs, Dir *d, int fmt, Drec *dp)
{
        int n, flags, i, j, lj, nl, vers, sysl, mode, l, have;
        uchar *s;
        char *p;
        char buf[Maxname+UTFmax+1];
        uchar *q;
        Rune r;
        enum { ONAMELEN = 28 }; /* old Plan 9 directory name length */

        have = 0;
        flags = 0;
        vers = -1;
        d->qid.path = l32(dp->addr);
        d->qid.type = 0;
        d->qid.vers = 0;
        n = dp->namelen;
        memset(d->name, 0, Maxname);
        if(n == 1) {
                switch(dp->name[0]){
                case 1:
                        d->name[1] = '.';
                        /* fall through */
                case 0:
                        d->name[0] = '.';
                        have = Hname;
                        break;
                default:
                        d->name[0] = tolower(dp->name[0]);
                }
        } else {
                if(fmt == 'J'){ /* Joliet, 16-bit Unicode */
                        q = (uchar*)dp->name;
                        for(i=j=lj=0; i<n && j<Maxname; i+=2){
                                lj = j;
                                r = (q[i]<<8)|q[i+1];
                                j += runetochar(buf+j, &r);
                        }
                        if(j >= Maxname)
                                j = lj;
                        memmove(d->name, buf, j);
                }else{
                        if(n >= Maxname)
                                n = Maxname-1;
                        for(i=0; i<n; i++)
                                d->name[i] = tolower(dp->name[i]);
                }
        }

        sysl = dp->reclen-(34+dp->namelen);
        s = (uchar*)dp->name + dp->namelen;
        if(((uintptr)s) & 1) {
                s++;
                sysl--;
        }
        if(fs->isplan9 && sysl > 0) {
                /*
                 * get gid, uid, mode and possibly name
                 * from plan9 directory extension
                 */
                nl = *s;
                if(nl >= ONAMELEN)
                        nl = ONAMELEN-1;
                if(nl) {
                        memset(d->name, 0, ONAMELEN);
                        memmove(d->name, s+1, nl);
                }
                s += 1 + *s;
                nl = *s;
                if(nl >= ONAMELEN)
                        nl = ONAMELEN-1;
                memset(d->uid, 0, ONAMELEN);
                memmove(d->uid, s+1, nl);
                s += 1 + *s;
                nl = *s;
                if(nl >= ONAMELEN)
                        nl = ONAMELEN-1;
                memset(d->gid, 0, ONAMELEN);
                memmove(d->gid, s+1, nl);
                s += 1 + *s;
                if(((uintptr)s) & 1)
                        s++;
                d->mode = l32(s);
                if(d->mode & DMDIR)
                        d->qid.type |= QTDIR;
        } else {
                d->mode = 0444;
                switch(fmt) {
                case 'z':
                        if(fs->isrock)
                                strcpy(d->gid, "ridge");
                        else
                                strcpy(d->gid, "iso9660");
                        flags = dp->flags;
                        break;
                case 'r':
                        strcpy(d->gid, "sierra");
                        flags = dp->r_flags;
                        break;
                case 'J':
                        strcpy(d->gid, "joliet");
                        flags = dp->flags;
                        break;
                case '9':
                        strcpy(d->gid, "plan9");
                        flags = dp->flags;
                        break;
                }
                if(flags & 0x02){
                        d->qid.type |= QTDIR;
                        d->mode |= DMDIR|0111;
                }
                strcpy(d->uid, "cdrom");
                if(fmt!='9' && !(d->mode&DMDIR)){
                        /*
                         * ISO 9660 actually requires that you always have a . and a ;,
                         * even if there is no version and no extension.  Very few writers
                         * do this.  If the version is present, we use it for qid.vers.
                         * If there is no extension but there is a dot, we strip it off.
                         * (VMS heads couldn't comprehend the dot as a file name character
                         * rather than as just a separator between name and extension.)
                         *
                         * We don't do this for directory names because directories are
                         * not allowed to have extensions and versions.
                         */
                        if((p=strchr(d->name, ';')) != nil){
                                vers = strtoul(p+1, 0, 0);
                                d->qid.vers = vers;
                                *p = '\0';
                        }
                        if((p=strchr(d->name, '.')) != nil && *(p+1)=='\0')
                                *p = '\0';
                }
                if(fs->issusp){
                        nl = 0;
                        s += fs->suspoff;
                        sysl -= fs->suspoff;
                        for(; sysl >= 4 && have != (Hname|Hmode); sysl -= l, s += l){
                                if(s[0] == 0 && ((uintptr)s & 1)){
                                        /* MacOS pads individual entries, contrary to spec */
                                        s++;
                                        sysl--;
                                }
                                l = s[2];
                                if(s[0] == 'P' && s[1] == 'X' && s[3] == 1){
                                        /* posix file attributes */
                                        mode = l32(s+4);
                                        d->mode = mode & 0777;
                                        if((mode & 0170000) == 040000){
                                                d->mode |= DMDIR;
                                                d->qid.type |= QTDIR;
                                        }
                                        have |= Hmode;
                                } else if(s[0] == 'N' && s[1] == 'M' && s[3] == 1){
                                        /* alternative name */
                                        if((s[4] & ~1) == 0){
                                                i = nl+l-5;
                                                if(i >= Maxname)
                                                        i = Maxname-1;
                                                if((i -= nl) > 0){
                                                        memmove(d->name+nl, s+5, i);
                                                        nl += i;
                                                }
                                                if(s[4] == 0)
                                                        have |= Hname;
                                        }
                                } else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){
                                        sysl = getcontin(fs->d, s, &s);
                                        continue;
                                } else if(s[0] == 'S' && s[1] == 'T')
                                        break;
                        }
                }
        }
        d->length = 0;
        if((d->mode & DMDIR) == 0)      
                d->length = fakemax(l32(dp->size));
        d->type = 0;
        d->dev = 0;
        d->atime = gtime(dp->date);
        d->mtime = d->atime;
        return vers;
}

static int
getcontin(Xdata *dev, uchar *p, uchar **s)
{
        vlong bn, off, len;
        Iobuf *b;

        bn = l32(p+4);
        off = l32(p+12);
        len = l32(p+20);
        chat("getcontin %lld...", bn);
        b = getbuf(dev, bn);
        if(b == 0){
                *s = 0;
                return 0;
        }
        *s = b->iobuf+off;
        putbuf(b);
        return len;
}

static char *
nstr(uchar *p, int n)
{
        static char buf[132];
        char *q = buf;

        while(--n >= 0){
                if(*p == '\\')
                        *q++ = '\\';
                if(' ' <= *p && *p <= '~')
                        *q++ = *p++;
                else
                        q += sprint(q, "\\%2.2ux", *p++);
        }
        *q = 0;
        return buf;
}

static char *
rdate(uchar *p, int fmt)
{
        static char buf[64];
        int htz, s, n;

        n = sprint(buf, "%2.2d.%2.2d.%2.2d %2.2d:%2.2d:%2.2d",
                p[0], p[1], p[2], p[3], p[4], p[5]);
        if(fmt == 'z'){
                htz = p[6];
                if(htz >= 128){
                        htz = 256-htz;
                        s = '-';
                }else
                        s = '+';
                sprint(&buf[n], " (%c%.1f)", s, (float)htz/2);
        }
        return buf;
}

static char
dmsize[12] =
{
        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
};

static int
dysize(int y)
{

        if((y%4) == 0)
                return 366;
        return 365;
}

static long
gtime(uchar *p) /* yMdhmsz */
{
        long t;
        int i, y, M, d, h, m, s, tz;

        y=p[0]; M=p[1]; d=p[2];
        h=p[3]; m=p[4]; s=p[5]; tz=p[6];
        USED(tz);
        y += 1900;
        if (y < 1970)
                return 0;
        if (M < 1 || M > 12)
                return 0;
        if (d < 1 || d > dmsize[M-1])
                if (!(M == 2 && d == 29 && dysize(y) == 366))
                        return 0;
        if (h > 23)
                return 0;
        if (m > 59)
                return 0;
        if (s > 59)
                return 0;
        t = 0;
        for(i=1970; i<y; i++)
                t += dysize(i);
        if (dysize(y)==366 && M >= 3)
                t++;
        while(--M)
                t += dmsize[M-1];
        t += d-1;
        t = 24*t + h;
        t = 60*t + m;
        t = 60*t + s;
        return t;
}

#define p       ((uchar*)arg)

static long
l16(void *arg)
{
        long v;

        v = ((long)p[1]<<8)|p[0];
        if (v >= 0x8000L)
                v -= 0x10000L;
        return v;
}

static long
l32(void *arg)
{
        return (((long)p[3]<<8 | p[2])<<8 | p[1])<<8 | p[0];
}

#undef  p

static vlong
l64(void *arg)
{
        uchar *p;

        p = arg;
        return (vlong)l32(p+4) << 32 | (ulong)l32(p);
}