dump.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <libsec.h>
  5. #include <ctype.h>
  6. #include "iso9660.h"
  7. static void
  8. md5cd(Cdimg *cd, ulong block, ulong length, uchar *digest)
  9. {
  10. int n;
  11. uchar buf[Blocksize];
  12. DigestState *s;
  13. s = md5(nil, 0, nil, nil);
  14. while(length > 0) {
  15. n = length;
  16. if(n > Blocksize)
  17. n = Blocksize;
  18. Creadblock(cd, buf, block, n);
  19. md5(buf, n, nil, s);
  20. block++;
  21. length -= n;
  22. }
  23. md5(nil, 0, digest, s);
  24. }
  25. static Dumpdir*
  26. mkdumpdir(char *name, uchar *md5, ulong block, ulong length)
  27. {
  28. Dumpdir *d;
  29. assert(block != 0);
  30. d = emalloc(sizeof *d);
  31. d->name = name;
  32. memmove(d->md5, md5, sizeof d->md5);
  33. d->block = block;
  34. d->length = length;
  35. return d;
  36. }
  37. static Dumpdir**
  38. ltreewalkmd5(Dumpdir **l, uchar *md5)
  39. {
  40. int i;
  41. while(*l) {
  42. i = memcmp(md5, (*l)->md5, MD5dlen);
  43. if(i < 0)
  44. l = &(*l)->md5left;
  45. else if(i == 0)
  46. return l;
  47. else
  48. l = &(*l)->md5right;
  49. }
  50. return l;
  51. }
  52. static Dumpdir**
  53. ltreewalkblock(Dumpdir **l, ulong block)
  54. {
  55. while(*l) {
  56. if(block < (*l)->block)
  57. l = &(*l)->blockleft;
  58. else if(block == (*l)->block)
  59. return l;
  60. else
  61. l = &(*l)->blockright;
  62. }
  63. return l;
  64. }
  65. /*
  66. * Add a particular file to our binary tree.
  67. */
  68. static void
  69. addfile(Cdimg *cd, Dump *d, char *name, Direc *dir)
  70. {
  71. uchar md5[MD5dlen];
  72. Dumpdir **lblock;
  73. assert((dir->mode & DMDIR) == 0);
  74. if(dir->length == 0)
  75. return;
  76. lblock = ltreewalkblock(&d->blockroot, dir->block);
  77. if(*lblock != nil) {
  78. if((*lblock)->length == dir->length)
  79. return;
  80. fprint(2, "block %lud length %lud %s %lud %s\n", dir->block, (*lblock)->length, (*lblock)->name,
  81. dir->length, dir->name);
  82. assert(0);
  83. }
  84. md5cd(cd, dir->block, dir->length, md5);
  85. if(chatty > 1)
  86. fprint(2, "note file %.16H %lud (%s)\n", md5, dir->length, dir->name);
  87. insertmd5(d, name, md5, dir->block, dir->length);
  88. }
  89. void
  90. insertmd5(Dump *d, char *name, uchar *md5, ulong block, ulong length)
  91. {
  92. Dumpdir **lmd5;
  93. Dumpdir **lblock;
  94. lblock = ltreewalkblock(&d->blockroot, block);
  95. if(*lblock != nil) {
  96. if((*lblock)->length == length)
  97. return;
  98. fprint(2, "block %lud length %lud %lud\n", block, (*lblock)->length, length);
  99. assert(0);
  100. }
  101. assert(length != 0);
  102. *lblock = mkdumpdir(name, md5, block, length);
  103. lmd5 = ltreewalkmd5(&d->md5root, md5);
  104. if(*lmd5 != nil)
  105. fprint(2, "warning: data duplicated on CD\n");
  106. else
  107. *lmd5 = *lblock;
  108. }
  109. /*
  110. * Fill all the children entries for a particular directory;
  111. * all we care about is block, length, and whether it is a directory.
  112. */
  113. void
  114. readkids(Cdimg *cd, Direc *dir, char *(*cvt)(uchar*, int))
  115. {
  116. char *dot, *dotdot;
  117. int m, n;
  118. uchar buf[Blocksize], *ebuf, *p;
  119. ulong b, nb;
  120. Cdir *c;
  121. Direc dx;
  122. assert(dir->mode & DMDIR);
  123. dot = atom(".");
  124. dotdot = atom("..");
  125. ebuf = buf+Blocksize;
  126. nb = (dir->length+Blocksize-1) / Blocksize;
  127. n = 0;
  128. for(b=0; b<nb; b++) {
  129. Creadblock(cd, buf, dir->block + b, Blocksize);
  130. p = buf;
  131. while(p < ebuf) {
  132. c = (Cdir*)p;
  133. if(c->len == 0)
  134. break;
  135. if(p+c->len > ebuf)
  136. break;
  137. if(parsedir(cd, &dx, p, ebuf-p, cvt) == 0 && dx.name != dot && dx.name != dotdot)
  138. n++;
  139. p += c->len;
  140. }
  141. }
  142. m = (n+Ndirblock-1)/Ndirblock * Ndirblock;
  143. dir->child = emalloc(m*sizeof dir->child[0]);
  144. dir->nchild = n;
  145. n = 0;
  146. for(b=0; b<nb; b++) {
  147. assert(n <= dir->nchild);
  148. Creadblock(cd, buf, dir->block + b, Blocksize);
  149. p = buf;
  150. while(p < ebuf) {
  151. c = (Cdir*)p;
  152. if(c->len == 0)
  153. break;
  154. if(p+c->len > ebuf)
  155. break;
  156. if(parsedir(cd, &dx, p, ebuf-p, cvt) == 0 && dx.name != dot && dx.name != dotdot) {
  157. assert(n < dir->nchild);
  158. dir->child[n++] = dx;
  159. }
  160. p += c->len;
  161. }
  162. }
  163. }
  164. /*
  165. * Free the children. Make sure their children are free too.
  166. */
  167. void
  168. freekids(Direc *dir)
  169. {
  170. int i;
  171. for(i=0; i<dir->nchild; i++)
  172. assert(dir->child[i].nchild == 0);
  173. free(dir->child);
  174. dir->child = nil;
  175. dir->nchild = 0;
  176. }
  177. /*
  178. * Add a whole directory and all its children to our binary tree.
  179. */
  180. static void
  181. adddir(Cdimg *cd, Dump *d, Direc *dir)
  182. {
  183. int i;
  184. readkids(cd, dir, isostring);
  185. for(i=0; i<dir->nchild; i++) {
  186. if(dir->child[i].mode & DMDIR)
  187. adddir(cd, d, &dir->child[i]);
  188. else
  189. addfile(cd, d, atom(dir->name), &dir->child[i]);
  190. }
  191. freekids(dir);
  192. }
  193. Dumpdir*
  194. lookupmd5(Dump *d, uchar *md5)
  195. {
  196. return *ltreewalkmd5(&d->md5root, md5);
  197. }
  198. void
  199. adddirx(Cdimg *cd, Dump *d, Direc *dir, int lev)
  200. {
  201. int i;
  202. Direc dd;
  203. if(lev == 2){
  204. dd = *dir;
  205. adddir(cd, d, &dd);
  206. return;
  207. }
  208. for(i=0; i<dir->nchild; i++)
  209. adddirx(cd, d, &dir->child[i], lev+1);
  210. }
  211. Dump*
  212. dumpcd(Cdimg *cd, Direc *dir)
  213. {
  214. Dump *d;
  215. d = emalloc(sizeof *d);
  216. d->cd = cd;
  217. adddirx(cd, d, dir, 0);
  218. return d;
  219. }
  220. /*
  221. static ulong
  222. minblock(Direc *root, int lev)
  223. {
  224. int i;
  225. ulong m, n;
  226. m = root->block;
  227. for(i=0; i<root->nchild; i++) {
  228. n = minblock(&root->child[i], lev-1);
  229. if(m > n)
  230. m = n;
  231. }
  232. return m;
  233. }
  234. */
  235. void
  236. copybutname(Direc *d, Direc *s)
  237. {
  238. Direc x;
  239. x = *d;
  240. *d = *s;
  241. d->name = x.name;
  242. d->confname = x.confname;
  243. }
  244. Direc*
  245. createdumpdir(Direc *root, XDir *dir, char *utfname)
  246. {
  247. char *p;
  248. Direc *d;
  249. if(utfname[0]=='/')
  250. sysfatal("bad dump name '%s'", utfname);
  251. p = strchr(utfname, '/');
  252. if(p == nil || strchr(p+1, '/'))
  253. sysfatal("bad dump name '%s'", utfname);
  254. *p++ = '\0';
  255. if((d = walkdirec(root, utfname)) == nil)
  256. d = adddirec(root, utfname, dir);
  257. if(walkdirec(d, p))
  258. sysfatal("duplicate dump name '%s/%s'", utfname, p);
  259. d = adddirec(d, p, dir);
  260. return d;
  261. }
  262. static void
  263. rmdirec(Direc *d, Direc *kid)
  264. {
  265. Direc *ekid;
  266. ekid = d->child+d->nchild;
  267. assert(d->child <= kid && kid < ekid);
  268. if(ekid != kid+1)
  269. memmove(kid, kid+1, (ekid-(kid+1))*sizeof(*kid));
  270. d->nchild--;
  271. }
  272. void
  273. rmdumpdir(Direc *root, char *utfname)
  274. {
  275. char *p;
  276. Direc *d, *dd;
  277. if(utfname[0]=='/')
  278. sysfatal("bad dump name '%s'", utfname);
  279. p = strchr(utfname, '/');
  280. if(p == nil || strchr(p+1, '/'))
  281. sysfatal("bad dump name '%s'", utfname);
  282. *p++ = '\0';
  283. if((d = walkdirec(root, utfname)) == nil)
  284. sysfatal("cannot remove %s/%s: %s does not exist", utfname, p, utfname);
  285. p[-1] = '/';
  286. if((dd = walkdirec(d, p)) == nil)
  287. sysfatal("cannot remove %s: does not exist", utfname);
  288. rmdirec(d, dd);
  289. if(d->nchild == 0)
  290. rmdirec(root, d);
  291. }
  292. char*
  293. adddumpdir(Direc *root, ulong now, XDir *dir)
  294. {
  295. char buf[40], *p;
  296. int n;
  297. Direc *dday, *dyear;
  298. Tm tm;
  299. tm = *localtime(now);
  300. sprint(buf, "%d", tm.year+1900);
  301. if((dyear = walkdirec(root, buf)) == nil) {
  302. dyear = adddirec(root, buf, dir);
  303. assert(dyear != nil);
  304. }
  305. n = 0;
  306. sprint(buf, "%.2d%.2d", tm.mon+1, tm.mday);
  307. p = buf+strlen(buf);
  308. while(walkdirec(dyear, buf))
  309. sprint(p, "%d", ++n);
  310. dday = adddirec(dyear, buf, dir);
  311. assert(dday != nil);
  312. sprint(buf, "%s/%s", dyear->name, dday->name);
  313. assert(walkdirec(root, buf)==dday);
  314. return atom(buf);
  315. }
  316. /*
  317. * The dump directory tree is inferred from a linked list of special blocks.
  318. * One block is written at the end of each dump.
  319. * The blocks have the form
  320. *
  321. * plan 9 dump cd
  322. * <dump-name> <dump-time> <next-block> <conform-block> <conform-length> \
  323. * <iroot-block> <iroot-length> <jroot-block> <jroot-length>
  324. *
  325. * If only the first line is present, this is the end of the chain.
  326. */
  327. static char magic[] = "plan 9 dump cd\n";
  328. ulong
  329. Cputdumpblock(Cdimg *cd)
  330. {
  331. uvlong x;
  332. Cwseek(cd, (vlong)cd->nextblock * Blocksize);
  333. x = Cwoffset(cd);
  334. Cwrite(cd, magic, sizeof(magic)-1);
  335. Cpadblock(cd);
  336. return x/Blocksize;
  337. }
  338. int
  339. hasdump(Cdimg *cd)
  340. {
  341. int i;
  342. char buf[128];
  343. for(i=16; i<24; i++) {
  344. Creadblock(cd, buf, i, sizeof buf);
  345. if(memcmp(buf, magic, sizeof(magic)-1) == 0)
  346. return i;
  347. }
  348. return 0;
  349. }
  350. Direc
  351. readdumpdirs(Cdimg *cd, XDir *dir, char *(*cvt)(uchar*, int))
  352. {
  353. char buf[Blocksize];
  354. char *p, *q, *f[16];
  355. int i, nf;
  356. ulong db, t;
  357. Direc *nr, root;
  358. XDir xd;
  359. mkdirec(&root, dir);
  360. db = hasdump(cd);
  361. xd = *dir;
  362. for(;;){
  363. if(db == 0)
  364. sysfatal("error in dump blocks");
  365. Creadblock(cd, buf, db, sizeof buf);
  366. if(memcmp(buf, magic, sizeof(magic)-1) != 0)
  367. break;
  368. p = buf+sizeof(magic)-1;
  369. if(p[0] == '\0')
  370. break;
  371. if((q = strchr(p, '\n')) != nil)
  372. *q = '\0';
  373. nf = tokenize(p, f, nelem(f));
  374. i = 5;
  375. if(nf < i || (cvt==jolietstring && nf < i+2))
  376. sysfatal("error in dump block %lud: nf=%d; p='%s'", db, nf, p);
  377. nr = createdumpdir(&root, &xd, f[0]);
  378. t = strtoul(f[1], 0, 0);
  379. xd.mtime = xd.ctime = xd.atime = t;
  380. db = strtoul(f[2], 0, 0);
  381. if(cvt == jolietstring)
  382. i += 2;
  383. nr->block = strtoul(f[i], 0, 0);
  384. nr->length = strtoul(f[i+1], 0, 0);
  385. }
  386. cd->nulldump = db;
  387. return root;
  388. }
  389. extern void addtx(char*, char*);
  390. static int
  391. isalldigit(char *s)
  392. {
  393. while(*s)
  394. if(!isdigit(*s++))
  395. return 0;
  396. return 1;
  397. }
  398. void
  399. readdumpconform(Cdimg *cd)
  400. {
  401. char buf[Blocksize];
  402. char *p, *q, *f[10];
  403. int nf;
  404. ulong cb, nc, db;
  405. uvlong m;
  406. db = hasdump(cd);
  407. assert(map==nil || map->nt == 0);
  408. for(;;){
  409. if(db == 0)
  410. sysfatal("error0 in dump blocks");
  411. Creadblock(cd, buf, db, sizeof buf);
  412. if(memcmp(buf, magic, sizeof(magic)-1) != 0)
  413. break;
  414. p = buf+sizeof(magic)-1;
  415. if(p[0] == '\0')
  416. break;
  417. if((q = strchr(p, '\n')) != nil)
  418. *q = '\0';
  419. nf = tokenize(p, f, nelem(f));
  420. if(nf < 5)
  421. sysfatal("error0 in dump block %lud", db);
  422. db = strtoul(f[2], 0, 0);
  423. cb = strtoul(f[3], 0, 0);
  424. nc = strtoul(f[4], 0, 0);
  425. Crseek(cd, (vlong)cb * Blocksize);
  426. m = (vlong)cb * Blocksize + nc;
  427. while(Croffset(cd) < m && (p = Crdline(cd, '\n')) != nil){
  428. p[Clinelen(cd)-1] = '\0';
  429. if(tokenize(p, f, 2) != 2 || (f[0][0] != 'D' && f[0][0] != 'F')
  430. || strlen(f[0]) != 7 || !isalldigit(f[0]+1))
  431. break;
  432. addtx(atom(f[1]), atom(f[0]));
  433. }
  434. }
  435. if(map)
  436. cd->nconform = map->nt;
  437. else
  438. cd->nconform = 0;
  439. }