mkpaqfs.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <flate.h>
  5. #include <mp.h>
  6. #include <libsec.h>
  7. #include "paqfs.h"
  8. enum {
  9. OffsetSize = 4, /* size of block offset */
  10. };
  11. void paqfs(char *root, char *label);
  12. PaqDir *paqFile(char *name, Dir *dir);
  13. PaqDir *paqDir(char *name, Dir *dir);
  14. PaqDir *paqDirAlloc(Dir *d, ulong offset);
  15. void paqDirFree(PaqDir *pd);
  16. void writeHeader(char *label);
  17. void writeTrailer(ulong root);
  18. ulong writeBlock(uchar *buf, int type);
  19. void usage(void);
  20. void outWrite(void *buf, int n);
  21. int paqDirSize(PaqDir *dir);
  22. void putDir(uchar *p, PaqDir *dir);
  23. void putHeader(uchar *p, PaqHeader *h);
  24. void putBlock(uchar *p, PaqBlock *h);
  25. void putTrailer(uchar *p, PaqTrailer *t);
  26. void putl(uchar *p, ulong v);
  27. void puts(uchar *p, int x);
  28. uchar *putstr(uchar *p, char *s);
  29. void *emallocz(int size);
  30. void warn(char *fmt, ...);
  31. int uflag=0; /* uncompressed */
  32. long blocksize = 4*1024;
  33. Biobuf *out;
  34. DigestState *outdg;
  35. void
  36. main(int argc, char *argv[])
  37. {
  38. char *s, *ss;
  39. char *outfile = nil;
  40. char *label = nil;
  41. char *file;
  42. ARGBEGIN {
  43. case 'u':
  44. uflag=1;
  45. break;
  46. case 'o':
  47. outfile = ARGF();
  48. break;
  49. case 'l':
  50. label = ARGF();
  51. if(label == nil)
  52. usage();
  53. break;
  54. case 'b':
  55. s = ARGF();
  56. if(s) {
  57. blocksize = strtoul(s, &ss, 0);
  58. if(s == ss)
  59. usage();
  60. if(*ss == 'k')
  61. blocksize *= 1024;
  62. }
  63. if(blocksize < MinBlockSize)
  64. sysfatal("blocksize too small: must be at lease %d", MinBlockSize);
  65. if(blocksize > MaxBlockSize)
  66. sysfatal("blocksize too large: must be no greater than %d", MaxBlockSize);
  67. break;
  68. } ARGEND
  69. if(outfile == nil) {
  70. out = emallocz(sizeof(Biobuf));
  71. Binit(out, 1, OWRITE);
  72. } else {
  73. out = Bopen(outfile, OWRITE|OTRUNC);
  74. if(out == nil)
  75. sysfatal("could not create file: %s: %r", outfile);
  76. }
  77. deflateinit();
  78. file = argv[0];
  79. if(file == nil)
  80. file = ".";
  81. if(label == nil) {
  82. if(strrchr(file, '/'))
  83. label = strrchr(file, '/') + 1;
  84. else
  85. label = file;
  86. }
  87. paqfs(file, label);
  88. Bterm(out);
  89. exits(0);
  90. }
  91. void
  92. usage(void)
  93. {
  94. fprint(2, "usage: %s [-u] [-b blocksize] -o output [root]\n", argv0);
  95. exits("usage");
  96. }
  97. void
  98. paqfs(char *root, char *label)
  99. {
  100. Dir *dir;
  101. PaqDir *pd;
  102. ulong offset;
  103. uchar *buf;
  104. dir = dirstat(root);
  105. if(dir == nil)
  106. sysfatal("could not stat root: %s: %r", root);
  107. writeHeader(label);
  108. if(dir->mode & DMDIR)
  109. pd = paqDir(root, dir);
  110. else
  111. pd = paqFile(root, dir);
  112. buf = emallocz(blocksize);
  113. putDir(buf, pd);
  114. offset = writeBlock(buf, DirBlock);
  115. writeTrailer(offset);
  116. paqDirFree(pd);
  117. free(dir);
  118. }
  119. PaqDir *
  120. paqFile(char *name, Dir *dir)
  121. {
  122. int fd, n, nn, nb;
  123. vlong tot;
  124. uchar *block, *pointer;
  125. ulong offset;
  126. fd = open(name, OREAD);
  127. if(fd < 0) {
  128. warn("could not open file: %s: %r", name);
  129. return nil;
  130. }
  131. block = emallocz(blocksize);
  132. pointer = emallocz(blocksize);
  133. nb = 0;
  134. n = 0;
  135. tot = 0;
  136. for(;;) {
  137. nn = read(fd, block+n, blocksize-n);
  138. if(nn < 0) {
  139. warn("read failed: %s: %r", name);
  140. goto Err;
  141. }
  142. tot += nn;
  143. if(nn == 0) {
  144. if(n == 0)
  145. break;
  146. /* pad out last block */
  147. memset(block+n, 0, blocksize-n);
  148. nn = blocksize - n;
  149. }
  150. n += nn;
  151. if(n < blocksize)
  152. continue;
  153. if(nb >= blocksize/OffsetSize) {
  154. warn("file too big for blocksize: %s", name);
  155. goto Err;
  156. }
  157. offset = writeBlock(block, DataBlock);
  158. putl(pointer+nb*OffsetSize, offset);
  159. nb++;
  160. n = 0;
  161. }
  162. offset = writeBlock(pointer, PointerBlock);
  163. close(fd);
  164. free(pointer);
  165. free(block);
  166. dir->length = tot;
  167. return paqDirAlloc(dir, offset);
  168. Err:
  169. close(fd);
  170. free(pointer);
  171. free(block);
  172. return nil;
  173. }
  174. PaqDir *
  175. paqDir(char *name, Dir *dir)
  176. {
  177. Dir *dirs, *p;
  178. PaqDir *pd;
  179. int i, n, nb, fd, ndir;
  180. uchar *block, *pointer;
  181. char *nname;
  182. ulong offset;
  183. fd = open(name, OREAD);
  184. if(fd < 0) {
  185. warn("could not open directory: %s: %r", name);
  186. return nil;
  187. }
  188. ndir = dirreadall(fd, &dirs);
  189. close(fd);
  190. if(ndir < 0) {
  191. warn("could not read directory: %s: %r", name);
  192. return nil;
  193. }
  194. block = emallocz(blocksize);
  195. pointer = emallocz(blocksize);
  196. nb = 0;
  197. n = 0;
  198. nname = nil;
  199. pd = nil;
  200. for(i=0; i<ndir; i++) {
  201. p = dirs + i;
  202. free(nname);
  203. nname = emallocz(strlen(name) + strlen(p->name) + 2);
  204. sprint(nname, "%s/%s", name, p->name);
  205. if(p->mode & DMDIR)
  206. pd = paqDir(nname, p);
  207. else
  208. pd = paqFile(nname, p);
  209. if(pd == nil)
  210. continue;
  211. if(n+paqDirSize(pd) >= blocksize) {
  212. /* zero fill the block */
  213. memset(block+n, 0, blocksize-n);
  214. offset = writeBlock(block, DirBlock);
  215. n = 0;
  216. if(nb >= blocksize/OffsetSize) {
  217. warn("directory too big for blocksize: %s", nname);
  218. goto Err;
  219. }
  220. putl(pointer+nb*OffsetSize, offset);
  221. nb++;
  222. }
  223. if(n+paqDirSize(pd) >= blocksize) {
  224. warn("directory entry does not fit in a block: %s", nname);
  225. paqDirFree(pd);
  226. continue;
  227. }
  228. putDir(block+n, pd);
  229. n += paqDirSize(pd);
  230. paqDirFree(pd);
  231. pd = nil;
  232. }
  233. if(n > 0) {
  234. /* zero fill the block */
  235. memset(block+n, 0, blocksize-n);
  236. offset = writeBlock(block, DirBlock);
  237. if(nb >= blocksize/OffsetSize) {
  238. warn("directory too big for blocksize: %s", nname);
  239. goto Err;
  240. }
  241. putl(pointer+nb*OffsetSize, offset);
  242. }
  243. offset = writeBlock(pointer, PointerBlock);
  244. free(nname);
  245. free(dirs);
  246. paqDirFree(pd);
  247. free(block);
  248. free(pointer);
  249. return paqDirAlloc(dir, offset);
  250. Err:
  251. free(nname);
  252. free(dirs);
  253. paqDirFree(pd);
  254. free(block);
  255. free(pointer);
  256. return nil;
  257. }
  258. PaqDir *
  259. paqDirAlloc(Dir *dir, ulong offset)
  260. {
  261. PaqDir *pd;
  262. static ulong qid = 1;
  263. pd = emallocz(sizeof(PaqDir));
  264. pd->name = strdup(dir->name);
  265. pd->qid = qid++;
  266. pd->mode = dir->mode & (DMDIR|DMAPPEND|0777);
  267. pd->mtime = dir->mtime;
  268. pd->length = dir->length;
  269. pd->uid = strdup(dir->uid);
  270. pd->gid = strdup(dir->gid);
  271. pd->offset = offset;
  272. return pd;
  273. }
  274. void
  275. paqDirFree(PaqDir *pd)
  276. {
  277. if(pd == nil)
  278. return;
  279. free(pd->name);
  280. free(pd->uid);
  281. free(pd->gid);
  282. free(pd);
  283. }
  284. void
  285. writeHeader(char *label)
  286. {
  287. PaqHeader hdr;
  288. uchar buf[HeaderSize];
  289. memset(&hdr, 0, sizeof(hdr));
  290. hdr.magic = HeaderMagic;
  291. hdr.version = Version;
  292. hdr.blocksize = blocksize;
  293. hdr.time = time(nil);
  294. strncpy(hdr.label, label, sizeof(hdr.label));
  295. hdr.label[sizeof(hdr.label)-1] = 0;
  296. putHeader(buf, &hdr);
  297. outWrite(buf, sizeof(buf));
  298. }
  299. void
  300. writeTrailer(ulong root)
  301. {
  302. PaqTrailer tlr;
  303. uchar buf[TrailerSize];
  304. memset(&tlr, 0, sizeof(tlr));
  305. tlr.magic = TrailerMagic;
  306. tlr.root = root;
  307. putTrailer(buf, &tlr);
  308. outWrite(buf, sizeof(buf));
  309. }
  310. ulong
  311. writeBlock(uchar *b, int type)
  312. {
  313. uchar *cb, *ob;
  314. int n;
  315. PaqBlock bh;
  316. uchar buf[BlockSize];
  317. ulong offset;
  318. offset = Boffset(out);
  319. bh.magic = BlockMagic;
  320. bh.size = blocksize;
  321. bh.type = type;
  322. bh.encoding = NoEnc;
  323. bh.adler32 = adler32(0, b, blocksize);
  324. ob = b;
  325. if(!uflag) {
  326. cb = emallocz(blocksize);
  327. n = deflateblock(cb, blocksize, b, blocksize, 6, 0);
  328. if(n > 0 && n < blocksize) {
  329. bh.encoding = DeflateEnc;
  330. bh.size = n;
  331. ob = cb;
  332. }
  333. }
  334. putBlock(buf, &bh);
  335. outWrite(buf, sizeof(buf));
  336. outWrite(ob, bh.size);
  337. if(ob != b)
  338. free(ob);
  339. return offset;
  340. }
  341. void
  342. outWrite(void *buf, int n)
  343. {
  344. if(Bwrite(out, buf, n) < n)
  345. sysfatal("write failed: %r");
  346. outdg = sha1((uchar*)buf, n, nil, outdg);
  347. }
  348. int
  349. paqDirSize(PaqDir *d)
  350. {
  351. return MinDirSize + strlen(d->name) + strlen(d->uid) + strlen(d->gid);
  352. }
  353. void
  354. putHeader(uchar *p, PaqHeader *h)
  355. {
  356. if(h->blocksize < 65536){
  357. putl(p, h->magic);
  358. puts(p+4, h->version);
  359. puts(p+6, h->blocksize);
  360. }else{
  361. assert(h->magic == HeaderMagic);
  362. puts(p, BigHeaderMagic);
  363. puts(p+2, h->version);
  364. putl(p+4, h->blocksize);
  365. }
  366. putl(p+8, h->time);
  367. memmove(p+12, h->label, sizeof(h->label));
  368. }
  369. void
  370. putTrailer(uchar *p, PaqTrailer *h)
  371. {
  372. putl(p, h->magic);
  373. putl(p+4, h->root);
  374. outdg = sha1(p, 8, p+8, outdg);
  375. }
  376. void
  377. putBlock(uchar *p, PaqBlock *b)
  378. {
  379. if(b->size < 65536){
  380. putl(p, b->magic);
  381. puts(p+4, b->size);
  382. }else{
  383. assert(b->magic == BlockMagic);
  384. puts(p, BigBlockMagic);
  385. putl(p+2, b->size);
  386. }
  387. p[6] = b->type;
  388. p[7] = b->encoding;
  389. putl(p+8, b->adler32);
  390. }
  391. void
  392. putDir(uchar *p, PaqDir *d)
  393. {
  394. uchar *q;
  395. puts(p, paqDirSize(d));
  396. putl(p+2, d->qid);
  397. putl(p+6, d->mode);
  398. putl(p+10, d->mtime);
  399. putl(p+14, d->length);
  400. putl(p+18, d->offset);
  401. q = putstr(p+22, d->name);
  402. q = putstr(q, d->uid);
  403. q = putstr(q, d->gid);
  404. assert(q-p == paqDirSize(d));
  405. }
  406. void
  407. putl(uchar *p, ulong v)
  408. {
  409. p[0] = v>>24;
  410. p[1] = v>>16;
  411. p[2] = v>>8;
  412. p[3] = v;
  413. }
  414. void
  415. puts(uchar *p, int v)
  416. {
  417. assert(v < (1<<16));
  418. p[0] = v>>8;
  419. p[1] = v;
  420. }
  421. uchar *
  422. putstr(uchar *p, char *s)
  423. {
  424. int n = strlen(s);
  425. puts(p, n+2);
  426. memmove(p+2, s, n);
  427. return p+2+n;
  428. }
  429. void *
  430. emallocz(int size)
  431. {
  432. void *p;
  433. p = malloc(size);
  434. if(p == nil)
  435. sysfatal("malloc failed");
  436. memset(p, 0, size);
  437. return p;
  438. }
  439. void
  440. warn(char *fmt, ...)
  441. {
  442. char buf[1024];
  443. va_list arg;
  444. va_start(arg, fmt);
  445. vseprint(buf, buf+sizeof(buf), fmt, arg);
  446. va_end(arg);
  447. fprint(2, "%s: %s\n", argv0, buf);
  448. }