| /* |
| * 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; |
| } |
| |