Dump9660 (and mk9660). Until we either do something

intelligent with symlinks or put in a switch for things
like dump9660, this is of rather limited utility under Unix.
This commit is contained in:
wkj 2004-06-17 01:47:21 +00:00
parent e1dddc0532
commit 7285a491c1
20 changed files with 4375 additions and 0 deletions

189
src/cmd/9660/boot.c Normal file
View file

@ -0,0 +1,189 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include "iso9660.h"
/* FreeBSD 4.5 installation CD for reference
g% cdsector 17 | xd -b
1+0 records in
1+0 records out
0000000 00 - volume descriptor type (0)
43 44 30 30 31 - "CD001"
01 - volume descriptor version (1)
45 4c 20 54 4f 52 49 54 4f
0000010 20 53 50 45 43 49 46 49 43 41 54 49 4f 4e 00 00
0000020 00 00 00 00 00 00 00 - 7-39 boot system identifier
"EL TORITO SPECIFICATION"
00 00 00 00 00 00 00 00 00
0000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000040 00 00 00 00 00 00 00 - 39-71 boot identifier
boot system use:
absolute pointer to the boot catalog??
4d 0c 00 00 00 00 00 00 00
0000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000580 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000590 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00005a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
g% cdsector 3149 | xd -b # 0x0c4d
1+0 records in
1+0 records out
0000000 01 - header (1)
00 - platform id (0 = 0x86)
00 00 - reserved 0
00 00 00 00 00 00 00 00 00 00 00 00
0000010 00 00 00 00 00 00 00 00 00 00 00 00 - id string
aa 55 - checksum
55 aa - magic
0000020 88 - 88 = bootable
03 - 3 = 2.88MB diskette
00 00 - load segment 0 means default 0x7C0
00 - system type (byte 5 of boot image)
00 - unused (0)
01 00 - 512-byte sector count for initial load
4e 0c 00 00 - ptr to virtual disk
00 00 00 00
0000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
g% cdsector `{h2d 0c4e} | xd -b
1+0 records in
1+0 records out
0000000 eb 3c 00 00 00 00 00 00 00 00 00 00 02 00 00 00
0000010 00 00 00 00 00 00 00 00 12 00 02 00 00 00 00 00
0000020 00 00 00 00 00 16 1f 66 6a 00 51 50 06 53
31 c0
FREEBSD
0000000 eb 3c 00 00 00 00 00 00 00 00 00 00 02 00 00 00
0000010 00 00 00 00 00 00 00 00 12 00 02 00 00 00 00 00
0000020 00 00 00 00 00 16 1f 66 6a 00 51 50 06 53
31 c0
DOS 5
0000000 eb 3c 90 4d 53 44 4f 53 35 2e 30 00 02 01 01 00
0000010 02 e0 00 40 0b f0 09 00 12 00 02 00 00 00 00 00
0000020 00 00 00 00 00 00 29 6a 2c e0 16 4e 4f 20 4e 41
0000030 4d 45 20 20 20 20 46 41 54 31 32 20 20 20 fa 33
0000040 c0 8e d0 bc 00 7c 16 07 bb 78 00 36 c5 37 1e 56
0000050 16 53 bf 3e 7c b9 0b 00 fc f3 a4 06 1f c6 45 fe
0000060 0f 8b 0e 18 7c 88 4d f9 89 47 02 c7 07 3e 7c fb
0000070 cd 13 72 79 33 c0 39 06 13 7c 74 08 8b 0e 13 7c
0000080 89 0e 20 7c a0 10 7c f7 26 16 7c 03 06 1c 7c 13
0000090 16 1e 7c 03 06 0e 7c 83 d2 00 a3 50 7c 89 16 52
00000a0 7c a3 49 7c 89 16 4b 7c b8 20 00 f7 26 11 7c 8b
NDISK
0000000 eb 3c 90 50 6c 61 6e 39 2e 30 30 00 02 01 01 00
0000010 02 e0 00 40 0b f0 09 00 12 00 02 00 00 00 00 00
0000020 40 0b 00 00 00 00 29 13 00 00 00 43 59 4c 49 4e
0000030 44 52 49 43 41 4c 46 41 54 31 32 20 20 20 fa 31
0000040 c0 8e d0 8e d8 8e c0 bc ec 7b 89 e5 88 56 12 fb
0000050 be ea 7d bf 90 7d ff d7 bf 82 7d ff d7 8b 06 27
0000060 7c 8b 16 29 7c bb 00 7e bf 2c 7d ff d7 bb 10 00
PBSDISK
0000000 eb 3c 90 50 6c 61 6e 39 2e 30 30 00 02 01 01 00
0000010 02 e0 00 40 0b f0 09 00 12 00 02 00 00 00 00 00
0000020 40 0b 00 00 00 00 29 13 00 00 00 43 59 4c 49 4e
0000030 44 52 49 43 41 4c 46 41 54 31 32 20 20 20 fa 31
0000040 c0 8e d0 8e d8 8e c0 bc f8 7b 89 e5 88 56 00 fb
0000050 be f8 7d bf 00 7d ff d7 bf df 7c ff d7 8b 06 27
0000060 7c 8b 16 29 7c bb 00 7e bf 89 7c ff d7 bf fb 7c
*/
void
Cputbootvol(Cdimg *cd)
{
Cputc(cd, 0x00);
Cputs(cd, "CD001", 5);
Cputc(cd, 0x01);
Cputs(cd, "EL TORITO SPECIFICATION", 2+1+6+1+13);
Crepeat(cd, 0, 2+16+16+7);
cd->bootcatptr = Cwoffset(cd);
Cpadblock(cd);
}
void
Cupdatebootvol(Cdimg *cd)
{
ulong o;
o = Cwoffset(cd);
Cwseek(cd, cd->bootcatptr);
Cputnl(cd, cd->bootcatblock, 4);
Cwseek(cd, o);
}
void
Cputbootcat(Cdimg *cd)
{
cd->bootcatblock = Cwoffset(cd) / Blocksize;
Cputc(cd, 0x01);
Cputc(cd, 0x00);
Cputc(cd, 0x00);
Cputc(cd, 0x00);
Crepeat(cd, 0, 12+12);
/*
* either the checksum doesn't include the header word
* or it just doesn't matter.
*/
Cputc(cd, 0xAA);
Cputc(cd, 0x55);
Cputc(cd, 0x55);
Cputc(cd, 0xAA);
cd->bootimageptr = Cwoffset(cd);
Cpadblock(cd);
}
void
Cupdatebootcat(Cdimg *cd)
{
ulong o;
if(cd->bootdirec == nil)
return;
o = Cwoffset(cd);
Cwseek(cd, cd->bootimageptr);
Cputc(cd, 0x88);
switch(cd->bootdirec->length){
default:
fprint(2, "warning: boot image is not 1.44MB or 2.88MB; pretending 1.44MB\n");
case 1440*1024:
Cputc(cd, 0x02); /* 1.44MB disk */
break;
case 2880*1024:
Cputc(cd, 0x03); /* 2.88MB disk */
break;
}
Cputnl(cd, 0, 2); /* load segment */
Cputc(cd, 0); /* system type */
Cputc(cd, 0); /* unused */
Cputnl(cd, 1, 2); /* 512-byte sector count for load */
Cputnl(cd, cd->bootdirec->block, 4); /* ptr to disk image */
Cwseek(cd, o);
}
void
findbootimage(Cdimg *cd, Direc *root)
{
Direc *d;
d = walkdirec(root, cd->bootimage);
if(d == nil){
fprint(2, "warning: did not encounter boot image\n");
return;
}
cd->bootdirec = d;
}

632
src/cmd/9660/cdrdwr.c Normal file
View file

