mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-12 11:10:07 +00:00
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:
parent
e1dddc0532
commit
7285a491c1
20 changed files with 4375 additions and 0 deletions
189
src/cmd/9660/boot.c
Normal file
189
src/cmd/9660/boot.c
Normal 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
632
src/cmd/9660/cdrdwr.c
Normal 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
141
src/cmd/9660/conform.c
Normal 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
222
src/cmd/9660/direc.c
Normal 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
511
src/cmd/9660/dump.c
Normal 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
402
src/cmd/9660/dump9660.c
Normal 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
206
src/cmd/9660/ichar.c
Normal 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
424
src/cmd/9660/iso9660.h
Normal 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
138
src/cmd/9660/jchar.c
Normal 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
5
src/cmd/9660/mk9660.rc
Executable 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
5
src/cmd/9660/mk9660.sh
Executable 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
34
src/cmd/9660/mkfile
Normal 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
155
src/cmd/9660/path.c
Normal 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
27
src/cmd/9660/plan9.c
Normal 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
39
src/cmd/9660/rune.c
Normal 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
613
src/cmd/9660/sysuse.c
Normal 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
41
src/cmd/9660/uid.c
Normal 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
84
src/cmd/9660/unix.c
Normal 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
98
src/cmd/9660/util.c
Normal 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
409
src/cmd/9660/write.c
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue