diff options
Diffstat (limited to 'src/cmd/9660/sysuse.c')
-rw-r--r-- | src/cmd/9660/sysuse.c | 613 |
1 files changed, 613 insertions, 0 deletions
diff --git a/src/cmd/9660/sysuse.c b/src/cmd/9660/sysuse.c new file mode 100644 index 00000000..dc326c6a --- /dev/null +++ b/src/cmd/9660/sysuse.c @@ -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; +} + |