@ -0,0 +1,632 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include "iso9660.h"
static int readisodesc(Cdimg*, Voldesc*);
static int readjolietdesc(Cdimg*, Voldesc*);
/*
* It's not strictly conforming; instead it's enough to
* get us up and running; presumably the real CD writing
* will take care of being conforming.
*
* Things not conforming include:
* - no path table
* - root directories are of length zero
*/
Cdimg*
createcd(char *file, Cdinfo info)
{
int fd, xfd;
Cdimg *cd;
if(access(file, AEXIST) == 0){
werrstr("file already exists");
return nil;
}
if((fd = create(file, ORDWR, 0666)) < 0)
return nil;
cd = emalloc(sizeof *cd);
cd->file = atom(file);
Binit(&cd->brd, fd, OREAD);
if((xfd = open(file, ORDWR)) < 0)
sysfatal("can't open file again: %r");
Binit(&cd->bwr, xfd, OWRITE);
Crepeat(cd, 0, 16*Blocksize);
Cputisopvd(cd, info);
if(info.flags & CDbootable){
cd->bootimage = info.bootimage;
cd->flags |= CDbootable;
Cputbootvol(cd);
}
if(readisodesc(cd, &cd->iso) < 0)
assert(0);
if(info.flags & CDplan9)
cd->flags |= CDplan9;
else if(info.flags & CDrockridge)
cd->flags |= CDrockridge;
if(info.flags & CDjoliet) {
Cputjolietsvd(cd, info);
if(readjolietdesc(cd, &cd->joliet) < 0)
assert(0);
cd->flags |= CDjoliet;
}
Cputendvd(cd);
if(info.flags & CDdump){
cd->nulldump = Cputdumpblock(cd);
cd->flags |= CDdump;
}
if(cd->flags & CDbootable){
Cputbootcat(cd);
Cupdatebootvol(cd);
}
if(info.flags & CDconform)
cd->flags |= CDconform;
cd->flags |= CDnew;
cd->nextblock = Cwoffset(cd) / Blocksize;
assert(cd->nextblock != 0);
return cd;
}
Cdimg*
opencd(char *file, Cdinfo info)
{
int fd, xfd;
Cdimg *cd;
Dir *d;
if((fd = open(file, ORDWR)) < 0) {
if(access(file, AEXIST) == 0)
return nil;
return createcd(file, info);
}
if((d = dirfstat(fd)) == nil) {
close(fd);
return nil;
}
if(d->length == 0 || d->length % Blocksize) {
werrstr("bad length %lld", d->length);
close(fd);
free(d);
return nil;
}
cd = emalloc(sizeof *cd);
cd->file = atom(file);
cd->nextblock = d->length / Blocksize;
assert(cd->nextblock != 0);
free(d);
Binit(&cd->brd, fd, OREAD);
if((xfd = open(file, ORDWR)) < 0)
sysfatal("can't open file again: %r");
Binit(&cd->bwr, xfd, OWRITE);
if(readisodesc(cd, &cd->iso) < 0) {
free(cd);
close(fd);
close(xfd);
return nil;
}
/* lowercase because of isostring */
if(strstr(cd->iso.systemid, "iso9660") == nil
&& strstr(cd->iso.systemid, "utf8") == nil) {
werrstr("unknown systemid %s", cd->iso.systemid);
free(cd);
close(fd);
close(xfd);
return nil;
}
if(strstr(cd->iso.systemid, "plan 9"))
cd->flags |= CDplan9;
if(strstr(cd->iso.systemid, "iso9660"))
cd->flags |= CDconform;
if(strstr(cd->iso.systemid, "rrip"))
cd->flags |= CDrockridge;
if(strstr(cd->iso.systemid, "boot"))
cd->flags |= CDbootable;
if(readjolietdesc(cd, &cd->joliet) == 0)
cd->flags |= CDjoliet;
if(hasdump(cd))
cd->flags |= CDdump;
return cd;
}
ulong
big(void *a, int n)
{
uchar *p;
ulong v;
int i;
p = a;
v = 0;
for(i=0; i<n; i++)
v = (v<<8) | *p++;
return v;
}
ulong
little(void *a, int n)
{
uchar *p;
ulong v;
int i;
p = a;
v = 0;
for(i=0; i<n; i++)
v |= (*p++<<(i*8));
return v;
}
void
Creadblock(Cdimg *cd, void *buf, ulong block, ulong len)
{
assert(block != 0); /* nothing useful there */
Bflush(&cd->bwr);
if(Bseek(&cd->brd, block*Blocksize, 0) != block*Blocksize)
sysfatal("error seeking to block %lud", block);
if(Bread(&cd->brd, buf, len) != len)
sysfatal("error reading %lud bytes at block %lud: %r %lld", len, block, Bseek(&cd->brd, 0, 2));
}
int
parsedir(Cdimg *cd, Direc *d, uchar *buf, int len, char *(*cvtname)(uchar*, int))
{
enum { NAMELEN = 28 };
char name[NAMELEN];
uchar *p;
Cdir *c;
memset(d, 0, sizeof *d);
c = (Cdir*)buf;
if(c->len > len) {
werrstr("buffer too small");
return -1;
}
if(c->namelen == 1 && c->name[0] == '\0')
d->name = atom(".");
else if(c->namelen == 1 && c->name[0] == '\001')
d->name = atom("..");
else if(cvtname)
d->name = cvtname(c->name, c->namelen);
d->block = little(c->dloc, 4);
d->length = little(c->dlen, 4);
if(c->flags & 2)
d->mode |= DMDIR;
/*BUG: do we really need to parse the plan 9 fields? */
/* plan 9 use fields */
if((cd->flags & CDplan9) && cvtname == isostring
&& (c->namelen != 1 || c->name[0] > 1)) {
p = buf+33+c->namelen;
if((p-buf)&1)
p++;
assert(p < buf+c->len);
assert(*p < NAMELEN);
if(*p != 0) {
memmove(name, p+1, *p);
name[*p] = '\0';
d->confname = d->name;
d->name = atom(name);
}
p += *p+1;
assert(*p < NAMELEN);
memmove(name, p+1, *p);
name[*p] = '\0';
d->uid = atom(name);
p += *p+1;
assert(*p < NAMELEN);
memmove(name, p+1, *p);
name[*p] = '\0';
d->gid = atom(name);
p += *p+1;
if((p-buf)&1)
p++;
d->mode = little(p, 4);
}
// BUG: rock ridge extensions
return 0;
}
void
setroot(Cdimg *cd, ulong block, ulong dloc, ulong dlen)
{
assert(block != 0);
Cwseek(cd, block*Blocksize+offsetof(Cvoldesc, rootdir[0])+offsetof(Cdir, dloc[0]));
Cputn(cd, dloc, 4);
Cputn(cd, dlen, 4);
}
void
setvolsize(Cdimg *cd, ulong block, ulong size)
{
assert(block != 0);
Cwseek(cd, block*Blocksize+offsetof(Cvoldesc, volsize[0]));
Cputn(cd, size, 4);
}
void
setpathtable(Cdimg *cd, ulong block, ulong sz, ulong lloc, ulong bloc)
{
assert(block != 0);
Cwseek(cd, block*Blocksize+offsetof(Cvoldesc, pathsize[0]));
Cputn(cd, sz, 4);
Cputnl(cd, lloc, 4);
Cputnl(cd, 0, 4);
Cputnm(cd, bloc, 4);
Cputnm(cd, 0, 4);
assert(Cwoffset(cd) == block*Blocksize+offsetof(Cvoldesc, rootdir[0]));
}
static void
parsedesc(Voldesc *v, Cvoldesc *cv, char *(*string)(uchar*, int))
{
v->systemid = string(cv->systemid, sizeof cv->systemid);
v->pathsize = little(cv->pathsize, 4);
v->lpathloc = little(cv->lpathloc, 4);
v->mpathloc = little(cv->mpathloc, 4);
v->volumeset = string(cv->volumeset, sizeof cv->volumeset);
v->publisher = string(cv->publisher, sizeof cv->publisher);
v->preparer = string(cv->preparer, sizeof cv->preparer);
v->application = string(cv->application, sizeof cv->application);
v->abstract = string(cv->abstract, sizeof cv->abstract);
v->biblio = string(cv->biblio, sizeof cv->biblio);
v->notice = string(cv->notice, sizeof cv->notice);
}
static int
readisodesc(Cdimg *cd, Voldesc *v)
{
static uchar magic[] = { 0x01, 'C', 'D', '0', '0', '1', 0x01, 0x00 };
Cvoldesc cv;
memset(v, 0, sizeof *v);
Creadblock(cd, &cv, 16, sizeof cv);
if(memcmp(cv.magic, magic, sizeof magic) != 0) {
werrstr("bad pvd magic");
return -1;
}
if(little(cv.blocksize, 2) != Blocksize) {
werrstr("block size not %d", Blocksize);
return -1;
}
cd->iso9660pvd = 16;
parsedesc(v, &cv, isostring);
return parsedir(cd, &v->root, cv.rootdir, sizeof cv.rootdir, isostring);
}
static int
readjolietdesc(Cdimg *cd, Voldesc *v)
{
int i;
static uchar magic[] = { 0x02, 'C', 'D', '0', '0', '1', 0x01, 0x00 };
Cvoldesc cv;
memset(v, 0, sizeof *v);
for(i=16; i<24; i++) {
Creadblock(cd, &cv, i, sizeof cv);
if(memcmp(cv.magic, magic, sizeof magic) != 0)
continue;
if(cv.charset[0] != 0x25 || cv.charset[1] != 0x2F
|| (cv.charset[2] != 0x40 && cv.charset[2] != 0x43 && cv.charset[2] != 0x45))
continue;
break;
}
if(i==24) {
werrstr("could not find Joliet SVD");
return -1;
}
if(little(cv.blocksize, 2) != Blocksize) {
werrstr("block size not %d", Blocksize);
return -1;
}
cd->jolietsvd = i;
parsedesc(v, &cv, jolietstring);
return parsedir(cd, &v->root, cv.rootdir, sizeof cv.rootdir, jolietstring);
}
/*
* CD image buffering routines.
*/
void
Cputc(Cdimg *cd, int c)
{
assert(Boffset(&cd->bwr) >= 16*Blocksize || c == 0);
if(Boffset(&cd->bwr) == 0x9962)
if(c >= 256) abort();
if(Bputc(&cd->bwr, c) < 0)
sysfatal("Bputc: %r");
Bflush(&cd->brd);
}
void
Cputnl(Cdimg *cd, ulong val, int size)
{
switch(size) {
default:
sysfatal("bad size %d in bputnl", size);
case 2:
Cputc(cd, val);
Cputc(cd, val>>8);
break;
case 4:
Cputc(cd, val);
Cputc(cd, val>>8);
Cputc(cd, val>>16);
Cputc(cd, val>>24);
break;
}
}
void
Cputnm(Cdimg *cd, ulong val, int size)
{
switch(size) {
default:
sysfatal("bad size %d in bputnl", size);
case 2:
Cputc(cd, val>>8);
Cputc(cd, val);
break;
case 4:
Cputc(cd, val>>24);
Cputc(cd, val>>16);
Cputc(cd, val>>8);
Cputc(cd, val);
break;
}
}
void
Cputn(Cdimg *cd, long val, int size)
{
Cputnl(cd, val, size);
Cputnm(cd, val, size);
}
/*
* ASCII/UTF string writing
*/
void
Crepeat(Cdimg *cd, int c, int n)
{
while(n-- > 0)
Cputc(cd, c);
}
void
Cputs(Cdimg *cd, char *s, int size)
{
int n;
if(s == nil) {
Crepeat(cd, ' ', size);
return;
}
for(n=0; n<size && *s; n++)
Cputc(cd, *s++);
if(n<size)
Crepeat(cd, ' ', size-n);
}
void
Cwrite(Cdimg *cd, void *buf, int n)
{
assert(Boffset(&cd->bwr) >= 16*Blocksize);
if(Bwrite(&cd->bwr, buf, n) != n)
sysfatal("Bwrite: %r");
Bflush(&cd->brd);
}
void
Cputr(Cdimg *cd, Rune r)
{
Cputc(cd, r>>8);
Cputc(cd, r);
}
void
Crepeatr(Cdimg *cd, Rune r, int n)
{
int i;
for(i=0; i<n; i++)
Cputr(cd, r);
}
void
Cputrs(Cdimg *cd, Rune *s, int osize)
{
int n, size;
size = osize/2;
if(s == nil)
Crepeatr(cd, (Rune)' ', size);
else {
for(n=0; *s && n<size; n++)
Cputr(cd, *s++);
if(n<size)
Crepeatr(cd, ' ', size-n);
}
if(osize&1)
Cputc(cd, 0); /* what else can we do? */
}
void
Cputrscvt(Cdimg *cd, char *s, int size)
{
Rune r[256];
strtorune(r, s);
Cputrs(cd, strtorune(r, s), size);
}
void
Cpadblock(Cdimg *cd)
{
int n;
ulong nb;
n = Blocksize - (Boffset(&cd->bwr) % Blocksize);
if(n != Blocksize)
Crepeat(cd, 0, n);
nb = Boffset(&cd->bwr)/Blocksize;
assert(nb != 0);
if(nb > cd->nextblock)
cd->nextblock = nb;
}
void
Cputdate(Cdimg *cd, ulong ust)
{
Tm *tm;
if(ust == 0) {
Crepeat(cd, 0, 7);
return;
}
tm = gmtime(ust);
Cputc(cd, tm->year);
Cputc(cd, tm->mon+1);
Cputc(cd, tm->mday);
Cputc(cd, tm->hour);
Cputc(cd, tm->min);
Cputc(cd, tm->sec);
Cputc(cd, 0);
}
void
Cputdate1(Cdimg *cd, ulong ust)
{
Tm *tm;
char str[20];
if(ust == 0) {
Crepeat(cd, '0', 16);
Cputc(cd, 0);
return;
}
tm = gmtime(ust);
sprint(str, "%.4d%.2d%.2d%.2d%.2d%.4d",
tm->year+1900,
tm->mon+1,
tm->mday,
tm->hour,
tm->min,
tm->sec*100);
Cputs(cd, str, 16);
Cputc(cd, 0);
}
void
Cwseek(Cdimg *cd, ulong offset)
{
Bseek(&cd->bwr, offset, 0);
}
ulong
Cwoffset(Cdimg *cd)
{
return Boffset(&cd->bwr);
}
void
Cwflush(Cdimg *cd)
{
Bflush(&cd->bwr);
}
ulong
Croffset(Cdimg *cd)
{
return Boffset(&cd->brd);
}
void
Crseek(Cdimg *cd, ulong offset)
{
Bseek(&cd->brd, offset, 0);
}
int
Cgetc(Cdimg *cd)
{
int c;
Cwflush(cd);
if((c = Bgetc(&cd->brd)) == Beof) {
fprint(2, "getc at %lud\n", Croffset(cd));
assert(0);
//sysfatal("Bgetc: %r");
}
return c;
}
void
Cread(Cdimg *cd, void *buf, int n)
{
Cwflush(cd);
if(Bread(&cd->brd, buf, n) != n)
sysfatal("Bread: %r");
}
char*
Crdline(Cdimg *cd, int c)
{
Cwflush(cd);
return Brdline(&cd->brd, c);
}
int
Clinelen(Cdimg *cd)
{
return Blinelen(&cd->brd);
}

141
src/cmd/9660/conform.c Normal file
View file

@ -0,0 +1,141 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include <ctype.h>
#include "iso9660.h"
/*
* We keep an array sorted by bad atom pointer.
* The theory is that since we don't free memory very often,
* the array will be mostly sorted already and insertions will
* usually be near the end, so we won't spend much time
* keeping it sorted.
*/
/*
* Binary search a Tx list.
* If no entry is found, return a pointer to
* where a new such entry would go.
*/
static Tx*
txsearch(char *atom, Tx *t, int n)
{
while(n > 0) {
if(atom < t[n/2].bad)
n = n/2;
else if(atom > t[n/2].bad) {
t += n/2+1;
n -= (n/2+1);
} else
return &t[n/2];
}
return t;
}
void
addtx(char *b, char *g)
{
Tx *t;
Conform *c;
if(map == nil)
map = emalloc(sizeof(*map));
c = map;
if(c->nt%32 == 0)
c->t = erealloc(c->t, (c->nt+32)*sizeof(c->t[0]));
t = txsearch(b, c->t, c->nt);
if(t < c->t+c->nt && t->bad == b) {
fprint(2, "warning: duplicate entry for %s in _conform.map\n", b);
return;
}
if(t != c->t+c->nt)
memmove(t+1, t, (c->t+c->nt - t)*sizeof(Tx));
t->bad = b;
t->good = g;
c->nt++;
}
char*
conform(char *s, int isdir)
{
Tx *t;
char buf[10], *g;
Conform *c;
c = map;
s = atom(s);
if(c){
t = txsearch(s, c->t, c->nt);
if(t < c->t+c->nt && t->bad == s)
return t->good;
}
sprint(buf, "%c%.6d", isdir ? 'D' : 'F', c ? c->nt : 0);
g = atom(buf);
addtx(s, g);
return g;
}
#ifdef NOTUSED
static int
isalldigit(char *s)
{
while(*s)
if(!isdigit(*s++))
return 0;
return 1;
}
#endif
static int
goodcmp(const void *va, const void *vb)
{
Tx *a, *b;
a = (Tx*)va;
b = (Tx*)vb;
return strcmp(a->good, b->good);
}
static int
badatomcmp(const void *va, const void *vb)
{
Tx *a, *b;
a = (Tx*)va;
b = (Tx*)vb;
if(a->good < b->good)
return -1;
if(a->good > b->good)
return 1;
return 0;
}
void
wrconform(Cdimg *cd, int n, ulong *pblock, ulong *plength)
{
char buf[1024];
int i;
Conform *c;
c = map;
*pblock = cd->nextblock;
if(c==nil || n==c->nt){
*plength = 0;
return;
}
Cwseek(cd, cd->nextblock*Blocksize);
qsort(c->t, c->nt, sizeof(c->t[0]), goodcmp);
for(i=n; i<c->nt; i++) {
snprint(buf, sizeof buf, "%s %s\n", c->t[i].good, c->t[i].bad);
Cwrite(cd, buf, strlen(buf));
}
qsort(c->t, c->nt, sizeof(c->t[0]), badatomcmp);
*plength = Cwoffset(cd) - *pblock*Blocksize;
chat("write _conform.map at %lud+%lud\n", *pblock, *plength);
Cpadblock(cd);
}

222
src/cmd/9660/direc.c Normal file
View file

