1209 lines
20 KiB
C
1209 lines
20 KiB
C
#include <u.h>
|
|
#include <libc.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(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
|
|
case -1:
|
|
sysfatal("fork");
|
|
case 0:
|
|
close(pfd[0]);
|
|
io(pfd[1]);
|
|
break;
|
|
default:
|
|
close(pfd[1]); /* don't deadlock if child fails */
|
|
if(mnt && 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(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");
|
|
}
|
|
|