|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <bio.h> | 
|  | #include <disk.h> | 
|  | #include <libsec.h> | 
|  | #include "iso9660.h" | 
|  |  | 
|  | ulong now; | 
|  | int chatty; | 
|  | int doabort; | 
|  | int docolon; | 
|  | int mk9660; | 
|  | Conform *map; | 
|  |  | 
|  | static void addprotofile(char *new, char *old, Dir *d, void *a); | 
|  | void usage(void); | 
|  |  | 
|  | char *argv0; | 
|  |  | 
|  | void | 
|  | usage(void) | 
|  | { | 
|  | if(mk9660) | 
|  | fprint(2, "usage: mk9660 [-D:] [-9cjr] [-b bootfile] [-p proto] [-s src] cdimage\n"); | 
|  | else | 
|  | fprint(2, "usage: dump9660 [-D:] [-9cjr] [-m maxsize] [-n now] [-p proto] [-s src] cdimage\n"); | 
|  | exits("usage"); | 
|  | } | 
|  |  | 
|  | int | 
|  | main(int argc, char **argv) | 
|  | { | 
|  | int fix; | 
|  | char buf[256], *dumpname, *proto, *s, *src, *status; | 
|  | ulong block, length, newnull, cblock, clength, maxsize; | 
|  | Cdimg *cd; | 
|  | Cdinfo info; | 
|  | XDir dir; | 
|  | Direc *iconform, idumproot, iroot, *jconform, jdumproot, jroot, *r; | 
|  | Dump *dump; | 
|  |  | 
|  | fix = 0; | 
|  | status = nil; | 
|  | memset(&info, 0, sizeof info); | 
|  | proto = unsharp("#9/proto/allproto"); | 
|  | src = "./"; | 
|  |  | 
|  | info.volumename = atom("9CD"); | 
|  | info.volumeset = atom("9VolumeSet"); | 
|  | info.publisher = atom("9Publisher"); | 
|  | info.preparer = atom("dump9660"); | 
|  | info.application = atom("dump9660"); | 
|  | info.flags = CDdump; | 
|  | maxsize = 0; | 
|  | mk9660 = 0; | 
|  | fmtinstall('H', encodefmt); | 
|  |  | 
|  | ARGBEGIN{ | 
|  | case 'D': | 
|  | chatty++; | 
|  | break; | 
|  | case 'M': | 
|  | mk9660 = 1; | 
|  | argv0 = "disk/mk9660"; | 
|  | info.flags &= ~CDdump; | 
|  | break; | 
|  | case '9': | 
|  | info.flags |= CDplan9; | 
|  | break; | 
|  | case ':': | 
|  | docolon = 1; | 
|  | break; | 
|  | case 'a': | 
|  | doabort = 1; | 
|  | break; | 
|  | case 'b': | 
|  | if(!mk9660) | 
|  | usage(); | 
|  | info.flags |= CDbootable; | 
|  | info.bootimage = EARGF(usage()); | 
|  | break; | 
|  | case 'c': | 
|  | info.flags |= CDconform; | 
|  | break; | 
|  | case 'f': | 
|  | fix = 1; | 
|  | break; | 
|  | case 'j': | 
|  | info.flags |= CDjoliet; | 
|  | break; | 
|  | case 'n': | 
|  | now = atoi(EARGF(usage())); | 
|  | break; | 
|  | case 'm': | 
|  | maxsize = strtoul(EARGF(usage()), 0, 0); | 
|  | break; | 
|  | case 'p': | 
|  | proto = EARGF(usage()); | 
|  | break; | 
|  | case 'r': | 
|  | info.flags |= CDrockridge; | 
|  | break; | 
|  | case 's': | 
|  | src = EARGF(usage()); | 
|  | break; | 
|  | case 'v': | 
|  | info.volumename = atom(EARGF(usage())); | 
|  | break; | 
|  | default: | 
|  | usage(); | 
|  | }ARGEND | 
|  |  | 
|  | if(mk9660 && (fix || now || maxsize)) | 
|  | usage(); | 
|  |  | 
|  | if(argc != 1) | 
|  | usage(); | 
|  |  | 
|  | if(now == 0) | 
|  | now = (ulong)time(0); | 
|  | if(mk9660){ | 
|  | if((cd = createcd(argv[0], info)) == nil) | 
|  | sysfatal("cannot create '%s': %r", argv[0]); | 
|  | }else{ | 
|  | if((cd = opencd(argv[0], info)) == nil) | 
|  | sysfatal("cannot open '%s': %r", argv[0]); | 
|  | if(!(cd->flags & CDdump)) | 
|  | sysfatal("not a dump cd"); | 
|  | } | 
|  |  | 
|  | /* create ISO9660/Plan 9 tree in memory */ | 
|  | memset(&dir, 0, sizeof dir); | 
|  | dir.name = atom(""); | 
|  | dir.uid = atom("sys"); | 
|  | dir.gid = atom("sys"); | 
|  | dir.uidno = 0; | 
|  | dir.gidno = 0; | 
|  | dir.mode = DMDIR | 0755; | 
|  | dir.mtime = now; | 
|  | dir.atime = now; | 
|  | dir.ctime = now; | 
|  |  | 
|  | mkdirec(&iroot, &dir); | 
|  | iroot.srcfile = src; | 
|  |  | 
|  | /* | 
|  | * Read new files into memory | 
|  | */ | 
|  | if(rdproto(proto, src, addprotofile, 0, &iroot) < 0) | 
|  | sysfatal("rdproto: %r"); | 
|  |  | 
|  | if(mk9660){ | 
|  | dump = emalloc(sizeof *dump); | 
|  | dumpname = nil; | 
|  | }else{ | 
|  | /* | 
|  | * Read current dump tree and _conform.map. | 
|  | */ | 
|  | idumproot = readdumpdirs(cd, &dir, isostring); | 
|  | readdumpconform(cd); | 
|  | if(cd->flags & CDjoliet) | 
|  | jdumproot = readdumpdirs(cd, &dir, jolietstring); | 
|  |  | 
|  | if(fix){ | 
|  | dumpname = nil; | 
|  | cd->nextblock = cd->nulldump+1; | 
|  | cd->nulldump = 0; | 
|  | Cwseek(cd, cd->nextblock*Blocksize); | 
|  | goto Dofix; | 
|  | } | 
|  |  | 
|  | dumpname = adddumpdir(&idumproot, now, &dir); | 
|  | /* note that we assume all names are conforming and thus sorted */ | 
|  | if(cd->flags & CDjoliet) { | 
|  | s = adddumpdir(&jdumproot, now, &dir); | 
|  | if(s != dumpname) | 
|  | sysfatal("dumpnames don't match %s %s\n", dumpname, s); | 
|  | } | 
|  | dump = dumpcd(cd, &idumproot); | 
|  | cd->nextblock = cd->nulldump+1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Write new files, starting where the dump tree was. | 
|  | * Must be done before creation of the Joliet tree so that | 
|  | * blocks and lengths are correct. | 
|  | */ | 
|  | Cwseek(cd, cd->nextblock*Blocksize); | 
|  | writefiles(dump, cd, &iroot); | 
|  |  | 
|  | if(cd->bootimage){ | 
|  | findbootimage(cd, &iroot); | 
|  | Cupdatebootcat(cd); | 
|  | } | 
|  |  | 
|  | /* create Joliet tree */ | 
|  | if(cd->flags & CDjoliet) | 
|  | copydirec(&jroot, &iroot); | 
|  |  | 
|  | if(info.flags & CDconform) { | 
|  | checknames(&iroot, isbadiso9660); | 
|  | convertnames(&iroot, struprcpy); | 
|  | } else | 
|  | convertnames(&iroot, (char* (*)(char*, char*))strcpy); | 
|  |  | 
|  | /*	isoabstract = findconform(&iroot, abstract); */ | 
|  | /*	isobiblio = findconform(&iroot, biblio); */ | 
|  | /*	isonotice = findconform(&iroot, notice); */ | 
|  |  | 
|  | dsort(&iroot, isocmp); | 
|  |  | 
|  | if(cd->flags & CDjoliet) { | 
|  | /*	jabstract = findconform(&jroot, abstract); */ | 
|  | /*	jbiblio = findconform(&jroot, biblio); */ | 
|  | /*	jnotice = findconform(&jroot, notice); */ | 
|  |  | 
|  | checknames(&jroot, isbadjoliet); | 
|  | convertnames(&jroot, (char* (*)(char*, char*))strcpy); | 
|  | dsort(&jroot, jolietcmp); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Write directories. | 
|  | */ | 
|  | writedirs(cd, &iroot, Cputisodir); | 
|  | if(cd->flags & CDjoliet) | 
|  | writedirs(cd, &jroot, Cputjolietdir); | 
|  |  | 
|  | if(mk9660){ | 
|  | cblock = 0; | 
|  | clength = 0; | 
|  | newnull = 0; | 
|  | }else{ | 
|  | /* | 
|  | * Write incremental _conform.map block. | 
|  | */ | 
|  | wrconform(cd, cd->nconform, &cblock, &clength); | 
|  |  | 
|  | /* jump here if we're just fixing up the cd */ | 
|  | Dofix: | 
|  | /* | 
|  | * Write null dump header block; everything after this will be | 
|  | * overwritten at the next dump.  Because of this, it needs to be | 
|  | * reconstructable.  We reconstruct the _conform.map and dump trees | 
|  | * from the header blocks in dump.c, and we reconstruct the path | 
|  | * tables by walking the cd. | 
|  | */ | 
|  | newnull = Cputdumpblock(cd); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Write _conform.map. | 
|  | */ | 
|  | dir.mode = 0444; | 
|  | if(cd->flags & (CDconform|CDjoliet)) { | 
|  | if(!mk9660 && cd->nconform == 0){ | 
|  | block = cblock; | 
|  | length = clength; | 
|  | }else | 
|  | wrconform(cd, 0, &block, &length); | 
|  |  | 
|  | if(mk9660) | 
|  | { | 
|  | idumproot = iroot; | 
|  | jdumproot = jroot; | 
|  | } | 
|  | if(length) { | 
|  | /* The ISO9660 name will get turned into uppercase when written. */ | 
|  | if((iconform = walkdirec(&idumproot, "_conform.map")) == nil) | 
|  | iconform = adddirec(&idumproot, "_conform.map", &dir); | 
|  | jconform = nil; | 
|  | if(cd->flags & CDjoliet) { | 
|  | if((jconform = walkdirec(&jdumproot, "_conform.map")) == nil) | 
|  | jconform = adddirec(&jdumproot, "_conform.map", &dir); | 
|  | } | 
|  | iconform->block = block; | 
|  | iconform->length = length; | 
|  | if(cd->flags & CDjoliet) { | 
|  | jconform->block = block; | 
|  | jconform->length = length; | 
|  | } | 
|  | } | 
|  | if(mk9660) { | 
|  | iroot = idumproot; | 
|  | jroot = jdumproot; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(mk9660){ | 
|  | /* | 
|  | * Patch in root directories. | 
|  | */ | 
|  | setroot(cd, cd->iso9660pvd, iroot.block, iroot.length); | 
|  | setvolsize(cd, cd->iso9660pvd, cd->nextblock*Blocksize); | 
|  | if(cd->flags & CDjoliet){ | 
|  | setroot(cd, cd->jolietsvd, jroot.block, jroot.length); | 
|  | setvolsize(cd, cd->jolietsvd, cd->nextblock*Blocksize); | 
|  | } | 
|  | }else{ | 
|  | /* | 
|  | * Write dump tree at end.  We assume the name characters | 
|  | * are all conforming, so everything is already sorted properly. | 
|  | */ | 
|  | convertnames(&idumproot, (info.flags & CDconform) ? struprcpy : (char* (*)(char*, char*)) strcpy); | 
|  | if(cd->nulldump) { | 
|  | r = walkdirec(&idumproot, dumpname); | 
|  | assert(r != nil); | 
|  | copybutname(r, &iroot); | 
|  | } | 
|  | if(cd->flags & CDjoliet) { | 
|  | convertnames(&jdumproot, (char* (*)(char*, char*))strcpy); | 
|  | if(cd->nulldump) { | 
|  | r = walkdirec(&jdumproot, dumpname); | 
|  | assert(r != nil); | 
|  | copybutname(r, &jroot); | 
|  | } | 
|  | } | 
|  |  | 
|  | writedumpdirs(cd, &idumproot, Cputisodir); | 
|  | if(cd->flags & CDjoliet) | 
|  | writedumpdirs(cd, &jdumproot, Cputjolietdir); | 
|  |  | 
|  | /* | 
|  | * Patch in new root directory entry. | 
|  | */ | 
|  | setroot(cd, cd->iso9660pvd, idumproot.block, idumproot.length); | 
|  | setvolsize(cd, cd->iso9660pvd, cd->nextblock*Blocksize); | 
|  | if(cd->flags & CDjoliet){ | 
|  | setroot(cd, cd->jolietsvd, jdumproot.block, jdumproot.length); | 
|  | setvolsize(cd, cd->jolietsvd, cd->nextblock*Blocksize); | 
|  | } | 
|  | } | 
|  | writepathtables(cd); | 
|  |  | 
|  | if(!mk9660){ | 
|  | /* | 
|  | * If we've gotten too big, truncate back to what we started with, | 
|  | * fix up the cd, and exit with a non-zero status. | 
|  | */ | 
|  | Cwflush(cd); | 
|  | if(cd->nulldump && maxsize && Cwoffset(cd) > maxsize){ | 
|  | fprint(2, "too big; writing old tree back\n"); | 
|  | status = "cd too big; aborted"; | 
|  |  | 
|  | rmdumpdir(&idumproot, dumpname); | 
|  | rmdumpdir(&jdumproot, dumpname); | 
|  |  | 
|  | cd->nextblock = cd->nulldump+1; | 
|  | cd->nulldump = 0; | 
|  | Cwseek(cd, cd->nextblock*Blocksize); | 
|  | goto Dofix; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Write old null header block; this commits all our changes. | 
|  | */ | 
|  | if(cd->nulldump){ | 
|  | Cwseek(cd, cd->nulldump*Blocksize); | 
|  | sprint(buf, "plan 9 dump cd\n"); | 
|  | sprint(buf+strlen(buf), "%s %lud %lud %lud %lud %lud %lud", | 
|  | dumpname, now, newnull, cblock, clength, iroot.block, | 
|  | iroot.length); | 
|  | if(cd->flags & CDjoliet) | 
|  | sprint(buf+strlen(buf), " %lud %lud", | 
|  | jroot.block, jroot.length); | 
|  | strcat(buf, "\n"); | 
|  | Cwrite(cd, buf, strlen(buf)); | 
|  | Cpadblock(cd); | 
|  | Cwflush(cd); | 
|  | } | 
|  | } | 
|  | fdtruncate(cd->fd, cd->nextblock*Blocksize); | 
|  | exits(status); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | addprotofile(char *new, char *old, Dir *d, void *a) | 
|  | { | 
|  | char *name, *p; | 
|  | Direc *direc; | 
|  | XDir xd; | 
|  |  | 
|  | dirtoxdir(&xd, d); | 
|  | name = nil; | 
|  | if(docolon && strchr(new, ':')) { | 
|  | name = emalloc(strlen(new)+1); | 
|  | strcpy(name, new); | 
|  | while((p=strchr(name, ':'))) | 
|  | *p=' '; | 
|  | new = name; | 
|  | } | 
|  | if((direc = adddirec((Direc*)a, new, &xd))) { | 
|  | direc->srcfile = atom(old); | 
|  |  | 
|  | /* BUG: abstract, biblio, notice */ | 
|  | } | 
|  | if(name) | 
|  | free(name); | 
|  |  | 
|  | } | 
|  |  |