jehanne/sys/src/cmd/disk/rofs/rofs.c

1225 lines
20 KiB
C

/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
/* Portions of this file are Copyright (C) 2015-2018 Giacomo Tesio <giacomo@tesio.it>
* See /doc/license/gpl-2.0.txt for details about the licensing.
*/
/* Portions of this file are Copyright (C) 9front's team.
* See /doc/license/9front-mit for details about the licensing.
* See http://git.9front.org/plan9front/plan9front/HEAD/info.html for a list of authors.
*/
#include <u.h>
#include <lib9.h>
#include <auth.h>
#include <9P2000.h>
#include <bio.h>
#include <mp.h>
#include <libsec.h>
#include <flate.h>
#include "paqfs.h"
enum
{
OPERM = 0x3, /* mask of all permission types in open mode */
OffsetSize = 4, /* size in bytes of an offset */
};
typedef struct Fid Fid;
typedef struct Paq Paq;
typedef struct Block Block;
struct Fid
{
short busy;
short open;
int fid;
char *user;
uint32_t offset; /* for directory reading */
Paq *paq;
Fid *next;
};
struct Paq
{
int ref;
Paq *up;
PaqDir *dir;
Qid qid;
};
struct Block
{
int ref;
uint32_t addr; /* block byte address */
uint32_t age;
uint8_t *data;
};
enum
{
Pexec = 1,
Pwrite = 2,
Pread = 4,
Pother = 1,
Pgroup = 8,
Powner = 64,
};
int noauth;
Fid *fids;
Fcall rhdr, thdr;
int blocksize;
int cachesize = 20;
int mesgsize = 8*1024 + IOHDRSZ;
Paq *root, *rootfile;
Block *cache;
uint32_t cacheage;
Biobuf *bin;
int qflag;
Fid * newfid(int);
void paqstat(PaqDir*, char*);
void io(int fd);
void *emalloc(uint32_t);
void *emallocz(uint32_t n);
char *estrdup(char*);
void usage(void);
uint32_t getl(uint8_t *p);
int gets(uint8_t *p);
char *getstr(uint8_t *p);
PaqDir *getDir(uint8_t*);
void getHeader(uint8_t *p, PaqHeader *b);
void getBlock(uint8_t *p, PaqBlock *b);
void getTrailer(uint8_t *p, PaqTrailer *b);
void init(char*, int);
void paqDirFree(PaqDir*);
Qid paqDirQid(PaqDir *d);
Paq *paqCpy(Paq *s);
Paq *paqLookup(Paq *s, char *name);
void paqFree(Paq*);
Paq *paqWalk(Paq *s, char *name);
int perm(PaqDir *s, char *user, int p);
int dirRead(Fid*, uint8_t*, int);
Block *blockLoad(uint32_t addr, int type);
void blockFree(Block*);
int checkDirSize(uint8_t *p, uint8_t *ep);
int packDir(PaqDir*, uint8_t*, int);
int blockRead(uint8_t *data, uint32_t addr, int type);
void readHeader(PaqHeader *hdr, char *name, DigestState *ds);
void readBlocks(char *name, DigestState *ds);
void readTrailer(PaqTrailer *tlr, char *name, DigestState *ds);
char *rflush(Fid*), *rversion(Fid*),
*rauth(Fid*), *rattach(Fid*), *rwalk(Fid*),
*ropen(Fid*), *rcreate(Fid*),
*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
char *(*fcalls[])(Fid*) = {
[Tflush] rflush,
[Tversion] rversion,
[Tattach] rattach,
[Tauth] rauth,
[Twalk] rwalk,
[Topen] ropen,
[Tcreate] rcreate,
[Tread] rread,
[Twrite] rwrite,
[Tclunk] rclunk,
[Tremove] rremove,
[Tstat] rstat,
[Twstat] rwstat,
};
char Eperm[] = "permission denied";
char Enotdir[] = "not a directory";
char Enoauth[] = "authentication not required";
char Enotexist[] = "file does not exist";
char Einuse[] = "file in use";
char Eexist[] = "file exists";
char Enotowner[] = "not owner";
char Eisopen[] = "file already open for I/O";
char Excl[] = "exclusive use file already open";
char Ename[] = "illegal name";
char Erdonly[] = "read only file system";
char Ebadblock[] = "bad block";
char Eversion[] = "bad version of P9";
char Edirtoobig[] = "directory entry too big";
int debug;
#pragma varargck type "V" uint8_t*
static int
sha1fmt(Fmt *f)
{
int i;
uint8_t *v;
v = va_arg(f->args, uint8_t*);
if(v == nil){
fmtprint(f, "*");
}
else{
for(i = 0; i < SHA1dlen; i++)
fmtprint(f, "%2.2ux", v[i]);
}
return 0;
}
void
main(int argc, char *argv[])
{
int pfd[2];
int fd, mnt, srv, stdio, verify;
char buf[64], *mntpoint, *srvname, *p;
fmtinstall('V', sha1fmt);
mntpoint = "/n/paq";
srvname = "paqfs";
mnt = 1;
srv = stdio = verify = 0;
ARGBEGIN{
default:
usage();
case 'a':
noauth = 1;
break;
case 'c':
p = EARGF(usage());
cachesize = atoi(p);
break;
case 'd':
debug = 1;
break;
case 'i':
mnt = 0;
stdio = 1;
pfd[0] = 0;
pfd[1] = 1;
break;
case 'm':
mntpoint = EARGF(usage());
break;
case 'M':
p = EARGF(usage());
mesgsize = atoi(p);
if(mesgsize < 512)
mesgsize = 512;
if(mesgsize > 128*1024)
mesgsize = 128*1024;
break;
case 'p':
srv = 1;
mnt = 1;
break;
case 'q':
qflag = 1;
break;
case 's':
srv = 1;
mnt = 0;
break;
case 'S':
srvname = EARGF(usage());
break;
case 'v':
verify = 1;
break;
}ARGEND
if(argc != 1)
usage();
init(argv[0], verify);
if(!stdio){
if(pipe(pfd) < 0)
sysfatal("pipe: %r");
if(srv){
snprint(buf, sizeof buf, "#s/%s", srvname);
fd = ocreate(buf, OWRITE, 0666);
if(fd < 0)
sysfatal("create %s: %r", buf);
if(fprint(fd, "%d", pfd[0]) < 0)
sysfatal("write %s: %r", buf);
}
}
if(debug)
fmtinstall('F', fcallfmt);
switch(sys_rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
case -1:
sysfatal("fork");
case 0:
sys_close(pfd[0]);
io(pfd[1]);
break;
default:
sys_close(pfd[1]); /* don't deadlock if child fails */
if(mnt && sys_mount(pfd[0], -1, mntpoint, MREPL|MCREATE, "", '9') < 0)
sysfatal("mount %s: %r", mntpoint);
}
exits(0);
}
char*
rversion(Fid* _)
{
Fid *f;
for(f = fids; f; f = f->next)
if(f->busy)
rclunk(f);
if(rhdr.msize > mesgsize)
thdr.msize = mesgsize;
else
thdr.msize = rhdr.msize;
if(strcmp(rhdr.version, "9P2000") != 0)
return Eversion;
thdr.version = "9P2000";
return 0;
}
char*
rauth(Fid* _)
{
return Enoauth;
}
char*
rflush(Fid *f)
{
USED(f);
return 0;
}
char*
rattach(Fid *f)
{
/* no authentication! */
f->busy = 1;
f->paq = paqCpy(root);
thdr.qid = f->paq->qid;
if(rhdr.uname[0])
f->user = estrdup(rhdr.uname);
else
f->user = estrdup("none");
return 0;
}
char*
clone(Fid *f, Fid **res)
{
Fid *nf;
if(f->open)
return Eisopen;
if(f->busy == 0)
return Enotexist;
nf = newfid(rhdr.newfid);
nf->busy = 1;
nf->open = 0;
nf->paq = paqCpy(f->paq);
nf->user = estrdup(f->user);
*res = nf;
return 0;
}
char*
rwalk(Fid *f)
{
Paq *paq, *npaq;
Fid *nf;
int nqid, nwname;
Qid qid;
char *err;
if(f->busy == 0)
return Enotexist;
nf = nil;
if(rhdr.fid != rhdr.newfid){
err = clone(f, &nf);
if(err)
return err;
f = nf; /* walk the new fid */
}
nwname = rhdr.nwname;
/* easy case */
if(nwname == 0) {
thdr.nwqid = 0;
return 0;
}
paq = paqCpy(f->paq);
qid = paq->qid;
err = nil;
for(nqid = 0; nqid < nwname; nqid++){
if((qid.type & QTDIR) == 0){
err = Enotdir;
break;
}
if(!perm(paq->dir, f->user, Pexec)) {
err = Eperm;
break;
}
npaq = paqWalk(paq, rhdr.wname[nqid]);
if(npaq == nil) {
err = Enotexist;
break;
}
paqFree(paq);
paq = npaq;
qid = paq->qid;
thdr.wqid[nqid] = qid;
}
thdr.nwqid = nqid;
if(nqid == nwname){
/* success */
paqFree(f->paq);
f->paq = paq;
return 0;
}
paqFree(paq);
if(nf != nil)
rclunk(nf);
/* only error on the first element */
if(nqid == 0)
return err;
return 0;
}
char *
ropen(Fid *f)
{
int mode, trunc;
if(f->open)
return Eisopen;
if(f->busy == 0)
return Enotexist;
mode = rhdr.mode;
if(f->paq->qid.type & QTDIR){
if(mode != NP_OREAD)
return Eperm;
thdr.qid = f->paq->qid;
return 0;
}
if(mode & NP_ORCLOSE)
return Erdonly;
trunc = mode & NP_OTRUNC;
mode &= OPERM;
if(mode==NP_OWRITE || mode==NP_ORDWR || trunc)
return Erdonly;
if(mode==NP_OREAD)
if(!perm(f->paq->dir, f->user, Pread))
return Eperm;
if(mode==NP_OEXEC)
if(!perm(f->paq->dir, f->user, Pexec))
return Eperm;
thdr.qid = f->paq->qid;
f->open = 1;
return 0;
}
char *
rcreate(Fid *f)
{
if(f->open)
return Eisopen;
if(f->busy == 0)
return Enotexist;
return Erdonly;
}
char *
readdir(Fid *f)
{
PaqDir *pd;
uint8_t *p, *ep;
uint32_t off;
int n, cnt, i;
uint8_t *buf;
Block *ptr, *b;
buf = (uint8_t*)thdr.data;
cnt = rhdr.count;
if(rhdr.offset == 0)
f->offset = 0;
off = f->offset;
if(rootfile && f->paq == root){
if(off != 0){
rhdr.count = 0;
return nil;
}
n = packDir(rootfile->dir, buf, cnt);
rhdr.count = n;
return nil;
}
ptr = blockLoad(f->paq->dir->offset, PointerBlock);
if(ptr == nil)
return Ebadblock;
i = off/blocksize;
off -= i*blocksize;
thdr.count = 0;
b = blockLoad(getl(ptr->data + i*4), DirBlock);
while(b != nil) {
p = b->data + off;
ep = b->data + blocksize;
if(checkDirSize(p, ep)) {
pd = getDir(p);
n = packDir(pd, buf, cnt);
paqDirFree(pd);
if(n == 0) {
blockFree(b);
if(thdr.count == 0) {
blockFree(ptr);
return Edirtoobig;
}
break;
}
off += gets(p);
cnt -= n;
buf += n;
thdr.count += n;
} else {
off = 0;
i++;
blockFree(b);
b = blockLoad(getl(ptr->data + i*4), DirBlock);
}
}
f->offset = i*blocksize + off;
blockFree(ptr);
return 0;
}
char*
rread(Fid *f)
{
PaqDir *pd;
uint8_t *buf;
int64_t off;
uint32_t uoff;
int n, cnt, i;
Block *ptr, *b;
if(f->busy == 0)
return Enotexist;
if(f->paq->qid.type & QTDIR)
return readdir(f);
pd = f->paq->dir;
off = rhdr.offset;
buf = (uint8_t*)thdr.data;
cnt = rhdr.count;
thdr.count = 0;
if(off >= pd->length || cnt == 0)
return 0;
if(cnt > pd->length - off)
cnt = pd->length - off;
ptr = blockLoad(pd->offset, PointerBlock);
if(ptr == nil)
return Ebadblock;
i = off/blocksize;
uoff = off-i*blocksize;
while(cnt > 0) {
b = blockLoad(getl(ptr->data + i*4), DataBlock);
if(b == nil) {
blockFree(ptr);
return Ebadblock;
}
n = blocksize - uoff;
if(n > cnt)
n = cnt;
memmove(buf, b->data + uoff, n);
cnt -= n;
thdr.count += n;
buf += n;
uoff = 0;
i++;
blockFree(b);
}
blockFree(ptr);
return 0;
}
char*
rwrite(Fid *f)
{
if(f->busy == 0)
return Enotexist;
return Erdonly;
}
char *
rclunk(Fid *f)
{
f->busy = 0;
f->open = 0;
free(f->user);
f->user = 0;
paqFree(f->paq);
f->paq = 0;
return 0;
}
char *
rremove(Fid *f)
{
rclunk(f);
return Erdonly;
}
char *
rstat(Fid *f)
{
if(f->busy == 0)
return Enotexist;
thdr.stat = (uint8_t*)thdr.data;
thdr.nstat = packDir(f->paq->dir, thdr.stat, mesgsize);
if(thdr.nstat == 0)
return Edirtoobig;
return 0;
}
char *
rwstat(Fid *f)
{
if(f->busy == 0)
return Enotexist;
return Erdonly;
}
Paq*
paqCpy(Paq *s)
{
s->ref++;
return s;
}
void
paqFree(Paq *p)
{
if(p == nil)
return;
p->ref--;
if(p->ref > 0)
return;
assert(p != root);
paqFree(p->up);
paqDirFree(p->dir);
free(p);
}
void
paqDirFree(PaqDir *pd)
{
if(pd == nil)
return;
free(pd->name);
free(pd->uid);
free(pd->gid);
free(pd);
}
Qid
paqDirQid(PaqDir *d)
{
Qid q;
q.path = d->qid;
q.vers = 0;
q.type = d->mode >> 24;
return q;
}
int
packDir(PaqDir *s, uint8_t *buf, int n)
{
Dir dir;
memset(&dir, 0, sizeof(dir));
dir.qid = paqDirQid(s);
dir.mode = s->mode;
dir.atime = s->mtime;
dir.mtime = s->mtime;
dir.length = s->length;
dir.name = s->name;
dir.uid = s->uid;
dir.gid = s->gid;
dir.muid = s->uid;
n = convD2M(&dir, buf, n);
if(n < STATFIXLEN)
return 0;
return n;
}
Block *
blockLoad(uint32_t addr, int type)
{
uint32_t age;
int i, j;
Block *b;
if(addr == 0)
return nil;
cacheage++;
/* age has wraped */
if(cacheage == 0) {
for(i=0; i<cachesize; i++)
cache[i].age = 0;
}
j = -1;
age = ~0;
for(i=0; i<cachesize; i++) {
b = &cache[i];
if(b->age < age && b->ref == 0) {
age = b->age;
j = i;
}
if(b->addr != addr)
continue;
b->age = cacheage;
b->ref++;
return b;
}
if(j < 0)
sysfatal("no empty spots in cache!");
b = &cache[j];
assert(b->ref == 0);
if(!blockRead(b->data, addr, type)) {
b->addr = 0;
b->age = 0;
return nil;
}
b->age = cacheage;
b->addr = addr;
b->ref = 1;
return b;
}
void
blockFree(Block *b)
{
if(b == nil)
return;
if(--b->ref > 0)
return;
assert(b->ref == 0);
}
Paq*
paqWalk(Paq *s, char *name)
{
Block *ptr, *b;
uint8_t *p, *ep;
PaqDir *pd;
int i, n;
Paq *ss;
if(strcmp(name, "..") == 0)
return paqCpy(s->up);
if(rootfile && s == root){
if(strcmp(name, rootfile->dir->name) == 0)
return paqCpy(rootfile);
return nil;
}
ptr = blockLoad(s->dir->offset, PointerBlock);
if(ptr == nil)
return nil;
for(i=0; i<blocksize/4; i++) {
b = blockLoad(getl(ptr->data+i*4), DirBlock);
if(b == nil)
break;
p = b->data;
ep = p + blocksize;
while(checkDirSize(p, ep)) {
n = gets(p);
pd = getDir(p);
if(strcmp(pd->name, name) == 0) {
ss = emallocz(sizeof(Paq));
ss->ref = 1;
ss->up = paqCpy(s);
ss->dir = pd;
ss->qid = paqDirQid(pd);
blockFree(b);
blockFree(ptr);
return ss;
}
paqDirFree(pd);
p += n;
}
blockFree(b);
}
blockFree(ptr);
return nil;
}
Fid *
newfid(int fid)
{
Fid *f, *ff;
ff = 0;
for(f = fids; f; f = f->next)
if(f->fid == fid)
return f;
else if(!ff && !f->busy)
ff = f;
if(ff){
ff->fid = fid;
return ff;
}
f = emallocz(sizeof *f);
f->fid = fid;
f->next = fids;
fids = f;
return f;
}
void
io(int fd)
{
char *err;
int n, pid;
uint8_t *mdata;
mdata = emalloc(mesgsize);
pid = getpid();
for(;;){
n = read9pmsg(fd, mdata, mesgsize);
if(n < 0)
sysfatal("mount read");
if(n == 0)
break;
if(convM2S(mdata, n, &rhdr) == 0)
continue;
if(debug)
fprint(2, "paqfs %d:<-%F\n", pid, &rhdr);
thdr.data = (char*)mdata + IOHDRSZ;
if(!fcalls[rhdr.type])
err = "bad fcall type";
else
err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
if(err){
thdr.type = Rerror;
thdr.ename = err;
}else{
thdr.type = rhdr.type + 1;
thdr.fid = rhdr.fid;
}
thdr.tag = rhdr.tag;
if(debug)
fprint(2, "paqfs %d:->%F\n", pid, &thdr);/**/
n = convS2M(&thdr, mdata, mesgsize);
if(n == 0)
sysfatal("convS2M sysfatal on write");
if(jehanne_write(fd, mdata, n) != n)
sysfatal("mount write");
}
}
int
perm(PaqDir *s, char *user, int p)
{
uint32_t perm = s->mode;
if((p*Pother) & perm)
return 1;
if((noauth || strcmp(user, s->gid)==0) && ((p*Pgroup) & perm))
return 1;
if((noauth || strcmp(user, s->uid)==0) && ((p*Powner) & perm))
return 1;
return 0;
}
void
init(char *file, int verify)
{
PaqHeader hdr;
PaqTrailer tlr;
Dir *dir;
int i;
uint8_t *p;
DigestState *ds = nil;
PaqDir *r;
Block *b;
uint32_t offset;
inflateinit();
bin = Bopen(file, OREAD);
if(bin == nil)
sysfatal("could not open file: %s: %r", file);
if(verify)
ds = sha1(0, 0, 0, 0);
readHeader(&hdr, file, ds);
blocksize = hdr.blocksize;
if(verify) {
readBlocks(file, ds);
} else {
dir = dirstat(file);
if(dir == nil)
sysfatal("could not stat file: %s: %r", file);
offset = dir->length - TrailerSize;
free(dir);
if(Bseek(bin, offset, 0) != offset)
sysfatal("could not seek to trailer: %s", file);
}
readTrailer(&tlr, file, ds);
/* asctime includes a newline - yuk */
if(!qflag){
fprint(2, "%s: %s", hdr.label, asctime(gmtime(hdr.time)));
fprint(2, "fingerprint: %V\n", tlr.sha1);
}
cache = emallocz(cachesize*sizeof(Block));
p = emalloc(cachesize*blocksize);
for(i=0; i<cachesize; i++) {
cache[i].data = p;
p += blocksize;
}
/* hand craft root */
b = blockLoad(tlr.root, DirBlock);
if(b == nil || !checkDirSize(b->data, b->data+blocksize))
sysfatal("could not read root block: %s", file);
r = getDir(b->data);
blockFree(b);
root = emallocz(sizeof(Paq));
root->qid = paqDirQid(r);
root->ref = 1;
root->dir = r;
root->up = root; /* parent of root is root */
/* craft root directory if root is a normal file */
if(!(root->qid.type&QTDIR)){
rootfile = root;
root = emallocz(sizeof(Paq));
root->qid = rootfile->qid;
root->qid.type |= QTDIR;
root->qid.path++;
root->ref = 1;
root->dir = emallocz(sizeof(PaqDir));
*root->dir = *r;
root->dir->mode |= DMDIR|0111;
root->up = root;
}
}
int
blockRead(uint8_t *data, uint32_t addr, int type)
{
uint8_t buf[BlockSize];
PaqBlock b;
uint8_t *cdat;
if(Bseek(bin, addr, 0) != addr){
fprint(2, "paqfs: seek %lud: %r\n", addr);
return 0;
}
if(Bread(bin, buf, BlockSize) != BlockSize){
fprint(2, "paqfs: read %d at %lud: %r\n", BlockSize, addr);
return 0;
}
getBlock(buf, &b);
if(b.magic != BlockMagic || b.size > blocksize || b.type != type){
fprint(2, "paqfs: bad block: magic %.8lux (want %.8ux) size %lud (max %d) type %ud (want %ud)\n",
b.magic, BlockMagic, b.size, blocksize, b.type, type);
return 0;
}
switch(b.encoding) {
default:
return 0;
case NoEnc:
if(Bread(bin, data, blocksize) < blocksize)
return 0;
break;
case DeflateEnc:
cdat = emalloc(b.size);
if(Bread(bin, cdat, b.size) < b.size) {
free(cdat);
return 0;
}
if(inflateblock(data, blocksize, cdat, b.size) < 0) {
fprint(2, "inflate error: %r\n");
free(cdat);
return 0;
}
free(cdat);
break;
}
if(adler32(0, data, blocksize) != b.adler32)
return 0;
return 1;
}
void
readHeader(PaqHeader *hdr, char *name, DigestState *ds)
{
uint8_t buf[HeaderSize];
if(Bread(bin, buf, HeaderSize) < HeaderSize)
sysfatal("could not read header: %s: %r", name);
if(ds)
sha1(buf, HeaderSize, 0, ds);
getHeader(buf, hdr);
if(hdr->magic != HeaderMagic)
sysfatal("bad header magic 0x%lux: %s", hdr->magic, name);
if(hdr->version != Version)
sysfatal("unknown file version: %s", name);
}
void
readBlocks(char *name, DigestState *ds)
{
uint8_t *buf;
PaqBlock b;
buf = emalloc(BlockSize+blocksize);
for(;;) {
if(Bread(bin, buf, 4) < 4)
sysfatal("could not read block: %s: %r", name);
Bseek(bin, -4, 1);
/* check if it is a data block */
if(getl(buf) != BlockMagic)
break;
if(Bread(bin, buf, BlockSize) < BlockSize)
sysfatal("could not read block: %s: %r", name);
if(ds)
sha1(buf, BlockSize, 0, ds);
getBlock(buf, &b);
if(b.size > blocksize)
sysfatal("bad block size: %lud: %s", b.size, name);
if(ds) {
if(Bread(bin, buf, b.size) < b.size)
sysfatal("sysfatal reading block: %s: %r", name);
sha1(buf, b.size, 0, ds);
} else
Bseek(bin, b.size, 1);
}
free(buf);
}
void
readTrailer(PaqTrailer *tlr, char *name, DigestState *ds)
{
uint8_t buf[TrailerSize];
uint8_t digest[SHA1dlen];
if(Bread(bin, buf, TrailerSize) < TrailerSize)
sysfatal("could not read trailer: %s: %r", name);
getTrailer(buf, tlr);
if(tlr->magic != TrailerMagic)
sysfatal("bad trailer magic: %s", name);
if(ds) {
sha1(buf, TrailerSize-SHA1dlen, digest, ds);
if(memcmp(digest, tlr->sha1, SHA1dlen) != 0)
sysfatal("bad sha1 digest: %s", name);
}
}
void *
emalloc(uint32_t n)
{
void *p;
p = malloc(n);
if(!p)
sysfatal("out of memory");
return p;
}
void *
emallocz(uint32_t n)
{
void *p;
p = emalloc(n);
memset(p, 0, n);
return p;
}
char *
estrdup(char *s)
{
s = strdup(s);
if(s == nil)
sysfatal("out of memory");
return s;
}
uint32_t
getl(uint8_t *p)
{
return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
}
int
gets(uint8_t *p)
{
return (p[0]<<8) | p[1];
}
int
checkDirSize(uint8_t *p, uint8_t *ep)
{
int n;
int i;
if(ep-p < 2)
return 0;
n = gets(p);
if(p+n > ep)
return 0;
ep = p+n;
p += 22;
for(i=0; i<3; i++) {
if(p+2 > ep)
return 0;
n = gets(p);
if(p+n > ep)
return 0;
p += n;
}
return 1;
}
void
getHeader(uint8_t *p, PaqHeader *h)
{
h->magic = getl(p);
h->version = gets(p+4);
h->blocksize = gets(p+6);
if((h->magic>>16) == BigHeaderMagic){
h->magic = HeaderMagic;
h->version = gets(p+2);
h->blocksize = getl(p+4);
}
h->time = getl(p+8);
memmove(h->label, p+12, sizeof(h->label));
h->label[sizeof(h->label)-1] = 0;
}
void
getTrailer(uint8_t *p, PaqTrailer *t)
{
t->magic = getl(p);
t->root = getl(p+4);
memmove(t->sha1, p+8, SHA1dlen);
}
void
getBlock(uint8_t *p, PaqBlock *b)
{
b->magic = getl(p);
b->size = gets(p+4);
if((b->magic>>16) == BigBlockMagic){
b->magic = BlockMagic;
b->size = getl(p+2);
}
b->type = p[6];
b->encoding = p[7];
b->adler32 = getl(p+8);
}
PaqDir *
getDir(uint8_t *p)
{
PaqDir *pd;
pd = emallocz(sizeof(PaqDir));
pd->qid = getl(p+2);
pd->mode = getl(p+6);
pd->mtime = getl(p+10);
pd->length = getl(p+14);
pd->offset = getl(p+18);
p += 22;
pd->name = getstr(p);
p += gets(p);
pd->uid = getstr(p);
p += gets(p);
pd->gid = getstr(p);
return pd;
}
char *
getstr(uint8_t *p)
{
char *s;
int n;
n = gets(p);
s = emalloc(n+1);
memmove(s, p+2, n);
s[n] = 0;
return s;
}
void
usage(void)
{
fprint(2, "usage: %s [-disv] [-c cachesize] [-m mountpoint] [-M mesgsize] paqfile\n", argv0);
exits("usage");
}