@ -0,0 +1,222 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include "iso9660.h"
void
mkdirec(Direc *direc, XDir *d)
{
memset(direc, 0, sizeof(Direc));
direc->name = atom(d->name);
direc->uid = atom(d->uid);
direc->gid = atom(d->gid);
direc->uidno = d->uidno;
direc->gidno = d->gidno;
direc->mode = d->mode;
direc->length = d->length;
direc->mtime = d->mtime;
direc->atime = d->atime;
direc->ctime = d->ctime;
direc->symlink = d->symlink;
}
static int
strecmp(char *a, char *ea, char *b)
{
int r;
if((r = strncmp(a, b, ea-a)) != 0)
return r;
if(b[ea-a] == '\0')
return 0;
return 1;
}
/*
* Binary search a list of directories for the
* entry with name name.
* If no entry is found, return a pointer to
* where a new such entry would go.
*/
static Direc*
dbsearch(char *name, int nname, Direc *d, int n)
{
int i;
while(n > 0) {
i = strecmp(name, name+nname, d[n/2].name);
if(i < 0)
n = n/2;
else if(i > 0) {
d += n/2+1;
n -= (n/2+1);
} else
return &d[n/2];
}
return d;
}
/*
* Walk to name, starting at d.
*/
Direc*
walkdirec(Direc *d, char *name)
{
char *p, *nextp, *slashp;
Direc *nd;
for(p=name; p && *p; p=nextp) {
if((slashp = strchr(p, '/')) != nil)
nextp = slashp+1;
else
nextp = slashp = p+strlen(p);
nd = dbsearch(p, slashp-p, d->child, d->nchild);
if(nd >= d->child+d->nchild || strecmp(p, slashp, nd->name) != 0)
return nil;
d = nd;
}
return d;
}
/*
* Add the file ``name'' with attributes d to the
* directory ``root''. Name may contain multiple
* elements; all but the last must exist already.
*
* The child lists are kept sorted by utfname.
*/
Direc*
adddirec(Direc *root, char *name, XDir *d)
{
char *p;
Direc *nd;
int off;
if(name[0] == '/')
name++;
if((p = strrchr(name, '/')) != nil) {
*p = '\0';
root = walkdirec(root, name);
if(root == nil) {
sysfatal("error in proto file: no entry for /%s but /%s/%s\n", name, name, p+1);
return nil;
}
*p = '/';
p++;
} else
p = name;
nd = dbsearch(p, strlen(p), root->child, root->nchild);
off = nd - root->child;
if(off < root->nchild && strcmp(nd->name, p) == 0) {
if ((d->mode & DMDIR) == 0)
fprint(2, "warning: proto lists %s twice\n", name);
return nil;
}
if(root->nchild%Ndirblock == 0) {
root->child = erealloc(root->child, (root->nchild+Ndirblock)*sizeof(Direc));
nd = root->child + off;
}
memmove(nd+1, nd, (root->nchild - off)*sizeof(Direc));
mkdirec(nd, d);
nd->name = atom(p);
root->nchild++;
return nd;
}
/*
* Copy the tree src into dst.
*/
void
copydirec(Direc *dst, Direc *src)
{
int i, n;
*dst = *src;
if((src->mode & DMDIR) == 0)
return;
n = (src->nchild + Ndirblock - 1);
n -= n%Ndirblock;
dst->child = emalloc(n*sizeof(Direc));
n = dst->nchild;
for(i=0; i<n; i++)
copydirec(&dst->child[i], &src->child[i]);
}
/*
* Turn the Dbadname flag on for any entries
* that have non-conforming names.
*/
static void
_checknames(Direc *d, int (*isbadname)(char*), int isroot)
{
int i;
if(!isroot && isbadname(d->name))
d->flags |= Dbadname;
if(strcmp(d->name, "_conform.map") == 0)
d->flags |= Dbadname;
for(i=0; i<d->nchild; i++)
_checknames(&d->child[i], isbadname, 0);
}
void
checknames(Direc *d, int (*isbadname)(char*))
{
_checknames(d, isbadname, 1);
}
/*
* Set the names to conform to 8.3
* by changing them to numbers.
* Plan 9 gets the right names from its
* own directory entry.
*
* We used to write a _conform.map file to translate
* names. Joliet should take care of most of the
* interoperability with other systems now.
*/
void
convertnames(Direc *d, char* (*cvt)(char*, char*))
{
int i;
char new[1024];
if(d->flags & Dbadname)
cvt(new, conform(d->name, d->mode & DMDIR));
else
cvt(new, d->name);
d->confname = atom(new);
for(i=0; i<d->nchild; i++)
convertnames(&d->child[i], cvt);
}
/*
* Sort a directory with a given comparison function.
* After this is called on a tree, adddirec should not be,
* since the entries may no longer be sorted as adddirec expects.
*/
void
dsort(Direc *d, int (*cmp)(const void*, const void*))
{
int i, n;
n = d->nchild;
qsort(d->child, n, sizeof(d[0]), cmp);
for(i=0; i<n; i++)
dsort(&d->child[i], cmp);
}

511
src/cmd/9660/dump.c Normal file
View file

@ -0,0 +1,511 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include <ctype.h>
#include "iso9660.h"
static void
md5cd(Cdimg *cd, ulong block, ulong length, uchar *digest)
{
int n;
uchar buf[Blocksize];
DigestState *s;
s = md5(nil, 0, nil, nil);
while(length > 0) {
n = length;
if(n > Blocksize)
n = Blocksize;
Creadblock(cd, buf, block, n);
md5(buf, n, nil, s);
block++;
length -= n;
}
md5(nil, 0, digest, s);
}
static Dumpdir*
mkdumpdir(char *name, uchar *md5, ulong block, ulong length)
{
Dumpdir *d;
assert(block != 0);
d = emalloc(sizeof *d);
d->name = name;
memmove(d->md5, md5, sizeof d->md5);
d->block = block;
d->length = length;
return d;
}
static Dumpdir**
ltreewalkmd5(Dumpdir **l, uchar *md5)
{
int i;
while(*l) {
i = memcmp(md5, (*l)->md5, MD5dlen);
if(i < 0)
l = &(*l)->md5left;
else if(i == 0)
return l;
else
l = &(*l)->md5right;
}
return l;
}
static Dumpdir**
ltreewalkblock(Dumpdir **l, ulong block)
{
while(*l) {
if(block < (*l)->block)
l = &(*l)->blockleft;
else if(block == (*l)->block)
return l;
else
l = &(*l)->blockright;
}
return l;
}
/*
* Add a particular file to our binary tree.
*/
static void
addfile(Cdimg *cd, Dump *d, char *name, Direc *dir)
{
uchar md5[MD5dlen];
Dumpdir **lblock;
assert((dir->mode & DMDIR) == 0);
if(dir->length == 0)
return;
lblock = ltreewalkblock(&d->blockroot, dir->block);
if(*lblock != nil) {
if((*lblock)->length == dir->length)
return;
fprint(2, "block %lud length %lud %s %lud %s\n", dir->block, (*lblock)->length, (*lblock)->name,
dir->length, dir->name);
assert(0);
}
md5cd(cd, dir->block, dir->length, md5);
if(chatty > 1)
fprint(2, "note file %.16H %lud (%s)\n", md5, dir->length, dir->name);
insertmd5(d, name, md5, dir->block, dir->length);
}
void
insertmd5(Dump *d, char *name, uchar *md5, ulong block, ulong length)
{
Dumpdir **lmd5;
Dumpdir **lblock;
lblock = ltreewalkblock(&d->blockroot, block);
if(*lblock != nil) {
if((*lblock)->length == length)
return;
fprint(2, "block %lud length %lud %lud\n", block, (*lblock)->length, length);
assert(0);
}
assert(length != 0);
*lblock = mkdumpdir(name, md5, block, length);
lmd5 = ltreewalkmd5(&d->md5root, md5);
if(*lmd5 != nil)
fprint(2, "warning: data duplicated on CD\n");
else
*lmd5 = *lblock;
}
/*
* Fill all the children entries for a particular directory;
* all we care about is block, length, and whether it is a directory.
*/
void
readkids(Cdimg *cd, Direc *dir, char *(*cvt)(uchar*, int))
{
char *dot, *dotdot;
int m, n;
uchar buf[Blocksize], *ebuf, *p;
ulong b, nb;
Cdir *c;
Direc dx;
assert(dir->mode & DMDIR);
dot = atom(".");
dotdot = atom("..");
ebuf = buf+Blocksize;
nb = (dir->length+Blocksize-1) / Blocksize;
n = 0;
for(b=0; b<nb; b++) {
Creadblock(cd, buf, dir->block + b, Blocksize);
p = buf;
while(p < ebuf) {
c = (Cdir*)p;
if(c->len == 0)
break;
if(p+c->len > ebuf)
break;
if(parsedir(cd, &dx, p, ebuf-p, cvt) == 0 && dx.name != dot && dx.name != dotdot)
n++;
p += c->len;
}
}
m = (n+Ndirblock-1)/Ndirblock * Ndirblock;
dir->child = emalloc(m*sizeof dir->child[0]);
dir->nchild = n;
n = 0;
for(b=0; b<nb; b++) {
assert(n <= dir->nchild);
Creadblock(cd, buf, dir->block + b, Blocksize);
p = buf;
while(p < ebuf) {
c = (Cdir*)p;
if(c->len == 0)
break;
if(p+c->len > ebuf)
break;
if(parsedir(cd, &dx, p, ebuf-p, cvt) == 0 && dx.name != dot && dx.name != dotdot) {
assert(n < dir->nchild);
dir->child[n++] = dx;
}
p += c->len;
}
}
}
/*
* Free the children. Make sure their children are free too.
*/
void
freekids(Direc *dir)
{
int i;
for(i=0; i<dir->nchild; i++)
assert(dir->child[i].nchild == 0);
free(dir->child);
dir->child = nil;
dir->nchild = 0;
}
/*
* Add a whole directory and all its children to our binary tree.
*/
static void
adddir(Cdimg *cd, Dump *d, Direc *dir)
{
int i;
readkids(cd, dir, isostring);
for(i=0; i<dir->nchild; i++) {
if(dir->child[i].mode & DMDIR)
adddir(cd, d, &dir->child[i]);
else
addfile(cd, d, atom(dir->name), &dir->child[i]);
}
freekids(dir);
}
Dumpdir*
lookupmd5(Dump *d, uchar *md5)
{
return *ltreewalkmd5(&d->md5root, md5);
}
void
adddirx(Cdimg *cd, Dump *d, Direc *dir, int lev)
{
int i;
Direc dd;
if(lev == 2){
dd = *dir;
adddir(cd, d, &dd);
return;
}
for(i=0; i<dir->nchild; i++)
adddirx(cd, d, &dir->child[i], lev+1);
}
Dump*
dumpcd(Cdimg *cd, Direc *dir)
{
Dump *d;
d = emalloc(sizeof *d);
d->cd = cd;
adddirx(cd, d, dir, 0);
return d;
}
/*
static ulong
minblock(Direc *root, int lev)
{
int i;
ulong m, n;
m = root->block;
for(i=0; i<root->nchild; i++) {
n = minblock(&root->child[i], lev-1);
if(m > n)
m = n;
}
return m;
}
*/
void
copybutname(Direc *d, Direc *s)
{
Direc x;
x = *d;
*d = *s;
d->name = x.name;
d->confname = x.confname;
}
Direc*
createdumpdir(Direc *root, XDir *dir, char *utfname)
{
char *p;
Direc *d;
if(utfname[0]=='/')
sysfatal("bad dump name '%s'", utfname);
p = strchr(utfname, '/');
if(p == nil || strchr(p+1, '/'))
sysfatal("bad dump name '%s'", utfname);
*p++ = '\0';
if((d = walkdirec(root, utfname)) == nil)
d = adddirec(root, utfname, dir);
if(walkdirec(d, p))
sysfatal("duplicate dump name '%s/%s'", utfname, p);
d = adddirec(d, p, dir);
return d;
}
static void
rmdirec(Direc *d, Direc *kid)
{
Direc *ekid;
ekid = d->child+d->nchild;
assert(d->child <= kid && kid < ekid);
if(ekid != kid+1)
memmove(kid, kid+1, (ekid-(kid+1))*sizeof(*kid));
d->nchild--;
}
void
rmdumpdir(Direc *root, char *utfname)
{
char *p;
Direc *d, *dd;
if(utfname[0]=='/')
sysfatal("bad dump name '%s'", utfname);
p = strchr(utfname, '/');
if(p == nil || strchr(p+1, '/'))
sysfatal("bad dump name '%s'", utfname);
*p++ = '\0';
if((d = walkdirec(root, utfname)) == nil)
sysfatal("cannot remove %s/%s: %s does not exist", utfname, p, utfname);
p[-1] = '/';
if((dd = walkdirec(d, p)) == nil)
sysfatal("cannot remove %s: does not exist", utfname);
rmdirec(d, dd);
if(d->nchild == 0)
rmdirec(root, d);
}
char*
adddumpdir(Direc *root, ulong now, XDir *dir)
{
char buf[40], *p;
int n;
Direc *dday, *dyear;
Tm tm;
tm = *localtime(now);
sprint(buf, "%d", tm.year+1900);
if((dyear = walkdirec(root, buf)) == nil) {
dyear = adddirec(root, buf, dir);
assert(dyear != nil);
}
n = 0;
sprint(buf, "%.2d%.2d", tm.mon+1, tm.mday);
p = buf+strlen(buf);
while(walkdirec(dyear, buf))
sprint(p, "%d", ++n);
dday = adddirec(dyear, buf, dir);
assert(dday != nil);
sprint(buf, "%s/%s", dyear->name, dday->name);
assert(walkdirec(root, buf)==dday);
return atom(buf);
}
/*
* The dump directory tree is inferred from a linked list of special blocks.
* One block is written at the end of each dump.
* The blocks have the form
*
* plan 9 dump cd
* <dump-name> <dump-time> <next-block> <conform-block> <conform-length> \
* <iroot-block> <iroot-length> <jroot-block> <jroot-length>
*
* If only the first line is present, this is the end of the chain.
*/
static char magic[] = "plan 9 dump cd\n";
ulong
Cputdumpblock(Cdimg *cd)
{
ulong x;
Cwseek(cd, cd->nextblock*Blocksize);
x = Cwoffset(cd);
Cwrite(cd, magic, sizeof(magic)-1);
Cpadblock(cd);
return x/Blocksize;
}
int
hasdump(Cdimg *cd)
{
int i;
char buf[128];
for(i=16; i<24; i++) {
Creadblock(cd, buf, i, sizeof buf);
if(memcmp(buf, magic, sizeof(magic)-1) == 0)
return i;
}
return 0;
}
Direc
readdumpdirs(Cdimg *cd, XDir *dir, char *(*cvt)(uchar*, int))
{
char buf[Blocksize];
char *p, *q, *f[16];
int i, nf;
ulong db, t;
Direc *nr, root;
XDir xd;
mkdirec(&root, dir);
db = hasdump(cd);
xd = *dir;
for(;;){
if(db == 0)
sysfatal("error in dump blocks");
Creadblock(cd, buf, db, sizeof buf);
if(memcmp(buf, magic, sizeof(magic)-1) != 0)
break;
p = buf+sizeof(magic)-1;
if(p[0] == '\0')
break;
if((q = strchr(p, '\n')) != nil)
*q = '\0';
nf = tokenize(p, f, nelem(f));
i = 5;
if(nf < i || (cvt==jolietstring && nf < i+2))
sysfatal("error in dump block %lud: nf=%d; p='%s'", db, nf, p);
nr = createdumpdir(&root, &xd, f[0]);
t = strtoul(f[1], 0, 0);
xd.mtime = xd.ctime = xd.atime = t;
db = strtoul(f[2], 0, 0);
if(cvt == jolietstring)
i += 2;
nr->block = strtoul(f[i], 0, 0);
nr->length = strtoul(f[i+1], 0, 0);
}
cd->nulldump = db;
return root;
}
extern void addtx(char*, char*);
static int
isalldigit(char *s)
{
while(*s)
if(!isdigit(*s++))
return 0;
return 1;
}
void
readdumpconform(Cdimg *cd)
{
char buf[Blocksize];
char *p, *q, *f[10];
ulong cb, nc, m, db;
int nf;
db = hasdump(cd);
assert(map==nil || map->nt == 0);
for(;;){
if(db == 0)
sysfatal("error0 in dump blocks");
Creadblock(cd, buf, db, sizeof buf);
if(memcmp(buf, magic, sizeof(magic)-1) != 0)
break;
p = buf+sizeof(magic)-1;
if(p[0] == '\0')
break;
if((q = strchr(p, '\n')) != nil)
*q = '\0';
nf = tokenize(p, f, nelem(f));
if(nf < 5)
sysfatal("error0 in dump block %lud", db);
db = strtoul(f[2], 0, 0);
cb = strtoul(f[3], 0, 0);
nc = strtoul(f[4], 0, 0);
Crseek(cd, cb*Blocksize);
m = cb*Blocksize+nc;
while(Croffset(cd) < m && (p = Crdline(cd, '\n')) != nil){
p[Clinelen(cd)-1] = '\0';
if(tokenize(p, f, 2) != 2 || (f[0][0] != 'D' && f[0][0] != 'F')
|| strlen(f[0]) != 7 || !isalldigit(f[0]+1))
break;
addtx(atom(f[1]), atom(f[0]));
}
}
if(map)
cd->nconform = map->nt;
else
cd->nconform = 0;
}

