xref: /plan9/sys/src/cmd/disk/9660/dump9660.c (revision a587111c8770e522e3667ff2b63cba8a77811dd9)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <disk.h>
5 #include <libsec.h>
6 #include "iso9660.h"
7 
8 ulong now;
9 int chatty;
10 int doabort;
11 int docolon;
12 int mk9660;
13 vlong dataoffset;
14 int blocksize;
15 Conform *map;
16 
17 static void addprotofile(char *new, char *old, Dir *d, void *a);
18 void usage(void);
19 
20 char *argv0;
21 
22 void
usage(void)23 usage(void)
24 {
25 	if(mk9660)
26 		fprint(2, "usage: disk/mk9660 [-D:] [-9cjr] "
27 			"[-[bB] bootfile] [-o offset blocksize] "
28 			"[-p proto] [-s src] cdimage\n");
29 	else
30 		fprint(2, "usage: disk/dump9660 [-D:] [-9cjr] "
31 			"[-m maxsize] [-n now] "
32 			"[-p proto] [-s src] cdimage\n");
33 	exits("usage");
34 }
35 
36 void
main(int argc,char ** argv)37 main(int argc, char **argv)
38 {
39 	int fix;
40 	ulong block, newnull, cblock;
41 	vlong maxsize;
42 	uvlong length, clength;
43 	char buf[256], *dumpname, *proto, *s, *src, *status;
44 	Cdimg *cd;
45 	Cdinfo info;
46 	XDir dir;
47 	Direc *iconform, idumproot, iroot, *jconform, jdumproot, jroot, *r;
48 	Dump *dump;
49 
50 	fix = 0;
51 	status = nil;
52 	memset(&info, 0, sizeof info);
53 	proto = "/sys/lib/sysconfig/proto/allproto";
54 	src = "./";
55 
56 	info.volumename = atom("9CD");
57 	info.volumeset = atom("9VolumeSet");
58 	info.publisher = atom("9Publisher");
59 	info.preparer = atom("dump9660");
60 	info.application = atom("dump9660");
61 	info.flags = CDdump;
62 	maxsize = 0;
63 	mk9660 = 0;
64 	fmtinstall('H', encodefmt);
65 
66 	ARGBEGIN{
67 	case 'D':
68 		chatty++;
69 		break;
70 	case 'M':
71 		mk9660 = 1;
72 		argv0 = "disk/mk9660";
73 		info.flags &= ~CDdump;
74 		break;
75 	case '9':
76 		info.flags |= CDplan9;
77 		break;
78 	case ':':
79 		docolon = 1;
80 		break;
81 	case 'a':
82 		doabort = 1;
83 		break;
84 	case 'B':
85 		info.flags |= CDbootnoemu;
86 		/* fall through */
87 	case 'b':
88 		if(!mk9660)
89 			usage();
90 		info.flags |= CDbootable;
91 		info.bootimage = EARGF(usage());
92 		break;
93 	case 'c':
94 		info.flags |= CDconform;
95 		break;
96 	case 'f':
97 		fix = 1;
98 		break;
99 	case 'j':
100 		info.flags |= CDjoliet;
101 		break;
102 	case 'n':
103 		now = atoi(EARGF(usage()));
104 		break;
105 	case 'm':
106 		maxsize = strtoull(EARGF(usage()), 0, 0);
107 		break;
108 	case 'o':
109 		dataoffset = atoll(EARGF(usage()));
110 		blocksize = atoi(EARGF(usage()));
111 		if(blocksize%Blocksize)
112 			sysfatal("bad block size %d -- must be multiple of 2048", blocksize);
113 		blocksize /= Blocksize;
114 		break;
115 	case 'p':
116 		proto = EARGF(usage());
117 		break;
118 	case 'r':
119 		info.flags |= CDrockridge;
120 		break;
121 	case 's':
122 		src = EARGF(usage());
123 		break;
124 	case 'v':
125 		info.volumename = atom(EARGF(usage()));
126 		break;
127 	case 'x':
128 		info.flags |= CDpbs;
129 		info.loader = EARGF(usage());
130 		break;
131 	default:
132 		usage();
133 	}ARGEND
134 
135 	if(info.flags & CDpbs && !(info.flags & CDbootnoemu))
136 		usage();
137 
138 	if(mk9660 && (fix || now || maxsize))
139 		usage();
140 
141 	if(argc != 1)
142 		usage();
143 
144 	if(now == 0)
145 		now = (ulong)time(0);
146 	if(mk9660){
147 		if((cd = createcd(argv[0], info)) == nil)
148 			sysfatal("cannot create '%s': %r", argv[0]);
149 	}else{
150 		if((cd = opencd(argv[0], info)) == nil)
151 			sysfatal("cannot open '%s': %r", argv[0]);
152 		if(!(cd->flags & CDdump))
153 			sysfatal("not a dump cd");
154 	}
155 
156 	/* create ISO9660/Plan 9 tree in memory */
157 	memset(&dir, 0, sizeof dir);
158 	dir.name = atom("");
159 	dir.uid = atom("sys");
160 	dir.gid = atom("sys");
161 	dir.uidno = 0;
162 	dir.gidno = 0;
163 	dir.mode = DMDIR | 0755;
164 	dir.mtime = now;
165 	dir.atime = now;
166 	dir.ctime = now;
167 
168 	mkdirec(&iroot, &dir);
169 	iroot.srcfile = src;
170 
171 	/*
172 	 * Read new files into memory
173 	 */
174 	if(rdproto(proto, src, addprotofile, nil, &iroot) < 0)
175 		sysfatal("rdproto: %r");
176 
177 	if(mk9660){
178 		dump = emalloc(sizeof *dump);
179 		dumpname = nil;
180 	}else{
181 		/*
182 		 * Read current dump tree and _conform.map.
183 		 */
184 		idumproot = readdumpdirs(cd, &dir, isostring);
185 		readdumpconform(cd);
186 		if(cd->flags & CDjoliet)
187 			jdumproot = readdumpdirs(cd, &dir, jolietstring);
188 
189 		if(fix){
190 			dumpname = nil;
191 			cd->nextblock = cd->nulldump+1;
192 			cd->nulldump = 0;
193 			Cwseek(cd, (vlong)cd->nextblock * Blocksize);
194 			goto Dofix;
195 		}
196 
197 		dumpname = adddumpdir(&idumproot, now, &dir);
198 		/* note that we assume all names are conforming and thus sorted */
199 		if(cd->flags & CDjoliet) {
200 			s = adddumpdir(&jdumproot, now, &dir);
201 			if(s != dumpname)
202 				sysfatal("dumpnames don't match %s %s", dumpname, s);
203 		}
204 		dump = dumpcd(cd, &idumproot);
205 		cd->nextblock = cd->nulldump+1;
206 	}
207 
208 	/*
209 	 * Write new files, starting where the dump tree was.
210  	 * Must be done before creation of the Joliet tree so that
211  	 * blocks and lengths are correct.
212 	 */
213 	if(dataoffset > (vlong)cd->nextblock * Blocksize)
214 		cd->nextblock = (dataoffset+Blocksize-1)/Blocksize;
215 	Cwseek(cd, (vlong)cd->nextblock * Blocksize);
216 	writefiles(dump, cd, &iroot);
217 
218 	if(cd->bootimage){
219 		findbootimage(cd, &iroot);
220 		if(cd->loader)
221 			findloader(cd, &iroot);
222 		Cupdatebootcat(cd);
223 	}
224 
225 	/* create Joliet tree */
226 	if(cd->flags & CDjoliet)
227 		copydirec(&jroot, &iroot);
228 
229 	if(info.flags & CDconform) {
230 		checknames(&iroot, isbadiso9660);
231 		convertnames(&iroot, struprcpy);
232 	} else
233 		convertnames(&iroot, (void *) strcpy);
234 
235 //	isoabstract = findconform(&iroot, abstract);
236 //	isobiblio = findconform(&iroot, biblio);
237 //	isonotice = findconform(&iroot, notice);
238 
239 	dsort(&iroot, isocmp);
240 
241 	if(cd->flags & CDjoliet) {
242 	//	jabstract = findconform(&jroot, abstract);
243 	//	jbiblio = findconform(&jroot, biblio);
244 	//	jnotice = findconform(&jroot, notice);
245 
246 		checknames(&jroot, isbadjoliet);
247 		convertnames(&jroot, (void *) strcpy);
248 		dsort(&jroot, jolietcmp);
249 	}
250 
251 	/*
252 	 * Write directories.
253 	 */
254 	writedirs(cd, &iroot, Cputisodir);
255 	if(cd->flags & CDjoliet)
256 		writedirs(cd, &jroot, Cputjolietdir);
257 
258 	if(mk9660){
259 		cblock = 0;
260 		clength = 0;
261 		newnull = 0;
262 	}else{
263 		/*
264 		 * Write incremental _conform.map block.
265 		 */
266 		wrconform(cd, cd->nconform, &cblock, &clength);
267 
268 		/* jump here if we're just fixing up the cd */
269 Dofix:
270 		/*
271 		 * Write null dump header block; everything after this will be
272 		 * overwritten at the next dump.  Because of this, it needs to be
273 		 * reconstructable.  We reconstruct the _conform.map and dump trees
274 		 * from the header blocks in dump.c, and we reconstruct the path
275 		 * tables by walking the cd.
276 		 */
277 		newnull = Cputdumpblock(cd);
278 	}
279 	if(info.flags & CDpbs)
280 		Cfillpbs(cd);
281 
282 	/*
283 	 * Write _conform.map.
284 	 */
285 	dir.mode = 0444;
286 	if(cd->flags & (CDconform|CDjoliet)) {
287 		if(!mk9660 && cd->nconform == 0){
288 			block = cblock;
289 			length = clength;
290 		}else
291 			wrconform(cd, 0, &block, &length);
292 
293 		if(mk9660)
294 {
295 			idumproot = iroot;
296 			jdumproot = jroot;
297 		}
298 		if(length) {
299 			/* The ISO9660 name will get turned into uppercase when written. */
300 			if((iconform = walkdirec(&idumproot, "_conform.map")) == nil)
301 				iconform = adddirec(&idumproot, "_conform.map", &dir);
302 			jconform = nil;
303 			if(cd->flags & CDjoliet) {
304 				if((jconform = walkdirec(&jdumproot, "_conform.map")) == nil)
305 					jconform = adddirec(&jdumproot, "_conform.map", &dir);
306 			}
307 			iconform->block = block;
308 			iconform->length = length;
309 			if(cd->flags & CDjoliet) {
310 				jconform->block = block;
311 				jconform->length = length;
312 			}
313 		}
314 		if(mk9660) {
315 			iroot = idumproot;
316 			jroot = jdumproot;
317 		}
318 	}
319 
320 	if(mk9660){
321 		/*
322 		 * Patch in root directories.
323 		 */
324 		setroot(cd, cd->iso9660pvd, iroot.block, iroot.length);
325 		setvolsize(cd, cd->iso9660pvd, cd->nextblock);
326 		if(cd->flags & CDjoliet){
327 			setroot(cd, cd->jolietsvd, jroot.block, jroot.length);
328 			setvolsize(cd, cd->jolietsvd, cd->nextblock);
329 		}
330 	}else{
331 		/*
332 		 * Write dump tree at end.  We assume the name characters
333 		 * are all conforming, so everything is already sorted properly.
334 		 */
335 		convertnames(&idumproot, (info.flags & CDconform) ? (void *) struprcpy : (void *) strcpy);
336 		if(cd->nulldump) {
337 			r = walkdirec(&idumproot, dumpname);
338 			assert(r != nil);
339 			copybutname(r, &iroot);
340 		}
341 		if(cd->flags & CDjoliet) {
342 			convertnames(&jdumproot, (void *) strcpy);
343 			if(cd->nulldump) {
344 				r = walkdirec(&jdumproot, dumpname);
345 				assert(r != nil);
346 				copybutname(r, &jroot);
347 			}
348 		}
349 
350 		writedumpdirs(cd, &idumproot, Cputisodir);
351 		if(cd->flags & CDjoliet)
352 			writedumpdirs(cd, &jdumproot, Cputjolietdir);
353 
354 		/*
355 		 * Patch in new root directory entry.
356 		 */
357 		setroot(cd, cd->iso9660pvd, idumproot.block, idumproot.length);
358 		setvolsize(cd, cd->iso9660pvd, cd->nextblock);
359 		if(cd->flags & CDjoliet){
360 			setroot(cd, cd->jolietsvd, jdumproot.block, jdumproot.length);
361 			setvolsize(cd, cd->jolietsvd, cd->nextblock);
362 		}
363 	}
364 	writepathtables(cd);
365 
366 	if(!mk9660){
367 		/*
368 		 * If we've gotten too big, truncate back to what we started with,
369 		 * fix up the cd, and exit with a non-zero status.
370 		 */
371 		Cwflush(cd);
372 		if(cd->nulldump && maxsize && Cwoffset(cd) > maxsize){
373 			fprint(2, "too big; writing old tree back\n");
374 			status = "cd too big; aborted";
375 
376 			rmdumpdir(&idumproot, dumpname);
377 			rmdumpdir(&jdumproot, dumpname);
378 
379 			cd->nextblock = cd->nulldump+1;
380 			cd->nulldump = 0;
381 			Cwseek(cd, (vlong)cd->nextblock * Blocksize);
382 			goto Dofix;
383 		}
384 
385 		/*
386 		 * Write old null header block; this commits all our changes.
387 		 */
388 		if(cd->nulldump){
389 			Cwseek(cd, (vlong)cd->nulldump * Blocksize);
390 			sprint(buf, "plan 9 dump cd\n");
391 			sprint(buf+strlen(buf), "%s %lud %lud %lud %llud %lud %lud",
392 				dumpname, now, newnull, cblock, clength,
393 				iroot.block, iroot.length);
394 			if(cd->flags & CDjoliet)
395 				sprint(buf+strlen(buf), " %lud %lud",
396 					jroot.block, jroot.length);
397 			strcat(buf, "\n");
398 			Cwrite(cd, buf, strlen(buf));
399 			Cpadblock(cd);
400 			Cwflush(cd);
401 		}
402 	}
403 	fdtruncate(cd->fd, (vlong)cd->nextblock * Blocksize);
404 	exits(status);
405 }
406 
407 static void
addprotofile(char * new,char * old,Dir * d,void * a)408 addprotofile(char *new, char *old, Dir *d, void *a)
409 {
410 	char *name, *p;
411 	Direc *direc;
412 	XDir xd;
413 
414 	dirtoxdir(&xd, d);
415 	name = nil;
416 	if(docolon && strchr(new, ':')) {
417 		name = emalloc(strlen(new)+1);
418 		strcpy(name, new);
419 		while((p=strchr(name, ':')))
420 			*p=' ';
421 		new = name;
422 	}
423 	if((direc = adddirec((Direc*)a, new, &xd))) {
424 		direc->srcfile = atom(old);
425 
426 		// BUG: abstract, biblio, notice
427 	}
428 	if(name)
429 		free(name);
430 }
431