xref: /plan9/sys/src/cmd/disk/9660/write.c (revision 73ee67a1775bb9176ad6f77775e030678f092526)
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