402
src/cmd/9660/dump9660.c Normal file
View file

@ -0,0 +1,402 @@
#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: disk/mk9660 [-D:] [-9cjr] [-b bootfile] [-p proto] [-s src] cdimage\n");
else
fprint(2, "usage: disk/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 = "/sys/lib/sysconfig/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, nil, &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, (void *) 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, (void *) 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) ? (void *) struprcpy : (void *) strcpy);
if(cd->nulldump) {
r = walkdirec(&idumproot, dumpname);
assert(r != nil);
copybutname(r, &iroot);
}
if(cd->flags & CDjoliet) {
convertnames(&jdumproot, (void *) 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);
}

206
src/cmd/9660/ichar.c Normal file
View file

@ -0,0 +1,206 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include <ctype.h>
#include "iso9660.h"
/*
* ISO 9660 file names must be uppercase, digits, or underscore.
* We use lowercase, digits, and underscore, translating lower to upper
* in mkisostring, and upper to lower in isostring.
* Files with uppercase letters in their names are thus nonconforming.
* Conforming files also must have a basename
* at most 8 letters and at most one suffix of at most 3 letters.
*/
char*
isostring(uchar *buf, int len)
{
char *p, *q;
p = emalloc(len+1);
memmove(p, buf, len);
p[len] = '\0';
while(len > 0 && p[len-1] == ' ')
p[--len] = '\0';
for(q=p; *q; q++)
*q = tolower(*q);
q = atom(p);
free(p);
return q;
}
int
isisofrog(char c)
{
if(c >= '0' && c <= '9')
return 0;
if(c >= 'a' && c <= 'z')
return 0;
if(c == '_')
return 0;
return 1;
}
int
isbadiso9660(char *s)
{
char *p, *q;
int i;
if((p = strchr(s, '.')) != nil) {
if(p-s > 8)
return 1;
for(q=s; q<p; q++)
if(isisofrog(*q))
return 1;
if(strlen(p+1) > 3)
return 1;
for(q=p+1; *q; q++)
if(isisofrog(*q))
return 1;
} else {
if(strlen(s) > 8)
return 1;
for(q=s; *q; q++)
if(isisofrog(*q))
return 1;
/*
* we rename files of the form [FD]dddddd
* so they don't interfere with us.
*/
if(strlen(s) == 7 && (s[0] == 'D' || s[0] == 'F')) {
for(i=1; i<7; i++)
if(s[i] < '0' || s[i] > '9')
break;
if(i == 7)
return 1;
}
}
return 0;
}
/*
* ISO9660 name comparison
*
* The standard algorithm is as follows:
* Take the filenames without extensions, pad the shorter with 0x20s (spaces),
* and do strcmp. If they are equal, go on.
* Take the extensions, pad the shorter with 0x20s (spaces),
* and do strcmp. If they are equal, go on.
* Compare the version numbers.
*
* Since Plan 9 names are not allowed to contain characters 0x00-0x1F,
* the padded comparisons are equivalent to using strcmp directly.
* We still need to handle the base and extension differently,
* so that .foo sorts before !foo.foo.
*/
int
isocmp(const void *va, const void *vb)
{
int i;
char s1[32], s2[32], *b1, *b2, *e1, *e2;
const Direc *a, *b;
a = va;
b = vb;
strecpy(s1, s1+sizeof s1, a->confname);
b1 = s1;
strecpy(s2, s2+sizeof s2, b->confname);
b2 = s2;
if((e1 = strchr(b1, '.')) != nil)
*e1++ = '\0';
else
e1 = "";
if((e2 = strchr(b2, '.')) != nil)
*e2++ = '\0';
else
e2 = "";
if((i = strcmp(b1, b2)) != 0)
return i;
return strcmp(e1, e2);
}
static char*
mkisostring(char *isobuf, int n, char *s)
{
char *p, *q, *eq;
eq = isobuf+n;
for(p=s, q=isobuf; *p && q < eq; p++)
if('a' <= *p && *p <= 'z')
*q++ = *p+'A'-'a';
else
*q++ = *p;
while(q < eq)
*q++ = ' ';
return isobuf;
}
void
Cputisopvd(Cdimg *cd, Cdinfo info)
{
char buf[130];
Cputc(cd, 1); /* primary volume descriptor */
Cputs(cd, "CD001", 5); /* standard identifier */
Cputc(cd, 1); /* volume descriptor version */
Cputc(cd, 0); /* unused */
assert(~info.flags & (CDplan9|CDrockridge));
/* system identifier */
strcpy(buf, "");
if(info.flags & CDplan9)
strcat(buf, "plan 9 ");
if(info.flags & CDrockridge)
strcat(buf, "rrip ");
if(info.flags & CDbootable)
strcat(buf, "boot ");
if(info.flags & CDconform)
strcat(buf, "iso9660");
else
strcat(buf, "utf8");
struprcpy(buf, buf);
Cputs(cd, buf, 32);
Cputs(cd, mkisostring(buf, 32, info.volumename), 32); /* volume identifier */
Crepeat(cd, 0, 8); /* unused */
Cputn(cd, 0, 4); /* volume space size */
Crepeat(cd, 0, 32); /* unused */
Cputn(cd, 1, 2); /* volume set size */
Cputn(cd, 1, 2); /* volume sequence number */
Cputn(cd, Blocksize, 2); /* logical block size */
Cputn(cd, 0, 4); /* path table size */
Cputnl(cd, 0, 4); /* location of Lpath */
Cputnl(cd, 0, 4); /* location of optional Lpath */
Cputnm(cd, 0, 4); /* location of Mpath */
Cputnm(cd, 0, 4); /* location of optional Mpath */
Cputisodir(cd, nil, DTroot, 1, Cwoffset(cd)); /* root directory */
Cputs(cd, mkisostring(buf, 128, info.volumeset), 128); /* volume set identifier */
Cputs(cd, mkisostring(buf, 128, info.publisher), 128); /* publisher identifier */
Cputs(cd, mkisostring(buf, 128, info.preparer), 128); /* data preparer identifier */
Cputs(cd, mkisostring(buf, 128, info.application), 128); /* application identifier */
Cputs(cd, "", 37); /* copyright notice */
Cputs(cd, "", 37); /* abstract */
Cputs(cd, "", 37); /* bibliographic file */
Cputdate1(cd, now); /* volume creation date */
Cputdate1(cd, now); /* volume modification date */
Cputdate1(cd, 0); /* volume expiration date */
Cputdate1(cd, 0); /* volume effective date */
Cputc(cd, 1); /* file structure version */
Cpadblock(cd);
}

424
src/cmd/9660/iso9660.h Normal file
View file

