mkpaqfs.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  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. uchar *block, *pointer;
  124. ulong offset;
  125. fd = open(name, OREAD);
  126. if(fd < 0) {
  127. warn("could not open file: %s: %r", name);
  128. return nil;
  129. }
  130. block = emallocz(blocksize);
  131. pointer = emallocz(blocksize);
  132. nb = 0;
  133. n = 0;
  134. for(;;) {
  135. nn = read(fd, block+n, blocksize-n);
  136. if(nn < 0) {
  137. warn("read failed: %s: %r", name);
  138. goto Err;
  139. }
  140. if(nn == 0) {
  141. if(n == 0)
  142. break;
  143. /* pad out last block */
  144. memset(block+n, 0, blocksize-n);
  145. nn = blocksize - n;
  146. }
  147. n += nn;
  148. if(n < blocksize)
  149. continue;
  150. if(nb >= blocksize/OffsetSize) {
  151. warn("file too big for blocksize: %s", name);
  152. goto Err;
  153. }
  154. offset = writeBlock(block, DataBlock);
  155. putl(pointer+nb*OffsetSize, offset);
  156. nb++;
  157. n = 0;
  158. }
  159. offset = writeBlock(pointer, PointerBlock);
  160. close(fd);
  161. free(pointer);
  162. free(block);
  163. return paqDirAlloc(dir, offset);
  164. Err:
  165. close(fd);
  166. free(pointer);
  167. free(block);
  168. return nil;
  169. }
  170. PaqDir *
  171. paqDir(char *name, Dir *dir)
  172. {
  173. Dir *dirs, *p;
  174. PaqDir *pd;
  175. int i, n, nb, fd, ndir;
  176. uchar *block, *pointer;
  177. char *nname;
  178. ulong offset;
  179. fd = open(name, OREAD);
  180. if(fd < 0) {
  181. warn("could not open directory: %s: %r", name);
  182. return nil;
  183. }
  184. ndir = dirreadall(fd, &dirs);
  185. close(fd);
  186. if(ndir < 0) {
  187. warn("could not read directory: %s: %r", name);
  188. return nil;
  189. }
  190. block = emallocz(blocksize);
  191. pointer = emallocz(blocksize);
  192. nb = 0;
  193. n = 0;
  194. nname = nil;
  195. pd = nil;
  196. for(i=0; i<ndir; i++) {
  197. p = dirs + i;
  198. free(nname);
  199. nname = emallocz(strlen(name) + strlen(p->name) + 2);
  200. sprint(nname, "%s/%s", name, p->name);
  201. if(p->mode & DMDIR)
  202. pd = paqDir(nname, p);
  203. else
  204. pd = paqFile(nname, p);
  205. if(pd == nil)
  206. continue;
  207. if(n+paqDirSize(pd) >= blocksize) {
  208. /* zero fill the block */
  209. memset(block+n, 0, blocksize-n);
  210. offset = writeBlock(block, DirBlock);
  211. n = 0;
  212. if(nb >= blocksize/OffsetSize) {
  213. warn("directory too big for blocksize: %s", nname);
  214. goto Err;
  215. }
  216. putl(pointer+nb*OffsetSize, offset);
  217. nb++;
  218. }
  219. if(n+paqDirSize(pd) >= blocksize) {
  220. warn("directory entry does not fit in a block: %s", nname);
  221. paqDirFree(pd);
  222. continue;
  223. }
  224. putDir(block+n, pd);
  225. n += paqDirSize(pd);
  226. paqDirFree(pd);
  227. pd = nil;
  228. }
  229. if(n > 0) {
  230. /* zero fill the block */
  231. memset(block+n, 0, blocksize-n);
  232. offset = writeBlock(block, DirBlock);
  233. if(nb >= blocksize/OffsetSize) {
  234. warn("directory too big for blocksize: %s", nname);
  235. goto Err;
  236. }
  237. putl(pointer+nb*OffsetSize, offset);
  238. }
  239. offset = writeBlock(pointer, PointerBlock);
  240. free(nname);
  241. free(dirs);
  242. paqDirFree(pd);
  243. free(block);
  244. free(pointer);
  245. return paqDirAlloc(dir, offset);
  246. Err:
  247. free(nname);
  248. free(dirs);
  249. paqDirFree(pd);
  250. free(block);
  251. free(pointer);
  252. return nil;
  253. }
  254. PaqDir *
  255. paqDirAlloc(Dir *dir, ulong offset)
  256. {
  257. PaqDir *pd;
  258. static ulong qid = 1;
  259. pd = emallocz(sizeof(PaqDir));
  260. pd->name = strdup(dir->name);
  261. pd->qid = qid++;
  262. pd->mode = dir->mode & (DMDIR|DMAPPEND|0777);
  263. pd->mtime = dir->mtime;
  264. pd->length = dir->length;
  265. pd->uid = strdup(dir->uid);
  266. pd->gid = strdup(dir->gid);
  267. pd->offset = offset;
  268. return pd;
  269. }
  270. void
  271. paqDirFree(PaqDir *pd)
  272. {
  273. if(pd == nil)
  274. return;
  275. free(pd->name);
  276. free(pd->uid);
  277. free(pd->gid);
  278. free(pd);
  279. }
  280. void
  281. writeHeader(char *label)
  282. {
  283. PaqHeader hdr;
  284. uchar buf[HeaderSize];
  285. memset(&hdr, 0, sizeof(hdr));
  286. hdr.magic = HeaderMagic;
  287. hdr.version = Version;
  288. hdr.blocksize = blocksize;
  289. hdr.time = time(nil);
  290. strncpy(hdr.label, label, sizeof(hdr.label));
  291. hdr.label[sizeof(hdr.label)-1] = 0;
  292. putHeader(buf, &hdr);
  293. outWrite(buf, sizeof(buf));
  294. }
  295. void
  296. writeTrailer(ulong root)
  297. {
  298. PaqTrailer tlr;
  299. uchar buf[TrailerSize];
  300. memset(&tlr, 0, sizeof(tlr));
  301. tlr.magic = TrailerMagic;
  302. tlr.root = root;
  303. putTrailer(buf, &tlr);
  304. outWrite(buf, sizeof(buf));
  305. }
  306. ulong
  307. writeBlock(uchar *b, int type)
  308. {
  309. uchar *cb, *ob;
  310. int n;
  311. PaqBlock bh;
  312. uchar buf[BlockSize];
  313. ulong offset;
  314. offset = Boffset(out);
  315. bh.magic = BlockMagic;
  316. bh.size = blocksize;
  317. bh.type = type;
  318. bh.encoding = NoEnc;
  319. bh.adler32 = adler32(0, b, blocksize);
  320. ob = b;
  321. if(!uflag) {
  322. cb = emallocz(blocksize);
  323. n = deflateblock(cb, blocksize, b, blocksize, 6, 0);
  324. if(n > 0 && n < blocksize) {
  325. bh.encoding = DeflateEnc;
  326. bh.size = n;
  327. ob = cb;
  328. }
  329. }
  330. putBlock(buf, &bh);
  331. outWrite(buf, sizeof(buf));
  332. outWrite(ob, bh.size);
  333. if(ob != b)
  334. free(ob);
  335. return offset;
  336. }
  337. void
  338. outWrite(void *buf, int n)
  339. {
  340. if(Bwrite(out, buf, n) < n)
  341. sysfatal("write failed: %r");
  342. outdg = sha1((uchar*)buf, n, nil, outdg);
  343. }
  344. int
  345. paqDirSize(PaqDir *d)
  346. {
  347. return MinDirSize + strlen(d->name) + strlen(d->uid) + strlen(d->gid);
  348. }
  349. void
  350. putHeader(uchar *p, PaqHeader *h)
  351. {
  352. putl(p, h->magic);
  353. puts(p+4, h->version);
  354. puts(p+6, h->blocksize);
  355. putl(p+8, h->time);
  356. memmove(p+12, h->label, sizeof(h->label));
  357. }
  358. void
  359. putTrailer(uchar *p, PaqTrailer *h)
  360. {
  361. putl(p, h->magic);
  362. putl(p+4, h->root);
  363. outdg = sha1(p, 8, p+8, outdg);
  364. }
  365. void
  366. putBlock(uchar *p, PaqBlock *b)
  367. {
  368. putl(p, b->magic);
  369. puts(p+4, b->size);
  370. p[6] = b->type;
  371. p[7] = b->encoding;
  372. putl(p+8, b->adler32);
  373. }
  374. void
  375. putDir(uchar *p, PaqDir *d)
  376. {
  377. uchar *q;
  378. puts(p, paqDirSize(d));
  379. putl(p+2, d->qid);
  380. putl(p+6, d->mode);
  381. putl(p+10, d->mtime);
  382. putl(p+14, d->length);
  383. putl(p+18, d->offset);
  384. q = putstr(p+22, d->name);
  385. q = putstr(q, d->uid);
  386. q = putstr(q, d->gid);
  387. assert(q-p == paqDirSize(d));
  388. }
  389. void
  390. putl(uchar *p, ulong v)
  391. {
  392. p[0] = v>>24;
  393. p[1] = v>>16;
  394. p[2] = v>>8;
  395. p[3] = v;
  396. }
  397. void
  398. puts(uchar *p, int v)
  399. {
  400. assert(v < (1<<16));
  401. p[0] = v>>8;
  402. p[1] = v;
  403. }
  404. uchar *
  405. putstr(uchar *p, char *s)
  406. {
  407. int n = strlen(s);
  408. puts(p, n+2);
  409. memmove(p+2, s, n);
  410. return p+2+n;
  411. }
  412. void *
  413. emallocz(int size)
  414. {
  415. void *p;
  416. p = malloc(size);
  417. if(p == nil)
  418. sysfatal("malloc failed");
  419. memset(p, 0, size);
  420. return p;
  421. }
  422. void
  423. warn(char *fmt, ...)
  424. {
  425. char buf[1024];
  426. va_list arg;
  427. va_start(arg, fmt);
  428. vseprint(buf, buf+sizeof(buf), fmt, arg);
  429. va_end(arg);
  430. fprint(2, "%s: %s\n", argv0, buf);
  431. }