write.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <libsec.h>
  5. #include "iso9660.h"
  6. static void
  7. writelittlebig4(uchar *buf, ulong x)
  8. {
  9. buf[0] = buf[7] = x;
  10. buf[1] = buf[6] = x>>8;
  11. buf[2] = buf[5] = x>>16;
  12. buf[3] = buf[4] = x>>24;
  13. }
  14. void
  15. rewritedot(Cdimg *cd, Direc *d)
  16. {
  17. uchar buf[Blocksize];
  18. Cdir *c;
  19. Creadblock(cd, buf, d->block, Blocksize);
  20. c = (Cdir*)buf;
  21. assert(c->len != 0);
  22. assert(c->namelen == 1 && c->name[0] == '\0'); /* dot */
  23. writelittlebig4(c->dloc, d->block);
  24. writelittlebig4(c->dlen, d->length);
  25. Cwseek(cd, (vlong)d->block * Blocksize);
  26. Cwrite(cd, buf, Blocksize);
  27. }
  28. void
  29. rewritedotdot(Cdimg *cd, Direc *d, Direc *dparent)
  30. {
  31. uchar buf[Blocksize];
  32. Cdir *c;
  33. Creadblock(cd, buf, d->block, Blocksize);
  34. c = (Cdir*)buf;
  35. assert(c->len != 0);
  36. assert(c->namelen == 1 && c->name[0] == '\0'); /* dot */
  37. c = (Cdir*)(buf+c->len);
  38. assert(c->len != 0);
  39. assert(c->namelen == 1 && c->name[0] == '\001'); /* dotdot*/
  40. writelittlebig4(c->dloc, dparent->block);
  41. writelittlebig4(c->dlen, dparent->length);
  42. Cwseek(cd, (vlong)d->block * Blocksize);
  43. Cwrite(cd, buf, Blocksize);
  44. }
  45. /*
  46. * Write each non-directory file. We copy the file to
  47. * the cd image, and then if it turns out that we've
  48. * seen this stream of bits before, we push the next block
  49. * pointer back. This ensures consistency between the MD5s
  50. * and the data on the CD image. MD5 summing on one pass
  51. * and copying on another would not ensure this.
  52. */
  53. void
  54. writefiles(Dump *d, Cdimg *cd, Direc *direc)
  55. {
  56. int i;
  57. uchar buf[8192], digest[MD5dlen];
  58. ulong length, n, start;
  59. Biobuf *b;
  60. DigestState *s;
  61. Dumpdir *dd;
  62. if(direc->mode & DMDIR) {
  63. for(i=0; i<direc->nchild; i++)
  64. writefiles(d, cd, &direc->child[i]);
  65. return;
  66. }
  67. assert(direc->block == 0);
  68. if((b = Bopen(direc->srcfile, OREAD)) == nil){
  69. fprint(2, "warning: cannot open '%s': %r\n", direc->srcfile);
  70. direc->block = 0;
  71. direc->length = 0;
  72. return;
  73. }
  74. start = cd->nextblock;
  75. assert(start != 0);
  76. if(blocksize && start%blocksize)
  77. start += blocksize-start%blocksize;
  78. Cwseek(cd, (vlong)start * Blocksize);
  79. s = md5(nil, 0, nil, nil);
  80. length = 0;
  81. while((n = Bread(b, buf, sizeof buf)) > 0) {
  82. md5(buf, n, nil, s);
  83. Cwrite(cd, buf, n);
  84. length += n;
  85. }
  86. md5(nil, 0, digest, s);
  87. Bterm(b);
  88. Cpadblock(cd);
  89. if(length != direc->length) {
  90. fprint(2, "warning: %s changed size underfoot\n", direc->srcfile);
  91. direc->length = length;
  92. }
  93. if(length == 0)
  94. direc->block = 0;
  95. else if((dd = lookupmd5(d, digest))) {
  96. assert(dd->length == length);
  97. assert(dd->block != 0);
  98. direc->block = dd->block;
  99. cd->nextblock = start;
  100. } else {
  101. direc->block = start;
  102. if(chatty > 1)
  103. fprint(2, "lookup %.16H %lud (%s) failed\n", digest, length, direc->name);
  104. insertmd5(d, atom(direc->name), digest, start, length);
  105. }
  106. }
  107. /*
  108. * Write a directory tree. We work from the leaves,
  109. * and patch the dotdot pointers afterward.
  110. */
  111. static void
  112. _writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level)
  113. {
  114. int i, l, ll;
  115. ulong start, next;
  116. if((d->mode & DMDIR) == 0)
  117. return;
  118. if(chatty)
  119. fprint(2, "%*s%s\n", 4*level, "", d->name);
  120. for(i=0; i<d->nchild; i++)
  121. _writedirs(cd, &d->child[i], put, level+1);
  122. l = 0;
  123. l += put(cd, d, (level == 0) ? DTrootdot : DTdot, 0, l);
  124. l += put(cd, nil, DTdotdot, 0, l);
  125. for(i=0; i<d->nchild; i++)
  126. l += put(cd, &d->child[i], DTiden, 0, l);
  127. start = cd->nextblock;
  128. cd->nextblock += (l+Blocksize-1)/Blocksize;
  129. next = cd->nextblock;
  130. Cwseek(cd, (vlong)start * Blocksize);
  131. ll = 0;
  132. ll += put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, ll);
  133. ll += put(cd, nil, DTdotdot, 1, ll);
  134. for(i=0; i<d->nchild; i++)
  135. ll += put(cd, &d->child[i], DTiden, 1, ll);
  136. assert(ll == l);
  137. Cpadblock(cd);
  138. assert(Cwoffset(cd) == (vlong)next * Blocksize);
  139. d->block = start;
  140. d->length = (vlong)(next - start) * Blocksize;
  141. rewritedot(cd, d);
  142. rewritedotdot(cd, d, d);
  143. for(i=0; i<d->nchild; i++)
  144. if(d->child[i].mode & DMDIR)
  145. rewritedotdot(cd, &d->child[i], d);
  146. }
  147. void
  148. writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int))
  149. {
  150. /*
  151. * If we're writing a mk9660 image, then the root really
  152. * is the root, so start at level 0. If we're writing a dump image,
  153. * then the "root" is really going to be two levels down once
  154. * we patch in the dump hierarchy above it, so start at level non-zero.
  155. */
  156. if(chatty)
  157. fprint(2, ">>> writedirs\n");
  158. _writedirs(cd, d, put, mk9660 ? 0 : 1);
  159. }
  160. /*
  161. * Write the dump tree. This is like writedirs but once we get to
  162. * the roots of the individual days we just patch the parent dotdot blocks.
  163. */
  164. static void
  165. _writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level)
  166. {
  167. int i;
  168. ulong start;
  169. switch(level) {
  170. case 0:
  171. /* write root, list of years, also conform.map */
  172. for(i=0; i<d->nchild; i++)
  173. if(d->child[i].mode & DMDIR)
  174. _writedumpdirs(cd, &d->child[i], put, level+1);
  175. chat("write dump root dir at %lud\n", cd->nextblock);
  176. goto Writedir;
  177. case 1: /* write year, list of days */
  178. for(i=0; i<d->nchild; i++)
  179. _writedumpdirs(cd, &d->child[i], put, level+1);
  180. chat("write dump %s dir at %lud\n", d->name, cd->nextblock);
  181. goto Writedir;
  182. Writedir:
  183. start = cd->nextblock;
  184. Cwseek(cd, (vlong)start * Blocksize);
  185. put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, Cwoffset(cd));
  186. put(cd, nil, DTdotdot, 1, Cwoffset(cd));
  187. for(i=0; i<d->nchild; i++)
  188. put(cd, &d->child[i], DTiden, 1, Cwoffset(cd));
  189. Cpadblock(cd);
  190. d->block = start;
  191. d->length = (vlong)(cd->nextblock - start) * Blocksize;
  192. rewritedot(cd, d);
  193. rewritedotdot(cd, d, d);
  194. for(i=0; i<d->nchild; i++)
  195. if(d->child[i].mode & DMDIR)
  196. rewritedotdot(cd, &d->child[i], d);
  197. break;
  198. case 2: /* write day: already written, do nothing */
  199. break;
  200. default:
  201. assert(0);
  202. }
  203. }
  204. void
  205. writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int))
  206. {
  207. _writedumpdirs(cd, d, put, 0);
  208. }
  209. static int
  210. Cputplan9(Cdimg *cd, Direc *d, int dot, int dowrite)
  211. {
  212. int l, n;
  213. if(dot != DTiden)
  214. return 0;
  215. l = 0;
  216. if(d->flags & Dbadname) {
  217. n = strlen(d->name);
  218. l += 1+n;
  219. if(dowrite) {
  220. Cputc(cd, n);
  221. Cputs(cd, d->name, n);
  222. }
  223. } else {
  224. l++;
  225. if(dowrite)
  226. Cputc(cd, 0);
  227. }
  228. n = strlen(d->uid);
  229. l += 1+n;
  230. if(dowrite) {
  231. Cputc(cd, n);
  232. Cputs(cd, d->uid, n);
  233. }
  234. n = strlen(d->gid);
  235. l += 1+n;
  236. if(dowrite) {
  237. Cputc(cd, n);
  238. Cputs(cd, d->gid, n);
  239. }
  240. if(l & 1) {
  241. l++;
  242. if(dowrite)
  243. Cputc(cd, 0);
  244. }
  245. l += 8;
  246. if(dowrite)
  247. Cputn(cd, d->mode, 4);
  248. return l;
  249. }
  250. /*
  251. * Write a directory entry.
  252. */
  253. static int
  254. genputdir(Cdimg *cd, Direc *d, int dot, int joliet, int dowrite, int offset)
  255. {
  256. int f, n, l, lp;
  257. vlong o;
  258. f = 0;
  259. if(dot != DTiden || (d->mode & DMDIR))
  260. f |= 2;
  261. n = 1;
  262. if(dot == DTiden) {
  263. if(joliet)
  264. n = 2*utflen(d->confname);
  265. else
  266. n = strlen(d->confname);
  267. }
  268. l = 33+n;
  269. if(l & 1)
  270. l++;
  271. assert(l <= 255);
  272. if(joliet == 0) {
  273. if(cd->flags & CDplan9)
  274. l += Cputplan9(cd, d, dot, 0);
  275. else if(cd->flags & CDrockridge)
  276. l += Cputsysuse(cd, d, dot, 0, l);
  277. assert(l <= 255);
  278. }
  279. if(dowrite == 0) {
  280. if(Blocksize - offset%Blocksize < l)
  281. l += Blocksize - offset%Blocksize;
  282. return l;
  283. }
  284. assert(offset%Blocksize == Cwoffset(cd)%Blocksize);
  285. o = Cwoffset(cd);
  286. lp = 0;
  287. if(Blocksize - Cwoffset(cd)%Blocksize < l) {
  288. lp = Blocksize - Cwoffset(cd)%Blocksize;
  289. Cpadblock(cd);
  290. }
  291. Cputc(cd, l); /* length of directory record */
  292. Cputc(cd, 0); /* extended attribute record length */
  293. if(d) {
  294. if((d->mode & DMDIR) == 0)
  295. assert(d->length == 0 || d->block >= 18);
  296. Cputn(cd, d->block, 4); /* location of extent */
  297. Cputn(cd, d->length, 4); /* data length */
  298. } else {
  299. Cputn(cd, 0, 4);
  300. Cputn(cd, 0, 4);
  301. }
  302. Cputdate(cd, d ? d->mtime : now); /* recorded date */
  303. Cputc(cd, f); /* file flags */
  304. Cputc(cd, 0); /* file unit size */
  305. Cputc(cd, 0); /* interleave gap size */
  306. Cputn(cd, 1, 2); /* volume sequence number */
  307. Cputc(cd, n); /* length of file identifier */
  308. if(dot == DTiden) { /* identifier */
  309. if(joliet)
  310. Cputrscvt(cd, d->confname, n);
  311. else
  312. Cputs(cd, d->confname, n);
  313. }else
  314. if(dot == DTdotdot)
  315. Cputc(cd, 1);
  316. else
  317. Cputc(cd, 0);
  318. if(Cwoffset(cd) & 1) /* pad */
  319. Cputc(cd, 0);
  320. if(joliet == 0) {
  321. if(cd->flags & CDplan9)
  322. Cputplan9(cd, d, dot, 1);
  323. else if(cd->flags & CDrockridge)
  324. Cputsysuse(cd, d, dot, 1, Cwoffset(cd)-(o+lp));
  325. }
  326. assert(o+lp+l == Cwoffset(cd));
  327. return lp+l;
  328. }
  329. int
  330. Cputisodir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset)
  331. {
  332. return genputdir(cd, d, dot, 0, dowrite, offset);
  333. }
  334. int
  335. Cputjolietdir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset)
  336. {
  337. return genputdir(cd, d, dot, 1, dowrite, offset);
  338. }
  339. void
  340. Cputendvd(Cdimg *cd)
  341. {
  342. Cputc(cd, 255); /* volume descriptor set terminator */
  343. Cputs(cd, "CD001", 5); /* standard identifier */
  344. Cputc(cd, 1); /* volume descriptor version */
  345. Cpadblock(cd);
  346. }