@ -0,0 +1,424 @@
/*
* iso9660.h
*
* Routines and data structures to support reading and writing
* ISO 9660 CD images. See the ISO 9660 or ECMA 119 standards.
*
* Also supports Rock Ridge extensions for long file names and Unix stuff.
* Also supports Microsoft's Joliet extensions for Unicode and long file names.
* Also supports El Torito bootable CD spec.
*/
typedef struct Cdimg Cdimg;
typedef struct Cdinfo Cdinfo;
typedef struct Conform Conform;
typedef struct Direc Direc;
typedef struct Dumproot Dumproot;
typedef struct Voldesc Voldesc;
typedef struct XDir XDir;
#ifndef CHLINK
#define CHLINK 0
#endif
struct XDir {
char *name;
char *uid;
char *gid;
char *symlink;
ulong uidno; /* Numeric uid */
ulong gidno; /* Numeric gid */
ulong mode;
ulong atime;
ulong mtime;
ulong ctime;
vlong length;
};
/*
* A directory entry in a ISO9660 tree.
* The extra data (uid, etc.) here is put into the system use areas.
*/
struct Direc {
char *name; /* real name */
char *confname; /* conformant name */
char *srcfile; /* file to copy onto the image */
ulong block;
ulong length;
int flags;
char *uid;
char *gid;
char *symlink;
ulong mode;
long atime;
long ctime;
long mtime;
ulong uidno;
ulong gidno;
Direc *child;
int nchild;
};
enum { /* Direc flags */
Dbadname = 1<<0, /* Non-conformant name */
};
/*
* Data found in a volume descriptor.
*/
struct Voldesc {
char *systemid;
char *volumeset;
char *publisher;
char *preparer;
char *application;
/* file names for various parameters */
char *abstract;
char *biblio;
char *notice;
/* path table */
ulong pathsize;
ulong lpathloc;
ulong mpathloc;
/* root of file tree */
Direc root;
};
/*
* An ISO9660 CD image. Various parameters are kept in memory but the
* real image file is opened for reading and writing on fd.
*
* The bio buffers brd and bwr moderate reading and writing to the image.
* The routines we use are careful to flush one before or after using the other,
* as necessary.
*/
struct Cdimg {
char *file;
int fd;
ulong dumpblock;
ulong nextblock;
ulong iso9660pvd;
ulong jolietsvd;
ulong pathblock;
ulong rrcontin; /* rock ridge continuation offset */
ulong nulldump; /* next dump block */
ulong nconform; /* number of conform entries written already */
ulong bootcatptr;
ulong bootcatblock;
ulong bootimageptr;
Direc *bootdirec;
char *bootimage;
Biobuf brd;
Biobuf bwr;
int flags;
Voldesc iso;
Voldesc joliet;
};
enum { /* Cdimg->flags, Cdinfo->flags */
CDjoliet = 1<<0,
CDplan9 = 1<<1,
CDconform = 1<<2,
CDrockridge = 1<<3,
CDnew = 1<<4,
CDdump = 1<<5,
CDbootable = 1<<6,
};
typedef struct Tx Tx;
struct Tx {
char *bad; /* atoms */
char *good;
};
struct Conform {
Tx *t;
int nt; /* delta = 32 */
};
struct Cdinfo {
int flags;
char *volumename;
char *volumeset;
char *publisher;
char *preparer;
char *application;
char *bootimage;
};
enum {
Blocklen = 2048,
};
/*
* This is a doubly binary tree.
* We have a tree keyed on the MD5 values
* as well as a tree keyed on the block numbers.
*/
typedef struct Dump Dump;
typedef struct Dumpdir Dumpdir;
struct Dump {
Cdimg *cd;
Dumpdir *md5root;
Dumpdir *blockroot;
};
struct Dumpdir {
char *name;
uchar md5[MD5dlen];
ulong block;
ulong length;
Dumpdir *md5left;
Dumpdir *md5right;
Dumpdir *blockleft;
Dumpdir *blockright;
};
struct Dumproot {
char *name;
int nkid;
Dumproot *kid;
Direc root;
Direc jroot;
};
/*
* ISO9660 on-CD structures.
*/
typedef struct Cdir Cdir;
typedef struct Cpath Cpath;
typedef struct Cvoldesc Cvoldesc;
/* a volume descriptor block */
struct Cvoldesc {
uchar magic[8]; /* 0x01, "CD001", 0x01, 0x00 */
uchar systemid[32]; /* system identifier */
uchar volumeid[32]; /* volume identifier */
uchar unused[8]; /* character set in secondary desc */
uchar volsize[8]; /* volume size */
uchar charset[32];
uchar volsetsize[4]; /* volume set size = 1 */
uchar volseqnum[4]; /* volume sequence number = 1 */
uchar blocksize[4]; /* logical block size */
uchar pathsize[8]; /* path table size */
uchar lpathloc[4]; /* Lpath */
uchar olpathloc[4]; /* optional Lpath */
uchar mpathloc[4]; /* Mpath */
uchar ompathloc[4]; /* optional Mpath */
uchar rootdir[34]; /* directory entry for root */
uchar volumeset[128]; /* volume set identifier */
uchar publisher[128];
uchar preparer[128]; /* data preparer identifier */
uchar application[128]; /* application identifier */
uchar notice[37]; /* copyright notice file */
uchar abstract[37]; /* abstract file */
uchar biblio[37]; /* bibliographic file */
uchar cdate[17]; /* creation date */
uchar mdate[17]; /* modification date */
uchar xdate[17]; /* expiration date */
uchar edate[17]; /* effective date */
uchar fsvers; /* file system version = 1 */
};
/* a directory entry */
struct Cdir {
uchar len;
uchar xlen;
uchar dloc[8];
uchar dlen[8];
uchar date[7];
uchar flags;
uchar unitsize;
uchar gapsize;
uchar volseqnum[4];
uchar namelen;
uchar name[1]; /* chumminess */
};
/* a path table entry */
struct Cpath {
uchar namelen;
uchar xlen;
uchar dloc[4];
uchar parent[2];
uchar name[1]; /* chumminess */
};
enum { /* Rockridge flags */
RR_PX = 1<<0,
RR_PN = 1<<1,
RR_SL = 1<<2,
RR_NM = 1<<3,
RR_CL = 1<<4,
RR_PL = 1<<5,
RR_RE = 1<<6,
RR_TF = 1<<7,
};
enum { /* CputrripTF type argument */
TFcreation = 1<<0,
TFmodify = 1<<1,
TFaccess = 1<<2,
TFattributes = 1<<3,
TFbackup = 1<<4,
TFexpiration = 1<<5,
TFeffective = 1<<6,
TFlongform = 1<<7,
};
enum { /* CputrripNM flag types */
NMcontinue = 1<<0,
NMcurrent = 1<<1,
NMparent = 1<<2,
NMroot = 1<<3,
NMvolroot = 1<<4,
NMhost = 1<<5,
};
/* boot.c */
void Cputbootvol(Cdimg*);
void Cputbootcat(Cdimg*);
void Cupdatebootvol(Cdimg*);
void Cupdatebootcat(Cdimg*);
void findbootimage(Cdimg*, Direc*);
/* cdrdwr.c */
Cdimg *createcd(char*, Cdinfo);
Cdimg *opencd(char*, Cdinfo);
void Creadblock(Cdimg*, void*, ulong, ulong);
ulong big(void*, int);
ulong little(void*, int);
int parsedir(Cdimg*, Direc*, uchar*, int, char *(*)(uchar*, int));
void setroot(Cdimg*, ulong, ulong, ulong);
void setvolsize(Cdimg*, ulong, ulong);
void setpathtable(Cdimg*, ulong, ulong, ulong, ulong);
void Cputc(Cdimg*, int);
void Cputnl(Cdimg*, ulong, int);
void Cputnm(Cdimg*, ulong, int);
void Cputn(Cdimg*, long, int);
void Crepeat(Cdimg*, int, int);
void Cputs(Cdimg*, char*, int);
void Cwrite(Cdimg*, void*, int);
void Cputr(Cdimg*, Rune);
void Crepeatr(Cdimg*, Rune, int);
void Cputrs(Cdimg*, Rune*, int);
void Cputrscvt(Cdimg*, char*, int);
void Cpadblock(Cdimg*);
void Cputdate(Cdimg*, ulong);
void Cputdate1(Cdimg*, ulong);
void Cread(Cdimg*, void*, int);
void Cwflush(Cdimg*);
void Cwseek(Cdimg*, ulong);
ulong Cwoffset(Cdimg*);
ulong Croffset(Cdimg*);
int Cgetc(Cdimg*);
void Crseek(Cdimg*, ulong);
char *Crdline(Cdimg*, int);
int Clinelen(Cdimg*);
/* conform.c */
void rdconform(Cdimg*);
char *conform(char*, int);
void wrconform(Cdimg*, int, ulong*, ulong*);
/* direc.c */
void mkdirec(Direc*, XDir*);
Direc *walkdirec(Direc*, char*);
Direc *adddirec(Direc*, char*, XDir*);
void copydirec(Direc*, Direc*);
void checknames(Direc*, int (*)(char*));
void convertnames(Direc*, char* (*)(char*, char*));
void dsort(Direc*, int (*)(const void*, const void*));
void setparents(Direc*);
/* dump.c */
ulong Cputdumpblock(Cdimg*);
int hasdump(Cdimg*);
Dump *dumpcd(Cdimg*, Direc*);
Dumpdir *lookupmd5(Dump*, uchar*);
void insertmd5(Dump*, char*, uchar*, ulong, ulong);
Direc readdumpdirs(Cdimg*, XDir*, char*(*)(uchar*,int));
char *adddumpdir(Direc*, ulong, XDir*);
void copybutname(Direc*, Direc*);
void readkids(Cdimg*, Direc*, char*(*)(uchar*,int));
void freekids(Direc*);
void readdumpconform(Cdimg*);
void rmdumpdir(Direc*, char*);
/* ichar.c */
char *isostring(uchar*, int);
int isbadiso9660(char*);
int isocmp(const void*, const void*);
int isisofrog(char);
void Cputisopvd(Cdimg*, Cdinfo);
/* jchar.c */
char *jolietstring(uchar*, int);
int isbadjoliet(char*);
int jolietcmp(const void*, const void*);
int isjolietfrog(Rune);
void Cputjolietsvd(Cdimg*, Cdinfo);
/* path.c */
void writepathtables(Cdimg*);
/* util.c */
void *emalloc(ulong);
void *erealloc(void*, ulong);
char *atom(char*);
char *struprcpy(char*, char*);
int chat(char*, ...);
/* unix.c, plan9.c */
void dirtoxdir(XDir*, Dir*);
void fdtruncate(int, ulong);
long uidno(char*);
long gidno(char*);
/* rune.c */
Rune *strtorune(Rune*, char*);
Rune *runechr(Rune*, Rune);
int runecmp(Rune*, Rune*);
/* sysuse.c */
int Cputsysuse(Cdimg*, Direc*, int, int, int);
/* write.c */
void writefiles(Dump*, Cdimg*, Direc*);
void writedirs(Cdimg*, Direc*, int(*)(Cdimg*, Direc*, int, int, int));
void writedumpdirs(Cdimg*, Direc*, int(*)(Cdimg*, Direc*, int, int, int));
int Cputisodir(Cdimg*, Direc*, int, int, int);
int Cputjolietdir(Cdimg*, Direc*, int, int, int);
void Cputendvd(Cdimg*);
enum {
Blocksize = 2048,
Ndirblock = 16, /* directory blocks allocated at once */
DTdot = 0,
DTdotdot,
DTiden,
DTroot,
DTrootdot,
};
extern ulong now;
extern Conform *map;
extern int chatty;
extern int docolon;
extern int mk9660;

138
src/cmd/9660/jchar.c Normal file
View file

@ -0,0 +1,138 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include "iso9660.h"
char*
jolietstring(uchar *buf, int len)
{
char *p, *q;
int i;
Rune *rp;
rp = emalloc(sizeof(Rune)*(len/2+1));
p = emalloc(UTFmax*(len/2+1));
for(i=0; i<len/2; i++)
rp[i] = (buf[2*i]<<8) | buf[2*i+1];
rp[i] = (Rune)'\0';
snprint(p, UTFmax*(len/2+1), "%S", rp);
q = atom(p);
free(p);
return q;
}
/*
* Joliet name validity check
*
* Joliet names have length at most 128 bytes (64 runes),
* and cannot contain '*', '/', ':', ';', '?', or '\'.
*/
int
isjolietfrog(Rune r)
{
return r==L'*' || r==L'/' || r==L':'
|| r==';' || r=='?' || r=='\\';
}
int
isbadjoliet(char *s)
{
Rune r[256], *p;
if(utflen(s) > 64)
return 1;
strtorune(r, s);
for(p=r; *p; p++)
if(isjolietfrog(*p))
return 1;
return 0;
}
/*
* Joliet name comparison
*
* The standard algorithm is the ISO9660 algorithm but
* on the encoded Runes. Runes are encoded in big endian
* format, so we can just use runecmp.
*
* Padding is with zeros, but that still doesn't affect us.
*/
static Rune emptystring[] = { (Rune)0 };
int
jolietcmp(const void *va, const void *vb)
{
int i;
Rune s1[256], s2[256], *b1, *b2, *e1, *e2; /*BUG*/
const Direc *a, *b;
a = va;
b = vb;
b1 = strtorune(s1, a->confname);
b2 = strtorune(s2, b->confname);
if((e1 = runechr(b1, (Rune)'.')) != nil)
*e1++ = '\0';
else
e1 = emptystring;
if((e2 = runechr(b2, (Rune)'.')) != nil)
*e2++ = '\0';
else
e2 = emptystring;
if((i = runecmp(b1, b2)) != 0)
return i;
return runecmp(e1, e2);
}
/*
* Write a Joliet secondary volume descriptor.
*/
void
Cputjolietsvd(Cdimg *cd, Cdinfo info)
{
Cputc(cd, 2); /* secondary volume descriptor */
Cputs(cd, "CD001", 5); /* standard identifier */
Cputc(cd, 1); /* volume descriptor version */
Cputc(cd, 0); /* unused */
Cputrscvt(cd, "Joliet Plan 9", 32); /* system identifier */
Cputrscvt(cd, info.volumename, 32); /* volume identifier */
Crepeat(cd, 0, 8); /* unused */
Cputn(cd, 0, 4); /* volume space size */
Cputc(cd, 0x25); /* escape sequences: UCS-2 Level 2 */
Cputc(cd, 0x2F);
Cputc(cd, 0x43);
Crepeat(cd, 0, 29);
Cputn(cd, 1, 2); /* volume set size */
Cputn(cd, 1, 2); /* volume sequence number */
Cputn(cd, Blocksize, 2); /* logical block size */
Cputn(cd, 0, 4); /* path table size */
Cputnl(cd, 0, 4); /* location of Lpath */
Cputnl(cd, 0, 4); /* location of optional Lpath */
Cputnm(cd, 0, 4); /* location of Mpath */
Cputnm(cd, 0, 4); /* location of optional Mpath */
Cputjolietdir(cd, nil, DTroot, 1, Cwoffset(cd)); /* root directory */
Cputrscvt(cd, info.volumeset, 128); /* volume set identifier */
Cputrscvt(cd, info.publisher, 128); /* publisher identifier */
Cputrscvt(cd, info.preparer, 128); /* data preparer identifier */
Cputrscvt(cd, info.application, 128); /* application identifier */
Cputrscvt(cd, "", 37); /* copyright notice */
Cputrscvt(cd, "", 37); /* abstract */
Cputrscvt(cd, "", 37); /* bibliographic file */
Cputdate1(cd, now); /* volume creation date */
Cputdate1(cd, now); /* volume modification date */
Cputdate1(cd, 0); /* volume expiration date */
Cputdate1(cd, 0); /* volume effective date */
Cputc(cd, 1); /* file structure version */
Cpadblock(cd);
}

