xref: /plan9/sys/src/cmd/disk/9660/write.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
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
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
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, d->block*Blocksize);
31 	Cwrite(cd, buf, Blocksize);
32 }
33 
34 void
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, 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
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 
92 	Cwseek(cd, start*Blocksize);
93 
94 	s = md5(nil, 0, nil, nil);
95 	length = 0;
96 	while((n = Bread(b, buf, sizeof buf)) > 0) {
97 		md5(buf, n, nil, s);
98 		Cwrite(cd, buf, n);
99 		length += n;
100 	}
101 	md5(nil, 0, digest, s);
102 	Bterm(b);
103 	Cpadblock(cd);
104 
105 	if(length != direc->length) {
106 		fprint(2, "warning: %s changed size underfoot\n", direc->srcfile);
107 		direc->length = length;
108 	}
109 
110 	if(length == 0)
111 		direc->block = 0;
112 	else if((dd = lookupmd5(d, digest))) {
113 		assert(dd->length == length);
114 		assert(dd->block != 0);
115 		direc->block = dd->block;
116 		cd->nextblock = start;
117 	} else {
118 		direc->block = start;
119 		insertmd5(d, atom(direc->name), digest, start, length);
120 	}
121 }
122 
123 /*
124  * Write a directory tree.  We work from the leaves,
125  * and patch the dotdot pointers afterward.
126  */
127 static void
128 _writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level)
129 {
130 	int i, l, ll;
131 	ulong start, next;
132 
133 	if((d->mode & DMDIR) == 0)
134 		return;
135 
136 	for(i=0; i<d->nchild; i++)
137 		writedirs(cd, &d->child[i], put);
138 
139 	l = 0;
140 	l += put(cd, d, (level == 0) ? DTrootdot : DTdot, 0, l);
141 	l += put(cd, nil, DTdotdot, 0, l);
142 	for(i=0; i<d->nchild; i++)
143 		l += put(cd, &d->child[i], DTiden, 0, l);
144 
145 	start = cd->nextblock;
146 	cd->nextblock += (l+Blocksize-1)/Blocksize;
147 	next = cd->nextblock;
148 
149 	Cwseek(cd, start*Blocksize);
150 	ll = 0;
151 	ll += put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, ll);
152 	ll += put(cd, nil, DTdotdot, 1, ll);
153 	for(i=0; i<d->nchild; i++)
154 		ll += put(cd, &d->child[i], DTiden, 1, ll);
155 	assert(ll == l);
156 	Cpadblock(cd);
157 	assert(Cwoffset(cd) == next*Blocksize);
158 
159 	d->block = start;
160 	d->length = (next - start) * Blocksize;
161 	rewritedot(cd, d);
162 	rewritedotdot(cd, d, d);
163 
164 	for(i=0; i<d->nchild; i++)
165 		if(d->child[i].mode & DMDIR)
166 			rewritedotdot(cd, &d->child[i], d);
167 }
168 
169 void
170 writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int))
171 {
172 	/*
173 	 * If we're writing a mk9660 image, then the root really
174 	 * is the root, so start at level 0.  If we're writing a dump image,
175 	 * then the "root" is really going to be two levels down once
176 	 * we patch in the dump hierarchy above it, so start at level non-zero.
177 	 */
178 	_writedirs(cd, d, put, mk9660 ? 0 : 1);
179 }
180 
181 
182 /*
183  * Write the dump tree.  This is like writedirs but once we get to
184  * the roots of the individual days we just patch the parent dotdot blocks.
185  */
186 static void
187 _writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level)
188 {
189 	int i;
190 	ulong start;
191 
192 	switch(level) {
193 	case 0:
194 		/* write root, list of years, also conform.map */
195 		for(i=0; i<d->nchild; i++)
196 			if(d->child[i].mode & DMDIR)
197 				_writedumpdirs(cd, &d->child[i], put, level+1);
198 		chat("write dump root dir at %lud\n", cd->nextblock);
199 		goto Writedir;
200 
201 	case 1:	/* write year, list of days */
202 		for(i=0; i<d->nchild; i++)
203 			_writedumpdirs(cd, &d->child[i], put, level+1);
204 		chat("write dump %s dir at %lud\n", d->name, cd->nextblock);
205 		goto Writedir;
206 
207 	Writedir:
208 		start = cd->nextblock;
209 		Cwseek(cd, start*Blocksize);
210 
211 		put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, Cwoffset(cd));
212 		put(cd, nil, DTdotdot, 1, Cwoffset(cd));
213 		for(i=0; i<d->nchild; i++)
214 			put(cd, &d->child[i], DTiden, 1, Cwoffset(cd));
215 		Cpadblock(cd);
216 
217 		d->block = start;
218 		d->length = (cd->nextblock - start) * Blocksize;
219 
220 		rewritedot(cd, d);
221 		rewritedotdot(cd, d, d);
222 
223 		for(i=0; i<d->nchild; i++)
224 			if(d->child[i].mode & DMDIR)
225 				rewritedotdot(cd, &d->child[i], d);
226 		break;
227 
228 	case 2:	/* write day: already written, do nothing */
229 		break;
230 
231 	default:
232 		assert(0);
233 	}
234 }
235 
236 void
237 writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int))
238 {
239 	_writedumpdirs(cd, d, put, 0);
240 }
241 
242 static int
243 Cputplan9(Cdimg *cd, Direc *d, int dot, int dowrite)
244 {
245 	int l, n;
246 
247 	if(dot != DTiden)
248 		return 0;
249 
250 	l = 0;
251 	if(d->flags & Dbadname) {
252 		n = strlen(d->name);
253 		l += 1+n;
254 		if(dowrite) {
255 			Cputc(cd, n);
256 			Cputs(cd, d->name, n);
257 		}
258 	} else {
259 		l++;
260 		if(dowrite)
261 			Cputc(cd, 0);
262 	}
263 
264 	n = strlen(d->uid);
265 	l += 1+n;
266 	if(dowrite) {
267 		Cputc(cd, n);
268 		Cputs(cd, d->uid, n);
269 	}
270 
271 	n = strlen(d->gid);
272 	l += 1+n;
273 	if(dowrite) {
274 		Cputc(cd, n);
275 		Cputs(cd, d->gid, n);
276 	}
277 
278 	if(l & 1) {
279 		l++;
280 		if(dowrite)
281 			Cputc(cd, 0);
282 	}
283 	l += 8;
284 	if(dowrite)
285 		Cputn(cd, d->mode, 4);
286 
287 	return l;
288 }
289 
290 /*
291  * Write a directory entry.
292  */
293 static int
294 genputdir(Cdimg *cd, Direc *d, int dot, int joliet, int dowrite, int offset)
295 {
296 	int f, n, l, lp;
297 	long o;
298 
299 	f = 0;
300 	if(dot != DTiden || (d->mode & DMDIR))
301 		f |= 2;
302 
303 	n = 1;
304 	if(dot == DTiden) {
305 		if(joliet)
306 			n = 2*utflen(d->confname);
307 		else
308 			n = strlen(d->confname);
309 	}
310 
311 	l = 33+n;
312 	if(l & 1)
313 		l++;
314 	assert(l <= 255);
315 
316 	if(joliet == 0) {
317 		if(cd->flags & CDplan9)
318 			l += Cputplan9(cd, d, dot, 0);
319 		else if(cd->flags & CDrockridge)
320 			l += Cputsysuse(cd, d, dot, 0, l);
321 		assert(l <= 255);
322 	}
323 
324 	if(dowrite == 0) {
325 		if(Blocksize - offset%Blocksize < l)
326 			l += Blocksize - offset%Blocksize;
327 		return l;
328 	}
329 
330 	assert(offset%Blocksize == Cwoffset(cd)%Blocksize);
331 
332 	o = Cwoffset(cd);
333 	lp = 0;
334 	if(Blocksize - Cwoffset(cd)%Blocksize < l) {
335 		lp = Blocksize - Cwoffset(cd)%Blocksize;
336 		Cpadblock(cd);
337 	}
338 
339 	Cputc(cd, l);			/* length of directory record */
340 	Cputc(cd, 0);			/* extended attribute record length */
341 	if(d) {
342 		if((d->mode & DMDIR) == 0)
343 			assert(d->length == 0 || d->block >= 18);
344 
345 		Cputn(cd, d->block, 4);		/* location of extent */
346 		Cputn(cd, d->length, 4);		/* data length */
347 	} else {
348 		Cputn(cd, 0, 4);
349 		Cputn(cd, 0, 4);
350 	}
351 	Cputdate(cd, d ? d->mtime : now);		/* recorded date */
352 	Cputc(cd, f);			/* file flags */
353 	Cputc(cd, 0);			/* file unit size */
354 	Cputc(cd, 0);			/* interleave gap size */
355 	Cputn(cd, 1, 2);	       	/* volume sequence number */
356 	Cputc(cd, n);			/* length of file identifier */
357 
358 	if(dot == DTiden) {		/* identifier */
359 		if(joliet)
360 			Cputrscvt(cd, d->confname, n);
361 		else
362 			Cputs(cd, d->confname, n);
363 	}else
364 	if(dot == DTdotdot)
365 		Cputc(cd, 1);
366 	else
367 		Cputc(cd, 0);
368 
369 	if(Cwoffset(cd) & 1)			/* pad */
370 		Cputc(cd, 0);
371 
372 	if(joliet == 0) {
373 		if(cd->flags & CDplan9)
374 			Cputplan9(cd, d, dot, 1);
375 		else if(cd->flags & CDrockridge)
376 			Cputsysuse(cd, d, dot, 1, Cwoffset(cd)-(o+lp));
377 	}
378 
379 	assert(o+lp+l == Cwoffset(cd));
380 	return lp+l;
381 }
382 
383 int
384 Cputisodir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset)
385 {
386 	return genputdir(cd, d, dot, 0, dowrite, offset);
387 }
388 
389 int
390 Cputjolietdir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset)
391 {
392 	return genputdir(cd, d, dot, 1, dowrite, offset);
393 }
394 
395 void
396 Cputendvd(Cdimg *cd)
397 {
398 	Cputc(cd, 255);				/* volume descriptor set terminator */
399 	Cputs(cd, "CD001", 5);			/* standard identifier */
400 	Cputc(cd, 1);				/* volume descriptor version */
401 	Cpadblock(cd);
402 }
403