528 lines
9.5 KiB
C
528 lines
9.5 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 <bio.h>
|
|
#include <flate.h>
|
|
#include <mp.h>
|
|
#include <libsec.h>
|
|
#include "paqfs.h"
|
|
|
|
enum {
|
|
OffsetSize = 4, /* size of block offset */
|
|
};
|
|
|
|
void paqfs(char *root, char *label);
|
|
PaqDir *paqFile(char *name, Dir *dir);
|
|
PaqDir *paqDir(char *name, Dir *dir);
|
|
PaqDir *paqDirAlloc(Dir *d, uint32_t offset);
|
|
void paqDirFree(PaqDir *pd);
|
|
void writeHeader(char *label);
|
|
void writeTrailer(uint32_t root);
|
|
uint32_t writeBlock(uint8_t *buf, int type);
|
|
void usage(void);
|
|
void outWrite(void *buf, int n);
|
|
int paqDirSize(PaqDir *dir);
|
|
void putDir(uint8_t *p, PaqDir *dir);
|
|
void putHeader(uint8_t *p, PaqHeader *h);
|
|
void putBlock(uint8_t *p, PaqBlock *h);
|
|
void putTrailer(uint8_t *p, PaqTrailer *t);
|
|
void putl(uint8_t *p, uint32_t v);
|
|
void puts(uint8_t *p, int x);
|
|
uint8_t *putstr(uint8_t *p, char *s);
|
|
void *emallocz(int size);
|
|
void warn(char *fmt, ...);
|
|
|
|
int uflag=0; /* uncompressed */
|
|
int32_t blocksize = 4*1024;
|
|
|
|
Biobuf *out;
|
|
DigestState *outdg;
|
|
|
|
void
|
|
main(int argc, char *argv[])
|
|
{
|
|
char *s, *ss;
|
|
char *outfile = nil;
|
|
char *label = nil;
|
|
char *file;
|
|
|
|
ARGBEGIN {
|
|
case 'u':
|
|
uflag=1;
|
|
break;
|
|
case 'o':
|
|
outfile = ARGF();
|
|
break;
|
|
case 'l':
|
|
label = ARGF();
|
|
if(label == nil)
|
|
usage();
|
|
break;
|
|
case 'b':
|
|
s = ARGF();
|
|
if(s) {
|
|
blocksize = strtoul(s, &ss, 0);
|
|
if(s == ss)
|
|
usage();
|
|
if(*ss == 'k')
|
|
blocksize *= 1024;
|
|
}
|
|
if(blocksize < MinBlockSize)
|
|
sysfatal("blocksize too small: must be at least %d", MinBlockSize);
|
|
if(blocksize > MaxBlockSize)
|
|
sysfatal("blocksize too large: must be no greater than %d", MaxBlockSize);
|
|
break;
|
|
} ARGEND
|
|
|
|
if(outfile == nil) {
|
|
out = emallocz(sizeof(Biobuf));
|
|
Binit(out, 1, OWRITE);
|
|
} else {
|
|
out = Bopen(outfile, OWRITE|OTRUNC);
|
|
if(out == nil)
|
|
sysfatal("could not create file: %s: %r", outfile);
|
|
}
|
|
|
|
deflateinit();
|
|
|
|
file = argv[0];
|
|
if(file == nil)
|
|
file = ".";
|
|
|
|
if(label == nil) {
|
|
if(strrchr(file, '/'))
|
|
label = strrchr(file, '/') + 1;
|
|
else
|
|
label = file;
|
|
}
|
|
|
|
paqfs(file, label);
|
|
|
|
Bterm(out);
|
|
|
|
exits(0);
|
|
}
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprint(2, "usage: %s [-u] [-b blocksize] -o output [root]\n", argv0);
|
|
exits("usage");
|
|
}
|
|
|
|
void
|
|
paqfs(char *root, char *label)
|
|
{
|
|
Dir *dir;
|
|
PaqDir *pd;
|
|
uint32_t offset;
|
|
uint8_t *buf;
|
|
|
|
dir = dirstat(root);
|
|
if(dir == nil)
|
|
sysfatal("could not stat root: %s: %r", root);
|
|
writeHeader(label);
|
|
if(dir->mode & DMDIR)
|
|
pd = paqDir(root, dir);
|
|
else
|
|
pd = paqFile(root, dir);
|
|
buf = emallocz(blocksize);
|
|
putDir(buf, pd);
|
|
offset = writeBlock(buf, DirBlock);
|
|
writeTrailer(offset);
|
|
paqDirFree(pd);
|
|
free(dir);
|
|
}
|
|
|
|
|
|
PaqDir *
|
|
paqFile(char *name, Dir *dir)
|
|
{
|
|
int fd, n, nn, nb;
|
|
int64_t tot;
|
|
uint8_t *block, *pointer;
|
|
uint32_t offset;
|
|
|
|
fd = sys_open(name, OREAD);
|
|
if(fd < 0) {
|
|
warn("could not open file: %s: %r", name);
|
|
return nil;
|
|
}
|
|
|
|
block = emallocz(blocksize);
|
|
pointer = emallocz(blocksize);
|
|
nb = 0;
|
|
n = 0;
|
|
tot = 0;
|
|
for(;;) {
|
|
nn = jehanne_read(fd, block+n, blocksize-n);
|
|
if(nn < 0) {
|
|
warn("read failed: %s: %r", name);
|
|
goto Err;
|
|
}
|
|
tot += nn;
|
|
if(nn == 0) {
|
|
if(n == 0)
|
|
break;
|
|
/* pad out last block */
|
|
memset(block+n, 0, blocksize-n);
|
|
nn = blocksize - n;
|
|
}
|
|
n += nn;
|
|
if(n < blocksize)
|
|
continue;
|
|
if(nb >= blocksize/OffsetSize) {
|
|
warn("file too big for blocksize: %s", name);
|
|
goto Err;
|
|
}
|
|
offset = writeBlock(block, DataBlock);
|
|
putl(pointer+nb*OffsetSize, offset);
|
|
nb++;
|
|
n = 0;
|
|
}
|
|
|
|
offset = writeBlock(pointer, PointerBlock);
|
|
|
|
sys_close(fd);
|
|
free(pointer);
|
|
free(block);
|
|
dir->length = tot;
|
|
return paqDirAlloc(dir, offset);
|
|
Err:
|
|
sys_close(fd);
|
|
free(pointer);
|
|
free(block);
|
|
return nil;
|
|
}
|
|
|
|
PaqDir *
|
|
paqDir(char *name, Dir *dir)
|
|
{
|
|
Dir *dirs, *p;
|
|
PaqDir *pd;
|
|
int i, n, nb, fd, ndir;
|
|
uint8_t *block, *pointer;
|
|
char *nname;
|
|
uint32_t offset;
|
|
|
|
fd = sys_open(name, OREAD);
|
|
if(fd < 0) {
|
|
warn("could not open directory: %s: %r", name);
|
|
return nil;
|
|
}
|
|
|
|
ndir = dirreadall(fd, &dirs);
|
|
sys_close(fd);
|
|
|
|
if(ndir < 0) {
|
|
warn("could not read directory: %s: %r", name);
|
|
return nil;
|
|
}
|
|
|
|
block = emallocz(blocksize);
|
|
pointer = emallocz(blocksize);
|
|
nb = 0;
|
|
n = 0;
|
|
nname = nil;
|
|
pd = nil;
|
|
|
|
for(i=0; i<ndir; i++) {
|
|
p = dirs + i;
|
|
free(nname);
|
|
nname = emallocz(strlen(name) + strlen(p->name) + 2);
|
|
sprint(nname, "%s/%s", name, p->name);
|
|
if(p->mode & DMDIR)
|
|
pd = paqDir(nname, p);
|
|
else
|
|
pd = paqFile(nname, p);
|
|
if(pd == nil)
|
|
continue;
|
|
|
|
if(n+paqDirSize(pd) >= blocksize) {
|
|
|
|
/* zero fill the block */
|
|
memset(block+n, 0, blocksize-n);
|
|
offset = writeBlock(block, DirBlock);
|
|
n = 0;
|
|
if(nb >= blocksize/OffsetSize) {
|
|
warn("directory too big for blocksize: %s", nname);
|
|
goto Err;
|
|
}
|
|
putl(pointer+nb*OffsetSize, offset);
|
|
nb++;
|
|
}
|
|
if(n+paqDirSize(pd) >= blocksize) {
|
|
warn("directory entry does not fit in a block: %s", nname);
|
|
paqDirFree(pd);
|
|
continue;
|
|
}
|
|
putDir(block+n, pd);
|
|
n += paqDirSize(pd);
|
|
paqDirFree(pd);
|
|
pd = nil;
|
|
}
|
|
|
|
if(n > 0) {
|
|
/* zero fill the block */
|
|
memset(block+n, 0, blocksize-n);
|
|
offset = writeBlock(block, DirBlock);
|
|
if(nb >= blocksize/OffsetSize) {
|
|
warn("directory too big for blocksize: %s", nname);
|
|
goto Err;
|
|
}
|
|
putl(pointer+nb*OffsetSize, offset);
|
|
}
|
|
offset = writeBlock(pointer, PointerBlock);
|
|
|
|
free(nname);
|
|
free(dirs);
|
|
paqDirFree(pd);
|
|
free(block);
|
|
free(pointer);
|
|
return paqDirAlloc(dir, offset);
|
|
Err:
|
|
free(nname);
|
|
free(dirs);
|
|
paqDirFree(pd);
|
|
free(block);
|
|
free(pointer);
|
|
return nil;
|
|
}
|
|
|
|
PaqDir *
|
|
paqDirAlloc(Dir *dir, uint32_t offset)
|
|
{
|
|
PaqDir *pd;
|
|
static uint32_t qid = 1;
|
|
|
|
pd = emallocz(sizeof(PaqDir));
|
|
|
|
pd->name = strdup(dir->name);
|
|
pd->qid = qid++;
|
|
pd->mode = dir->mode & (DMDIR|DMAPPEND|0777);
|
|
pd->mtime = dir->mtime;
|
|
pd->length = dir->length;
|
|
pd->uid = strdup(dir->uid);
|
|
pd->gid = strdup(dir->gid);
|
|
pd->offset = offset;
|
|
|
|
return pd;
|
|
}
|
|
|
|
void
|
|
paqDirFree(PaqDir *pd)
|
|
{
|
|
if(pd == nil)
|
|
return;
|
|
free(pd->name);
|
|
free(pd->uid);
|
|
free(pd->gid);
|
|
free(pd);
|
|
}
|
|
|
|
|
|
void
|
|
writeHeader(char *label)
|
|
{
|
|
PaqHeader hdr;
|
|
uint8_t buf[HeaderSize];
|
|
|
|
memset(&hdr, 0, sizeof(hdr));
|
|
hdr.magic = HeaderMagic;
|
|
hdr.version = Version;
|
|
hdr.blocksize = blocksize;
|
|
hdr.time = time(nil);
|
|
strncpy(hdr.label, label, sizeof(hdr.label));
|
|
hdr.label[sizeof(hdr.label)-1] = 0;
|
|
putHeader(buf, &hdr);
|
|
outWrite(buf, sizeof(buf));
|
|
}
|
|
|
|
void
|
|
writeTrailer(uint32_t root)
|
|
{
|
|
PaqTrailer tlr;
|
|
uint8_t buf[TrailerSize];
|
|
|
|
memset(&tlr, 0, sizeof(tlr));
|
|
tlr.magic = TrailerMagic;
|
|
tlr.root = root;
|
|
putTrailer(buf, &tlr);
|
|
outWrite(buf, sizeof(buf));
|
|
}
|
|
|
|
uint32_t
|
|
writeBlock(uint8_t *b, int type)
|
|
{
|
|
uint8_t *cb, *ob;
|
|
int n;
|
|
PaqBlock bh;
|
|
uint8_t buf[BlockSize];
|
|
uint32_t offset;
|
|
|
|
offset = Boffset(out);
|
|
|
|
bh.magic = BlockMagic;
|
|
bh.size = blocksize;
|
|
bh.type = type;
|
|
bh.encoding = NoEnc;
|
|
bh.adler32 = adler32(0, b, blocksize);
|
|
ob = b;
|
|
|
|
if(!uflag) {
|
|
cb = emallocz(blocksize);
|
|
n = deflateblock(cb, blocksize, b, blocksize, 6, 0);
|
|
if(n > 0 && n < blocksize) {
|
|
bh.encoding = DeflateEnc;
|
|
bh.size = n;
|
|
ob = cb;
|
|
}
|
|
}
|
|
|
|
putBlock(buf, &bh);
|
|
outWrite(buf, sizeof(buf));
|
|
outWrite(ob, bh.size);
|
|
|
|
if(ob != b)
|
|
free(ob);
|
|
return offset;
|
|
}
|
|
|
|
|
|
void
|
|
outWrite(void *buf, int n)
|
|
{
|
|
if(Bwrite(out, buf, n) < n)
|
|
sysfatal("write failed: %r");
|
|
outdg = sha1((uint8_t*)buf, n, nil, outdg);
|
|
}
|
|
|
|
int
|
|
paqDirSize(PaqDir *d)
|
|
{
|
|
return MinDirSize + strlen(d->name) + strlen(d->uid) + strlen(d->gid);
|
|
}
|
|
|
|
void
|
|
putHeader(uint8_t *p, PaqHeader *h)
|
|
{
|
|
if(h->blocksize < 65536){
|
|
putl(p, h->magic);
|
|
puts(p+4, h->version);
|
|
puts(p+6, h->blocksize);
|
|
}else{
|
|
assert(h->magic == HeaderMagic);
|
|
puts(p, BigHeaderMagic);
|
|
puts(p+2, h->version);
|
|
putl(p+4, h->blocksize);
|
|
}
|
|
putl(p+8, h->time);
|
|
memmove(p+12, h->label, sizeof(h->label));
|
|
}
|
|
|
|
void
|
|
putTrailer(uint8_t *p, PaqTrailer *h)
|
|
{
|
|
putl(p, h->magic);
|
|
putl(p+4, h->root);
|
|
outdg = sha1(p, 8, p+8, outdg);
|
|
}
|
|
|
|
void
|
|
putBlock(uint8_t *p, PaqBlock *b)
|
|
{
|
|
if(b->size < 65536){
|
|
putl(p, b->magic);
|
|
puts(p+4, b->size);
|
|
}else{
|
|
assert(b->magic == BlockMagic);
|
|
puts(p, BigBlockMagic);
|
|
putl(p+2, b->size);
|
|
}
|
|
p[6] = b->type;
|
|
p[7] = b->encoding;
|
|
putl(p+8, b->adler32);
|
|
}
|
|
|
|
void
|
|
putDir(uint8_t *p, PaqDir *d)
|
|
{
|
|
uint8_t *q;
|
|
|
|
puts(p, paqDirSize(d));
|
|
putl(p+2, d->qid);
|
|
putl(p+6, d->mode);
|
|
putl(p+10, d->mtime);
|
|
putl(p+14, d->length);
|
|
putl(p+18, d->offset);
|
|
q = putstr(p+22, d->name);
|
|
q = putstr(q, d->uid);
|
|
q = putstr(q, d->gid);
|
|
assert(q-p == paqDirSize(d));
|
|
}
|
|
|
|
void
|
|
putl(uint8_t *p, uint32_t v)
|
|
{
|
|
p[0] = v>>24;
|
|
p[1] = v>>16;
|
|
p[2] = v>>8;
|
|
p[3] = v;
|
|
}
|
|
|
|
void
|
|
puts(uint8_t *p, int v)
|
|
{
|
|
assert(v < (1<<16));
|
|
p[0] = v>>8;
|
|
p[1] = v;
|
|
}
|
|
|
|
uint8_t *
|
|
putstr(uint8_t *p, char *s)
|
|
{
|
|
int n = strlen(s);
|
|
puts(p, n+2);
|
|
memmove(p+2, s, n);
|
|
return p+2+n;
|
|
}
|
|
|
|
|
|
void *
|
|
emallocz(int size)
|
|
{
|
|
void *p;
|
|
|
|
p = malloc(size);
|
|
if(p == nil)
|
|
sysfatal("malloc failed");
|
|
memset(p, 0, size);
|
|
return p;
|
|
}
|
|
|
|
void
|
|
warn(char *fmt, ...)
|
|
{
|
|
char buf[1024];
|
|
va_list arg;
|
|
|
|
va_start(arg, fmt);
|
|
vseprint(buf, buf+sizeof(buf), fmt, arg);
|
|
va_end(arg);
|
|
fprint(2, "%s: %s\n", argv0, buf);
|
|
}
|