5
src/cmd/9660/mk9660.rc Executable file
View file

@ -0,0 +1,5 @@
#!/bin/rc
# the master copy of this file is /sys/src/cmd/disk/9660/mk9660.rc
# do NOT edit the copies in /*/bin/disk.
exec disk/dump9660 -M $*

5
src/cmd/9660/mk9660.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/sh
# the master copy of this file is /sys/src/cmd/disk/9660/mk9660.rc
# do NOT edit the copies in /*/bin/disk.
exec disk/dump9660 -M $*

34
src/cmd/9660/mkfile Normal file
View file

@ -0,0 +1,34 @@
<$PLAN9/src/mkhdr
TARG=dump9660 mk9660
OFILES=
DFILES=\
boot.$O\
cdrdwr.$O\
conform.$O\
direc.$O\
dump.$O\
dump9660.$O\
ichar.$O\
jchar.$O\
path.$O\
unix.$O\
rune.$O\
sysuse.$O\
util.$O\
write.$O\
HFILES=iso9660.h
SHORTLIB=sec disk bio 9
<$PLAN9/src/mkmany
$O.dump9660: $DFILES
mk9660.$O:V:
# nothing
$O.mk9660: mk9660.sh
cp mk9660.sh $target

155
src/cmd/9660/path.c Normal file
View file

@ -0,0 +1,155 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include "iso9660.h"
/*
* Add the requisite path tables to the CD image.
* They get put on the end once everything else is done.
* We use the path table itself as a queue in the breadth-first
* traversal of the tree.
*
* The only problem with this is that the path table does not
* store the lengths of the directories. So we keep an explicit
* map in an array in memory.
*/
enum {
Big,
Little
};
static void
Crdpath(Cdimg *cd, Cpath *p)
{
p->namelen = Cgetc(cd);
if(p->namelen == 0) {
Crseek(cd, (Croffset(cd)+Blocksize-1)/Blocksize * Blocksize);
p->namelen = Cgetc(cd);
assert(p->namelen != 0);
}
p->xlen = Cgetc(cd);
assert(p->xlen == 0); /* sanity, might not be true if we start using the extended fields */
Cread(cd, p->dloc, 4);
Cread(cd, p->parent, 2);
p->name[0] = '\0';
Crseek(cd, Croffset(cd)+p->namelen+p->xlen+(p->namelen&1)); /* skip name, ext data */
}
static void
writepath(Cdimg *cd, Cdir *c, int parent, int size)
{
/*
DO NOT UNCOMMENT THIS CODE.
This commented-out code is here only so that no one comes
along and adds it later.
The ISO 9660 spec is silent about whether path table entries
need to be padded so that they never cross block boundaries.
It would be reasonable to assume that they are like every other
data structure in the bloody spec; this code pads them out.
Empirically, though, they're NOT padded. Windows NT and
derivatives are the only known current operating systems
that actually read these things.
int l;
l = 1+1+4+2+c->namelen;
if(Cwoffset(cd)/Blocksize != (Cwoffset(cd)+l)/Blocksize)
Cpadblock(cd);
*/
Cputc(cd, c->namelen);
Cputc(cd, 0);
Cwrite(cd, c->dloc + (size==Little ? 0 : 4), 4);
(size==Little ? Cputnl : Cputnm)(cd, parent, 2);
Cwrite(cd, c->name, c->namelen);
if(c->namelen & 1)
Cputc(cd, 0);
}
static ulong*
addlength(ulong *a, ulong x, int n)
{
if(n%128==0)
a = erealloc(a, (n+128)*sizeof a[0]);
a[n] = x;
return a;
}
static ulong
writepathtable(Cdimg *cd, ulong vdblock, int size)
{
int rp, wp;
uchar buf[Blocksize];
ulong bk, end, i, *len, n, rdoff, start;
Cdir *c;
Cpath p;
Creadblock(cd, buf, vdblock, Blocksize);
c = (Cdir*)(buf+offsetof(Cvoldesc, rootdir[0]));
rp = 0;
wp = 0;
len = nil;
start = cd->nextblock*Blocksize;
Cwseek(cd, start);
Crseek(cd, start);
writepath(cd, c, 1, size);
len = addlength(len, little(c->dlen, 4), wp);
wp++;
while(rp < wp) {
Crdpath(cd, &p);
n = (len[rp]+Blocksize-1)/Blocksize;
rp++;
bk = (size==Big ? big : little)(p.dloc, 4);
rdoff = Croffset(cd);
for(i=0; i<n; i++) {
Creadblock(cd, buf, bk+i, Blocksize);
c = (Cdir*)buf;
if(i != 0 && c->namelen == 1 && c->name[0] == '\0') /* hit another directory; stop */
break;
while(c->len && c->namelen && (uchar*)c+c->len < buf+Blocksize) {
if((c->flags & 0x02) && (c->namelen > 1 || c->name[0] > '\001')) { /* directory */
writepath(cd, c, rp, size);
len = addlength(len, little(c->dlen, 4), wp);
wp++;
}
c = (Cdir*)((uchar*)c+c->len);
}
}
Crseek(cd, rdoff);
}
end = Cwoffset(cd);
Cpadblock(cd);
return end-start;
}
static void
writepathtablepair(Cdimg *cd, ulong vdblock)
{
ulong bloc, lloc, sz, sz2;
lloc = cd->nextblock;
sz = writepathtable(cd, vdblock, Little);
bloc = cd->nextblock;
sz2 = writepathtable(cd, vdblock, Big);
assert(sz == sz2);
setpathtable(cd, vdblock, sz, lloc, bloc);
}
void
writepathtables(Cdimg *cd)
{
cd->pathblock = cd->nextblock;
writepathtablepair(cd, cd->iso9660pvd);
if(cd->flags & CDjoliet)
writepathtablepair(cd, cd->jolietsvd);
}

27
src/cmd/9660/plan9.c Normal file
View file

@ -0,0 +1,27 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include <disk.h>
#include "iso9660.h"
void
dirtoxdir(XDir *xd, Dir *d)
{
xd->name = atom(d->name);
xd->uid = atom(d->uid);
xd->gid = atom(d->gid);
xd->uidno = 0;
xd->gidno = 0;
xd->mode = d->mode;
xd->atime = d->atime;
xd->mtime = d->mtime;
xd->ctime = 0;
xd->length = d->length;
};
void
fdtruncate(int fd, ulong size)
{
USED(fd, size);
}

39
src/cmd/9660/rune.c Normal file
View file

@ -0,0 +1,39 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include "iso9660.h"
Rune*
strtorune(Rune *r, char *s)
{
Rune *or;
if(s == nil)
return nil;
or = r;
while(*s)
s += chartorune(r++, s);
*r = L'\0';
return or;
}
Rune*
runechr(Rune *s, Rune c)
{
for(; *s; s++)
if(*s == c)
return s;
return nil;
}
int
runecmp(Rune *s, Rune *t)
{
while(*s && *t && *s == *t)
s++, t++;
return *s - *t;
}

613
src/cmd/9660/sysuse.c Normal file
View file

