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