@ -0,0 +1,613 @@
/*
* To understand this code, see Rock Ridge Interchange Protocol
* standard 1.12 and System Use Sharing Protocol version 1.12
* (search for rrip112.ps and susp112.ps on the web).
*
* Even better, go read something else.
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include "iso9660.h"
static long mode(Direc*, int);
static long nlink(Direc*);
static ulong suspdirflags(Direc*, int);
static ulong CputsuspCE(Cdimg *cd, ulong offset);
static int CputsuspER(Cdimg*, int);
static int CputsuspRR(Cdimg*, int, int);
static int CputsuspSP(Cdimg*, int);
//static int CputsuspST(Cdimg*, int);
static int Cputrripname(Cdimg*, char*, int, char*, int);
static int CputrripSL(Cdimg*, int, int, char*, int);
static int CputrripPX(Cdimg*, Direc*, int, int);
static int CputrripTF(Cdimg*, Direc*, int, int);
/*
* Patch the length field in a CE record.
*/
static void
setcelen(Cdimg *cd, ulong woffset, ulong len)
{
ulong o;
o = Cwoffset(cd);
Cwseek(cd, woffset);
Cputn(cd, len, 4);
Cwseek(cd, o);
}
/*
* Rock Ridge data is put into little blockettes, which can be
* at most 256 bytes including a one-byte length. Some number
* of blockettes get packed together into a normal 2048-byte block.
* Blockettes cannot cross block boundaries.
*
* A Cbuf is a blockette buffer. Len contains
* the length of the buffer written so far, and we can
* write up to 254-28.
*
* We only have one active Cbuf at a time; cdimg.rrcontin is the byte
* offset of the beginning of that Cbuf.
*
* The blockette can be at most 255 bytes. The last 28
* will be (in the worst case) a CE record pointing at
* a new blockette. If we do write 255 bytes though,
* we'll try to pad it out to be even, and overflow.
* So the maximum is 254-28.
*
* Ceoffset contains the offset to be used with setcelen
* to patch the CE pointing at the Cbuf once we know how
* long the Cbuf is.
*/
typedef struct Cbuf Cbuf;
struct Cbuf {
int len; /* written so far, of 254-28 */
ulong ceoffset;
};
static int
freespace(Cbuf *cp)
{
return (254-28) - cp->len;
}
static Cbuf*
ensurespace(Cdimg *cd, int n, Cbuf *co, Cbuf *cn, int dowrite)
{
ulong end;
if(co->len+n <= 254-28) {
co->len += n;
return co;
}
co->len += 28;
assert(co->len <= 254);
if(dowrite == 0) {
cn->len = n;
return cn;
}
/*
* the current blockette is full; update cd->rrcontin and then
* write a CE record to finish it. Unfortunately we need to
* figure out which block will be next before we write the CE.
*/
end = Cwoffset(cd)+28;
/*
* if we're in a continuation blockette, update rrcontin.
* also, write our length into the field of the CE record
* that points at us.
*/
if(cd->rrcontin+co->len == end) {
assert(cd->rrcontin != 0);
assert(co == cn);
cd->rrcontin += co->len;
setcelen(cd, co->ceoffset, co->len);
} else
assert(co != cn);
/*
* if the current continuation block can't fit another
* blockette, then start a new continuation block.
* rrcontin = 0 (mod Blocksize) means we just finished
* one, not that we've just started one.
*/
if(cd->rrcontin%Blocksize == 0
|| cd->rrcontin/Blocksize != (cd->rrcontin+256)/Blocksize) {
cd->rrcontin = cd->nextblock*Blocksize;
cd->nextblock++;
}
cn->ceoffset = CputsuspCE(cd, cd->rrcontin);
assert(Cwoffset(cd) == end);
cn->len = n;
Cwseek(cd, cd->rrcontin);
assert(cd->rrcontin != 0);
return cn;
}
/*
* Put down the name, but we might need to break it
* into chunks so that each chunk fits in 254-28-5 bytes.
* What a crock.
*
* The new Plan 9 format uses strings of this form too,
* since they're already there.
*/
Cbuf*
Cputstring(Cdimg *cd, Cbuf *cp, Cbuf *cn, char *nm, char *p, int flags, int dowrite)
{
char buf[256], *q;
int free;
for(; p[0] != '\0'; p = q) {
cp = ensurespace(cd, 5+1, cp, cn, dowrite);
cp->len -= 5+1;
free = freespace(cp);
assert(5+1 <= free && free < 256);
strncpy(buf, p, free-5);
buf[free-5] = '\0';
q = p+strlen(buf);
p = buf;
ensurespace(cd, 5+strlen(p), cp, nil, dowrite); /* nil: better not use this. */
Cputrripname(cd, nm, flags | (q[0] ? NMcontinue : 0), p, dowrite);
}
return cp;
}
/*
* Write a Rock Ridge SUSP set of records for a directory entry.
*/
int
Cputsysuse(Cdimg *cd, Direc *d, int dot, int dowrite, int initlen)
{
char buf[256], buf0[256], *nextpath, *p, *path, *q;
int flags, free, m, what;
ulong o;
Cbuf cn, co, *cp;
assert(cd != nil);
assert((initlen&1) == 0);
if(dot == DTroot)
return 0;
co.len = initlen;
o = Cwoffset(cd);
assert(dowrite==0 || Cwoffset(cd) == o+co.len-initlen);
cp = &co;
if (dot == DTrootdot) {
m = CputsuspSP(cd, 0);
cp = ensurespace(cd, m, cp, &cn, dowrite);
CputsuspSP(cd, dowrite);
m = CputsuspER(cd, 0);
cp = ensurespace(cd, m, cp, &cn, dowrite);
CputsuspER(cd, dowrite);
}
/*
* In a perfect world, we'd be able to omit the NM
* entries when our name was all lowercase and conformant,
* but OpenBSD insists on uppercasing (really, not lowercasing)
* the ISO9660 names.
*/
what = RR_PX | RR_TF | RR_NM;
if(d != nil && (d->mode & CHLINK))
what |= RR_SL;
m = CputsuspRR(cd, what, 0);
cp = ensurespace(cd, m, cp, &cn, dowrite);
CputsuspRR(cd, what, dowrite);
if(what & RR_PX) {
m = CputrripPX(cd, d, dot, 0);
cp = ensurespace(cd, m, cp, &cn, dowrite);
CputrripPX(cd, d, dot, dowrite);
}
if(what & RR_NM) {
if(dot == DTiden)
p = d->name;
else if(dot == DTdotdot)
p = "..";
else
p = ".";
flags = suspdirflags(d, dot);
assert(dowrite==0 || cp != &co || Cwoffset(cd) == o+co.len-initlen);
cp = Cputstring(cd, cp, &cn, "NM", p, flags, dowrite);
}
/*
* Put down the symbolic link. This is even more of a crock.
* Not only are the individual elements potentially split,
* but the whole path itself can be split across SL blocks.
* To keep the code simple as possible (really), we write
* only one element per SL block, wasting 6 bytes per element.
*/
if(what & RR_SL) {
for(path=d->symlink; path[0] != '\0'; path=nextpath) {
/* break off one component */
if((nextpath = strchr(path, '/')) == nil)
nextpath = path+strlen(path);
strncpy(buf0, path, nextpath-path);
buf0[nextpath-path] = '\0';
if(nextpath[0] == '/')
nextpath++;
p = buf0;
/* write the name, perhaps broken into pieces */
if(strcmp(p, "") == 0)
flags = NMroot;
else if(strcmp(p, ".") == 0)
flags = NMcurrent;
else if(strcmp(p, "..") == 0)
flags = NMparent;
else
flags = 0;
/* the do-while handles the empty string properly */
do {
/* must have room for at least 1 byte of name */
cp = ensurespace(cd, 7+1, cp, &cn, dowrite);
cp->len -= 7+1;
free = freespace(cp);
assert(7+1 <= free && free < 256);
strncpy(buf, p, free-7);
buf[free-7] = '\0';
q = p+strlen(buf);
p = buf;
/* nil: better not need to expand */
assert(7+strlen(p) <= free);
ensurespace(cd, 7+strlen(p), cp, nil, dowrite);
CputrripSL(cd, nextpath[0], flags | (q[0] ? NMcontinue : 0), p, dowrite);
p = q;
} while(p[0] != '\0');
}
}
assert(dowrite==0 || cp != &co || Cwoffset(cd) == o+co.len-initlen);
if(what & RR_TF) {
m = CputrripTF(cd, d, TFcreation|TFmodify|TFaccess|TFattributes, 0);
cp = ensurespace(cd, m, cp, &cn, dowrite);
CputrripTF(cd, d, TFcreation|TFmodify|TFaccess|TFattributes, dowrite);
}
assert(dowrite==0 || cp != &co || Cwoffset(cd) == o+co.len-initlen);
if(cp == &cn && dowrite) {
/* seek out of continuation, but mark our place */
cd->rrcontin = Cwoffset(cd);
setcelen(cd, cn.ceoffset, cn.len);
Cwseek(cd, o+co.len-initlen);
}
if(co.len & 1) {
co.len++;
if(dowrite)
Cputc(cd, 0);
}
if(dowrite) {
if(Cwoffset(cd) != o+co.len-initlen)
fprint(2, "offset %lud o+co.len-initlen %lud\n", Cwoffset(cd), o+co.len-initlen);
assert(Cwoffset(cd) == o+co.len-initlen);
} else
assert(Cwoffset(cd) == o);
assert(co.len <= 255);
return co.len - initlen;
}
static char SUSPrrip[10] = "RRIP_1991A";
static char SUSPdesc[84] = "RRIP <more garbage here>";
static char SUSPsrc[135] = "RRIP <more garbage here>";
static ulong
CputsuspCE(Cdimg *cd, ulong offset)
{
ulong o, x;
chat("writing SUSP CE record pointing to %ld, %ld\n", offset/Blocksize, offset%Blocksize);
o = Cwoffset(cd);
Cputc(cd, 'C');
Cputc(cd, 'E');
Cputc(cd, 28);
Cputc(cd, 1);
Cputn(cd, offset/Blocksize, 4);
Cputn(cd, offset%Blocksize, 4);
x = Cwoffset(cd);
Cputn(cd, 0, 4);
assert(Cwoffset(cd) == o+28);
return x;
}
static int
CputsuspER(Cdimg *cd, int dowrite)
{
assert(cd != nil);
if(dowrite) {
chat("writing SUSP ER record\n");
Cputc(cd, 'E'); /* ER field marker */
Cputc(cd, 'R');
Cputc(cd, 26); /* Length */
Cputc(cd, 1); /* Version */
Cputc(cd, 10); /* LEN_ID */
Cputc(cd, 4); /* LEN_DESC */
Cputc(cd, 4); /* LEN_SRC */
Cputc(cd, 1); /* EXT_VER */
Cputs(cd, SUSPrrip, 10); /* EXT_ID */
Cputs(cd, SUSPdesc, 4); /* EXT_DESC */
Cputs(cd, SUSPsrc, 4); /* EXT_SRC */
}
return 8+10+4+4;
}
static int
CputsuspRR(Cdimg *cd, int what, int dowrite)
{
assert(cd != nil);
if(dowrite) {
Cputc(cd, 'R'); /* RR field marker */
Cputc(cd, 'R');
Cputc(cd, 5); /* Length */
Cputc(cd, 1); /* Version number */
Cputc(cd, what); /* Flags */
}
return 5;
}
static int
CputsuspSP(Cdimg *cd, int dowrite)
{
assert(cd!=0);
if(dowrite) {
chat("writing SUSP SP record\n");
Cputc(cd, 'S'); /* SP field marker */
Cputc(cd, 'P');
Cputc(cd, 7); /* Length */
Cputc(cd, 1); /* Version */
Cputc(cd, 0xBE); /* Magic */
Cputc(cd, 0xEF);
Cputc(cd, 0);
}
return 7;
}
#ifdef NOTUSED
static int
CputsuspST(Cdimg *cd, int dowrite)
{
assert(cd!=0);
if(dowrite) {
Cputc(cd, 'S'); /* ST field marker */
Cputc(cd, 'T');
Cputc(cd, 4); /* Length */
Cputc(cd, 1); /* Version */
}
return 4;
}
#endif
static ulong
suspdirflags(Direc *d, int dot)
{
uchar flags;
USED(d);
flags = 0;
switch(dot) {
default:
assert(0);
case DTdot:
case DTrootdot:
flags |= NMcurrent;
break;
case DTdotdot:
flags |= NMparent;
break;
case DTroot:
flags |= NMvolroot;
break;
case DTiden:
break;
}
return flags;
}
static int
Cputrripname(Cdimg *cd, char *nm, int flags, char *name, int dowrite)
{
int l;
l = strlen(name);
if(dowrite) {
Cputc(cd, nm[0]); /* NM field marker */
Cputc(cd, nm[1]);
Cputc(cd, l+5); /* Length */
Cputc(cd, 1); /* Version */
Cputc(cd, flags); /* Flags */
Cputs(cd, name, l); /* Alternate name */
}
return 5+l;
}
static int
CputrripSL(Cdimg *cd, int contin, int flags, char *name, int dowrite)
{
int l;
l = strlen(name);
if(dowrite) {
Cputc(cd, 'S');
Cputc(cd, 'L');
Cputc(cd, l+7);
Cputc(cd, 1);
Cputc(cd, contin ? 1 : 0);
Cputc(cd, flags);
Cputc(cd, l);
Cputs(cd, name, l);
}
return 7+l;
}
static int
CputrripPX(Cdimg *cd, Direc *d, int dot, int dowrite)
{
assert(cd!=0);
if(dowrite) {
Cputc(cd, 'P'); /* PX field marker */
Cputc(cd, 'X');
Cputc(cd, 36); /* Length */
Cputc(cd, 1); /* Version */
Cputn(cd, mode(d, dot), 4); /* POSIX File mode */
Cputn(cd, nlink(d), 4); /* POSIX st_nlink */
Cputn(cd, d?d->uidno:0, 4); /* POSIX st_uid */
Cputn(cd, d?d->gidno:0, 4); /* POSIX st_gid */
}
return 36;
}
static int
CputrripTF(Cdimg *cd, Direc *d, int type, int dowrite)
{
int i, length;
assert(cd!=0);
assert(!(type & TFlongform));
length = 0;
for(i=0; i<7; i++)
if (type & (1<<i))
length++;
assert(length == 4);
if(dowrite) {
Cputc(cd, 'T'); /* TF field marker */
Cputc(cd, 'F');
Cputc(cd, 5+7*length); /* Length */
Cputc(cd, 1); /* Version */
Cputc(cd, type); /* Flags (types) */
if (type & TFcreation)
Cputdate(cd, d?d->ctime:0);
if (type & TFmodify)
Cputdate(cd, d?d->mtime:0);
if (type & TFaccess)
Cputdate(cd, d?d->atime:0);
if (type & TFattributes)
Cputdate(cd, d?d->ctime:0);
// if (type & TFbackup)
// Cputdate(cd, 0);
// if (type & TFexpiration)
// Cputdate(cd, 0);
// if (type & TFeffective)
// Cputdate(cd, 0);
}
return 5+7*length;
}
#define NONPXMODES (DMDIR & DMAPPEND & DMEXCL & DMMOUNT)
#define POSIXMODEMASK (0177777)
#ifndef S_IFMT
#define S_IFMT (0170000)
#endif
#ifndef S_IFDIR
#define S_IFDIR (0040000)
#endif
#ifndef S_IFREG
#define S_IFREG (0100000)
#endif
#ifndef S_IFLNK
#define S_IFLNK (0120000)
#endif
#undef ISTYPE
#define ISTYPE(mode, mask) (((mode) & S_IFMT) == (mask))
#ifndef S_ISDIR
#define S_ISDIR(mode) ISTYPE(mode, S_IFDIR)
#endif
#ifndef S_ISREG
#define S_ISREG(mode) ISTYPE(mode, S_IREG)
#endif
#ifndef S_ISLNK
#define S_ISLNK(mode) ISTYPE(mode, S_ILNK)
#endif
static long
mode(Direc *d, int dot)
{
long mode;
if (!d)
return 0;
if ((dot != DTroot) && (dot != DTrootdot)) {
mode = (d->mode & ~(NONPXMODES));
if (d->mode & DMDIR)
mode |= S_IFDIR;
else if (d->mode & CHLINK)
mode |= S_IFLNK;
else
mode |= S_IFREG;
} else
mode = S_IFDIR | (0755);
mode &= POSIXMODEMASK;
/* Botch: not all POSIX types supported yet */
assert(mode & (S_IFDIR|S_IFREG));
chat("writing PX record mode field %ulo with dot %d and name \"%s\"\n", mode, dot, d->name);
return mode;
}
static long
nlink(Direc *d) /* Trump up the nlink field for POSIX compliance */
{
int i;
long n;
if (!d)
return 0;
n = 1;
if (d->mode & DMDIR) /* One for "." and one more for ".." */
n++;
for(i=0; i<d->nchild; i++)
if (d->child[i].mode & DMDIR)
n++;
return n;
}

41
src/cmd/9660/uid.c Normal file
View file

@ -0,0 +1,41 @@
#include <u.h>
#include <libc.h>
/*
* /adm/users is
* id:user/group:head member:other members
*
* /etc/{passwd,group}
* name:x:nn:other stuff
*/
static int isnumber(char *s);
sniff(Biobuf *b)
{
read first line of file into p;
nf = getfields(p, f, nelem(f), ":");
if(nf < 4)
return nil;
if(isnumber(f[0]) && !isnumber(f[2]))
return _plan9;
if(!isnumber(f[0]) && isnumber(f[2]))
return _unix;
return nil;
}
int
isnumber(char *s)
{
char *q;
strtol(s, &q, 10);
return *q == '\0';
}
/* EOF */

84
src/cmd/9660/unix.c Normal file
View file

@ -0,0 +1,84 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include <disk.h>
#include <ctype.h>
#include "iso9660.h"
#include <grp.h>
#include <pwd.h>
typedef struct Xarg Xarg;
struct Xarg {
void (*enm)(char*,char*,XDir*,void*);
void (*warn)(char*,void*);
void *arg;
};
static long numericuid(char *user);
static long numericgid(char *gp);
void
dirtoxdir(XDir *xd, Dir *d)
{
// char buf[NAMELEN+1];
memset(xd, 0, sizeof *xd);
xd->name = atom(d->name);
xd->uid = atom(d->uid);
xd->gid = atom(d->gid);
xd->uidno = numericuid(d->uid);
xd->gidno = numericgid(d->gid);
xd->mode = d->mode;
xd->atime = d->atime;
xd->mtime = d->mtime;
xd->ctime = 0;
xd->length = d->length;
if(xd->mode & CHLINK) {
xd->mode |= 0777;
//xd->symlink = atom(d->symlink);
xd->symlink = atom("symlink"); // XXX: rsc
}
};
void
fdtruncate(int fd, ulong size)
{
ftruncate(fd, size);
return;
}
static long
numericuid(char *user)
{
struct passwd *pass;
static int warned = 0;
if (! (pass = getpwnam(user))) {
if (!warned)
fprint(2, "Warning: getpwnam(3) failed for \"%s\"\n", user);
warned = 1;
return 0;
}
return pass->pw_uid;
}
static long
numericgid(char *gp)
{
struct group *gr;
static int warned = 0;
if (! (gr = getgrnam(gp))) {
if (!warned)
fprint(2, "Warning: getgrnam(3) failed for \"%s\"\n", gp);
warned = 1;
return 0;
}
return gr->gr_gid;
}

98
src/cmd/9660/util.c Normal file
View file

@ -0,0 +1,98 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include <ctype.h>
#include "iso9660.h"
typedef struct Stringtab Stringtab;
struct Stringtab {
Stringtab *link;
char *str;
};
static Stringtab *stab[1024];
static uint
hash(char *s)
{
uint h;
uchar *p;
h = 0;
for(p=(uchar*)s; *p; p++)
h = h*37 + *p;
return h;
}
static char*
estrdup(char *s)
{
if((s = strdup(s)) == nil)
sysfatal("strdup(%.10s): out of memory", s);
return s;
}
char*
atom(char *str)
{
uint h;
Stringtab *tab;
h = hash(str) % nelem(stab);
for(tab=stab[h]; tab; tab=tab->link)
if(strcmp(str, tab->str) == 0)
return tab->str;
tab = emalloc(sizeof *tab);
tab->str = estrdup(str);
tab->link = stab[h];
stab[h] = tab;
return tab->str;
}
void*
emalloc(ulong n)
{
void *p;
if((p = malloc(n)) == nil)
sysfatal("malloc(%lud): out of memory", n);
memset(p, 0, n);
return p;
}
void*
erealloc(void *v, ulong n)
{
if((v = realloc(v, n)) == nil)
sysfatal("realloc(%p, %lud): out of memory", v, n);
return v;
}
char*
struprcpy(char *p, char *s)
{
char *op;
op = p;
for(; *s; s++)
*p++ = toupper(*s);
*p = '\0';
return op;
}
int
chat(char *fmt, ...)
{
va_list arg;
if(!chatty)
return 0;
va_start(arg, fmt);
vfprint(2, fmt, arg);
va_end(arg);
return 1;
}

409
src/cmd/9660/write.c Normal file
View file

@ -0,0 +1,409 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <libsec.h>
#include "iso9660.h"
static void
writelittlebig4(uchar *buf, ulong x)
{
buf[0] = buf[7] = x;
buf[1] = buf[6] = x>>8;
buf[2] = buf[5] = x>>16;
buf[3] = buf[4] = x>>24;
}
void
rewritedot(Cdimg *cd, Direc *d)
{
uchar buf[Blocksize];
Cdir *c;
Creadblock(cd, buf, d->block, Blocksize);
c = (Cdir*)buf;
assert(c->len != 0);
assert(c->namelen == 1 && c->name[0] == '\0'); /* dot */
writelittlebig4(c->dloc, d->block);
writelittlebig4(c->dlen, d->length);
Cwseek(cd, d->block*Blocksize);
Cwrite(cd, buf, Blocksize);
}
void
rewritedotdot(Cdimg *cd, Direc *d, Direc *dparent)
{
uchar buf[Blocksize];
Cdir *c;
Creadblock(cd, buf, d->block, Blocksize);
c = (Cdir*)buf;
assert(c->len != 0);
assert(c->namelen == 1 && c->name[0] == '\0'); /* dot */
c = (Cdir*)(buf+c->len);
assert(c->len != 0);
assert(c->namelen == 1 && c->name[0] == '\001'); /* dotdot*/
writelittlebig4(c->dloc, dparent->block);
writelittlebig4(c->dlen, dparent->length);
Cwseek(cd, d->block*Blocksize);
Cwrite(cd, buf, Blocksize);
}
/*
* Write each non-directory file. We copy the file to
* the cd image, and then if it turns out that we've
* seen this stream of bits before, we push the next block
* pointer back. This ensures consistency between the MD5s
* and the data on the CD image. MD5 summing on one pass
* and copying on another would not ensure this.
*/
void
writefiles(Dump *d, Cdimg *cd, Direc *direc)
{
int i;
uchar buf[8192], digest[MD5dlen];
ulong length, n, start;
Biobuf *b;
DigestState *s;
Dumpdir *dd;
if(direc->mode & DMDIR) {
for(i=0; i<direc->nchild; i++)
writefiles(d, cd, &direc->child[i]);
return;
}
assert(direc->block == 0);
if((b = Bopen(direc->srcfile, OREAD)) == nil){
fprint(2, "warning: cannot open '%s': %r\n", direc->srcfile);
direc->block = 0;
direc->length = 0;
return;
}
start = cd->nextblock;
assert(start != 0);
Cwseek(cd, start*Blocksize);
s = md5(nil, 0, nil, nil);
length = 0;
while((n = Bread(b, buf, sizeof buf)) > 0) {
md5(buf, n, nil, s);
Cwrite(cd, buf, n);
length += n;
}
md5(nil, 0, digest, s);
Bterm(b);
Cpadblock(cd);
if(length != direc->length) {
fprint(2, "warning: %s changed size underfoot\n", direc->srcfile);
direc->length = length;
}
if(length == 0)
direc->block = 0;
else if((dd = lookupmd5(d, digest))) {
assert(dd->length == length);
assert(dd->block != 0);
direc->block = dd->block;
cd->nextblock = start;
} else {
direc->block = start;
if(chatty > 1)
fprint(2, "lookup %.16H %lud (%s) failed\n", digest, length, direc->name);
insertmd5(d, atom(direc->name), digest, start, length);
}
}
/*
* Write a directory tree. We work from the leaves,
* and patch the dotdot pointers afterward.
*/
static void
_writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level)
{
int i, l, ll;
ulong start, next;
if((d->mode & DMDIR) == 0)
return;
if(chatty)
fprint(2, "%*s%s\n", 4*level, "", d->name);
for(i=0; i<d->nchild; i++)
_writedirs(cd, &d->child[i], put, level+1);
l = 0;
l += put(cd, d, (level == 0) ? DTrootdot : DTdot, 0, l);
l += put(cd, nil, DTdotdot, 0, l);
for(i=0; i<d->nchild; i++)
l += put(cd, &d->child[i], DTiden, 0, l);
start = cd->nextblock;
cd->nextblock += (l+Blocksize-1)/Blocksize;
next = cd->nextblock;
Cwseek(cd, start*Blocksize);
ll = 0;
ll += put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, ll);
ll += put(cd, nil, DTdotdot, 1, ll);
for(i=0; i<d->nchild; i++)
ll += put(cd, &d->child[i], DTiden, 1, ll);
assert(ll == l);
Cpadblock(cd);
assert(Cwoffset(cd) == next*Blocksize);
d->block = start;
d->length = (next - start) * Blocksize;
rewritedot(cd, d);
rewritedotdot(cd, d, d);
for(i=0; i<d->nchild; i++)
if(d->child[i].mode & DMDIR)
rewritedotdot(cd, &d->child[i], d);
}
void
writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int))
{
/*
* If we're writing a mk9660 image, then the root really
* is the root, so start at level 0. If we're writing a dump image,
* then the "root" is really going to be two levels down once
* we patch in the dump hierarchy above it, so start at level non-zero.
*/
if(chatty)
fprint(2, ">>> writedirs\n");
_writedirs(cd, d, put, mk9660 ? 0 : 1);
}
/*
* Write the dump tree. This is like writedirs but once we get to
* the roots of the individual days we just patch the parent dotdot blocks.
*/
static void
_writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level)
{
int i;
ulong start;
switch(level) {
case 0:
/* write root, list of years, also conform.map */
for(i=0; i<d->nchild; i++)
if(d->child[i].mode & DMDIR)
_writedumpdirs(cd, &d->child[i], put, level+1);
chat("write dump root dir at %lud\n", cd->nextblock);
goto Writedir;
case 1: /* write year, list of days */
for(i=0; i<d->nchild; i++)
_writedumpdirs(cd, &d->child[i], put, level+1);
chat("write dump %s dir at %lud\n", d->name, cd->nextblock);
goto Writedir;
Writedir:
start = cd->nextblock;
Cwseek(cd, start*Blocksize);
put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, Cwoffset(cd));
put(cd, nil, DTdotdot, 1, Cwoffset(cd));
for(i=0; i<d->nchild; i++)
put(cd, &d->child[i], DTiden, 1, Cwoffset(cd));
Cpadblock(cd);
d->block = start;
d->length = (cd->nextblock - start) * Blocksize;
rewritedot(cd, d);
rewritedotdot(cd, d, d);
for(i=0; i<d->nchild; i++)
if(d->child[i].mode & DMDIR)
rewritedotdot(cd, &d->child[i], d);
break;
case 2: /* write day: already written, do nothing */
break;
default:
assert(0);
}
}
void
writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int))
{
_writedumpdirs(cd, d, put, 0);
}
static int
Cputplan9(Cdimg *cd, Direc *d, int dot, int dowrite)
{
int l, n;
if(dot != DTiden)
return 0;
l = 0;
if(d->flags & Dbadname) {
n = strlen(d->name);
l += 1+n;
if(dowrite) {
Cputc(cd, n);
Cputs(cd, d->name, n);
}
} else {
l++;
if(dowrite)
Cputc(cd, 0);
}
n = strlen(d->uid);
l += 1+n;
if(dowrite) {
Cputc(cd, n);
Cputs(cd, d->uid, n);
}
n = strlen(d->gid);
l += 1+n;
if(dowrite) {
Cputc(cd, n);
Cputs(cd, d->gid, n);
}
if(l & 1) {
l++;
if(dowrite)
Cputc(cd, 0);
}
l += 8;
if(dowrite)
Cputn(cd, d->mode, 4);
return l;
}
/*
* Write a directory entry.
*/
static int
genputdir(Cdimg *cd, Direc *d, int dot, int joliet, int dowrite, int offset)
{
int f, n, l, lp;
long o;
f = 0;
if(dot != DTiden || (d->mode & DMDIR))
f |= 2;
n = 1;
if(dot == DTiden) {
if(joliet)
n = 2*utflen(d->confname);
else
n = strlen(d->confname);
}
l = 33+n;
if(l & 1)
l++;
assert(l <= 255);
if(joliet == 0) {
if(cd->flags & CDplan9)
l += Cputplan9(cd, d, dot, 0);
else if(cd->flags & CDrockridge)
l += Cputsysuse(cd, d, dot, 0, l);
assert(l <= 255);
}
if(dowrite == 0) {
if(Blocksize - offset%Blocksize < l)
l += Blocksize - offset%Blocksize;
return l;
}
assert(offset%Blocksize == Cwoffset(cd)%Blocksize);
o = Cwoffset(cd);
lp = 0;
if(Blocksize - Cwoffset(cd)%Blocksize < l) {
lp = Blocksize - Cwoffset(cd)%Blocksize;
Cpadblock(cd);
}
Cputc(cd, l); /* length of directory record */
Cputc(cd, 0); /* extended attribute record length */
if(d) {
if((d->mode & DMDIR) == 0)
assert(d->length == 0 || d->block >= 18);
Cputn(cd, d->block, 4); /* location of extent */
Cputn(cd, d->length, 4); /* data length */
} else {
Cputn(cd, 0, 4);
Cputn(cd, 0, 4);
}
Cputdate(cd, d ? d->mtime : now); /* recorded date */
Cputc(cd, f); /* file flags */
Cputc(cd, 0); /* file unit size */
Cputc(cd, 0); /* interleave gap size */
Cputn(cd, 1, 2); /* volume sequence number */
Cputc(cd, n); /* length of file identifier */
if(dot == DTiden) { /* identifier */
if(joliet)
Cputrscvt(cd, d->confname, n);
else
Cputs(cd, d->confname, n);
}else
if(dot == DTdotdot)
Cputc(cd, 1);
else
Cputc(cd, 0);
if(Cwoffset(cd) & 1) /* pad */
Cputc(cd, 0);
if(joliet == 0) {
if(cd->flags & CDplan9)
Cputplan9(cd, d, dot, 1);
else if(cd->flags & CDrockridge)
Cputsysuse(cd, d, dot, 1, Cwoffset(cd)-(o+lp));
}
assert(o+lp+l == Cwoffset(cd));
return lp+l;
}
int
Cputisodir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset)
{
return genputdir(cd, d, dot, 0, dowrite, offset);
}
int
Cputjolietdir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset)
{
return genputdir(cd, d, dot, 1, dowrite, offset);
}
void
Cputendvd(Cdimg *cd)
{
Cputc(cd, 255); /* volume descriptor set terminator */
Cputs(cd, "CD001", 5); /* standard identifier */
Cputc(cd, 1); /* volume descriptor version */
Cpadblock(cd);
}