plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

sysuse.c (13953B)


      1 /*
      2  * To understand this code, see Rock Ridge Interchange Protocol
      3  * standard 1.12 and System Use Sharing Protocol version 1.12
      4  * (search for rrip112.ps and susp112.ps on the web).
      5  *
      6  * Even better, go read something else.
      7  */
      8 
      9 #include <u.h>
     10 #include <libc.h>
     11 #include <bio.h>
     12 #include <libsec.h>
     13 #include "iso9660.h"
     14 
     15 static long mode(Direc*, int);
     16 static long nlink(Direc*);
     17 static ulong suspdirflags(Direc*, int);
     18 static ulong CputsuspCE(Cdimg *cd, ulong offset);
     19 static int CputsuspER(Cdimg*, int);
     20 static int CputsuspRR(Cdimg*, int, int);
     21 static int CputsuspSP(Cdimg*, int);
     22 /*static int CputsuspST(Cdimg*, int); */
     23 static int Cputrripname(Cdimg*, char*, int, char*, int);
     24 static int CputrripSL(Cdimg*, int, int, char*, int);
     25 static int CputrripPX(Cdimg*, Direc*, int, int);
     26 static int CputrripTF(Cdimg*, Direc*, int, int);
     27 
     28 /*
     29  * Patch the length field in a CE record.
     30  */
     31 static void
     32 setcelen(Cdimg *cd, ulong woffset, ulong len)
     33 {
     34 	ulong o;
     35 
     36 	o = Cwoffset(cd);
     37 	Cwseek(cd, woffset);
     38 	Cputn(cd, len, 4);
     39 	Cwseek(cd, o);
     40 }
     41 
     42 /*
     43  * Rock Ridge data is put into little blockettes, which can be
     44  * at most 256 bytes including a one-byte length.  Some number
     45  * of blockettes get packed together into a normal 2048-byte block.
     46  * Blockettes cannot cross block boundaries.
     47  *
     48  * A Cbuf is a blockette buffer.  Len contains
     49  * the length of the buffer written so far, and we can
     50  * write up to 254-28.
     51  *
     52  * We only have one active Cbuf at a time; cdimg.rrcontin is the byte
     53  * offset of the beginning of that Cbuf.
     54  *
     55  * The blockette can be at most 255 bytes.  The last 28
     56  * will be (in the worst case) a CE record pointing at
     57  * a new blockette.  If we do write 255 bytes though,
     58  * we'll try to pad it out to be even, and overflow.
     59  * So the maximum is 254-28.
     60  *
     61  * Ceoffset contains the offset to be used with setcelen
     62  * to patch the CE pointing at the Cbuf once we know how
     63  * long the Cbuf is.
     64  */
     65 typedef struct Cbuf Cbuf;
     66 struct Cbuf {
     67 	int len;	/* written so far, of 254-28 */
     68 	ulong ceoffset;
     69 };
     70 
     71 static int
     72 freespace(Cbuf *cp)
     73 {
     74 	return (254-28) - cp->len;
     75 }
     76 
     77 static Cbuf*
     78 ensurespace(Cdimg *cd, int n, Cbuf *co, Cbuf *cn, int dowrite)
     79 {
     80 	ulong end;
     81 
     82 	if(co->len+n <= 254-28) {
     83 		co->len += n;
     84 		return co;
     85 	}
     86 
     87 	co->len += 28;
     88 	assert(co->len <= 254);
     89 
     90 	if(dowrite == 0) {
     91 		cn->len = n;
     92 		return cn;
     93 	}
     94 
     95 	/*
     96 	 * the current blockette is full; update cd->rrcontin and then
     97  	 * write a CE record to finish it.  Unfortunately we need to
     98 	 * figure out which block will be next before we write the CE.
     99 	 */
    100 	end = Cwoffset(cd)+28;
    101 
    102 	/*
    103 	 * if we're in a continuation blockette, update rrcontin.
    104 	 * also, write our length into the field of the CE record
    105 	 * that points at us.
    106 	 */
    107 	if(cd->rrcontin+co->len == end) {
    108 		assert(cd->rrcontin != 0);
    109 		assert(co == cn);
    110 		cd->rrcontin += co->len;
    111 		setcelen(cd, co->ceoffset, co->len);
    112 	} else
    113 		assert(co != cn);
    114 
    115 	/*
    116 	 * if the current continuation block can't fit another
    117 	 * blockette, then start a new continuation block.
    118 	 * rrcontin = 0 (mod Blocksize) means we just finished
    119 	 * one, not that we've just started one.
    120 	 */
    121 	if(cd->rrcontin%Blocksize == 0
    122 	|| cd->rrcontin/Blocksize != (cd->rrcontin+256)/Blocksize) {
    123 		cd->rrcontin = cd->nextblock*Blocksize;
    124 		cd->nextblock++;
    125 	}
    126 
    127 	cn->ceoffset = CputsuspCE(cd, cd->rrcontin);
    128 
    129 	assert(Cwoffset(cd) == end);
    130 
    131 	cn->len = n;
    132 	Cwseek(cd, cd->rrcontin);
    133 	assert(cd->rrcontin != 0);
    134 
    135 	return cn;
    136 }
    137 
    138 /*
    139  * Put down the name, but we might need to break it
    140  * into chunks so that each chunk fits in 254-28-5 bytes.
    141  * What a crock.
    142  *
    143  * The new Plan 9 format uses strings of this form too,
    144  * since they're already there.
    145  */
    146 Cbuf*
    147 Cputstring(Cdimg *cd, Cbuf *cp, Cbuf *cn, char *nm, char *p, int flags, int dowrite)
    148 {
    149 	char buf[256], *q;
    150 	int free;
    151 
    152 	for(; p[0] != '\0'; p = q) {
    153 		cp = ensurespace(cd, 5+1, cp, cn, dowrite);
    154 		cp->len -= 5+1;
    155 		free = freespace(cp);
    156 		assert(5+1 <= free && free < 256);
    157 
    158 		strncpy(buf, p, free-5);
    159 		buf[free-5] = '\0';
    160 		q = p+strlen(buf);
    161 		p = buf;
    162 
    163 		ensurespace(cd, 5+strlen(p), cp, nil, dowrite);	/* nil: better not use this. */
    164 		Cputrripname(cd, nm, flags | (q[0] ? NMcontinue : 0), p, dowrite);
    165 	}
    166 	return cp;
    167 }
    168 
    169 /*
    170  * Write a Rock Ridge SUSP set of records for a directory entry.
    171  */
    172 int
    173 Cputsysuse(Cdimg *cd, Direc *d, int dot, int dowrite, int initlen)
    174 {
    175 	char buf[256], buf0[256], *nextpath, *p, *path, *q;
    176 	int flags, free, m, what;
    177 	ulong o;
    178 	Cbuf cn, co, *cp;
    179 
    180 	assert(cd != nil);
    181 	assert((initlen&1) == 0);
    182 
    183 	if(dot == DTroot)
    184 		return 0;
    185 
    186 	co.len = initlen;
    187 
    188 	o = Cwoffset(cd);
    189 
    190 	assert(dowrite==0 || Cwoffset(cd) == o+co.len-initlen);
    191 	cp = &co;
    192 
    193 	if (dot == DTrootdot) {
    194 		m = CputsuspSP(cd, 0);
    195 		cp = ensurespace(cd, m, cp, &cn, dowrite);
    196 		CputsuspSP(cd, dowrite);
    197 
    198 		m = CputsuspER(cd, 0);
    199 		cp = ensurespace(cd, m, cp, &cn, dowrite);
    200 		CputsuspER(cd, dowrite);
    201 	}
    202 
    203 	/*
    204 	 * In a perfect world, we'd be able to omit the NM
    205 	 * entries when our name was all lowercase and conformant,
    206 	 * but OpenBSD insists on uppercasing (really, not lowercasing)
    207 	 * the ISO9660 names.
    208 	 */
    209 	what = RR_PX | RR_TF | RR_NM;
    210 	if(d != nil && (d->mode & CHLINK))
    211 		what |= RR_SL;
    212 
    213 	m = CputsuspRR(cd, what, 0);
    214 	cp = ensurespace(cd, m, cp, &cn, dowrite);
    215 	CputsuspRR(cd, what, dowrite);
    216 
    217 	if(what & RR_PX) {
    218 		m = CputrripPX(cd, d, dot, 0);
    219 		cp = ensurespace(cd, m, cp, &cn, dowrite);
    220 		CputrripPX(cd, d, dot, dowrite);
    221 	}
    222 
    223 	if(what & RR_NM) {
    224 		if(dot == DTiden)
    225 			p = d->name;
    226 		else if(dot == DTdotdot)
    227 			p = "..";
    228 		else
    229 			p = ".";
    230 
    231 		flags = suspdirflags(d, dot);
    232 		assert(dowrite==0 || cp != &co || Cwoffset(cd) == o+co.len-initlen);
    233 		cp = Cputstring(cd, cp, &cn, "NM", p, flags, dowrite);
    234 	}
    235 
    236 	/*
    237 	 * Put down the symbolic link.  This is even more of a crock.
    238 	 * Not only are the individual elements potentially split,
    239 	 * but the whole path itself can be split across SL blocks.
    240 	 * To keep the code simple as possible (really), we write
    241 	 * only one element per SL block, wasting 6 bytes per element.
    242 	 */
    243 	if(what & RR_SL) {
    244 		for(path=d->symlink; path[0] != '\0'; path=nextpath) {
    245 			/* break off one component */
    246 			if((nextpath = strchr(path, '/')) == nil)
    247 				nextpath = path+strlen(path);
    248 			strncpy(buf0, path, nextpath-path);
    249 			buf0[nextpath-path] = '\0';
    250 			if(nextpath[0] == '/')
    251 				nextpath++;
    252 			p = buf0;
    253 
    254 			/* write the name, perhaps broken into pieces */
    255 			if(strcmp(p, "") == 0)
    256 				flags = NMroot;
    257 			else if(strcmp(p, ".") == 0)
    258 				flags = NMcurrent;
    259 			else if(strcmp(p, "..") == 0)
    260 				flags = NMparent;
    261 			else
    262 				flags = 0;
    263 
    264 			/* the do-while handles the empty string properly */
    265 			do {
    266 				/* must have room for at least 1 byte of name */
    267 				cp = ensurespace(cd, 7+1, cp, &cn, dowrite);
    268 				cp->len -= 7+1;
    269 				free = freespace(cp);
    270 				assert(7+1 <= free && free < 256);
    271 
    272 				strncpy(buf, p, free-7);
    273 				buf[free-7] = '\0';
    274 				q = p+strlen(buf);
    275 				p = buf;
    276 
    277 				/* nil: better not need to expand */
    278 				assert(7+strlen(p) <= free);
    279 				ensurespace(cd, 7+strlen(p), cp, nil, dowrite);
    280 				CputrripSL(cd, nextpath[0], flags | (q[0] ? NMcontinue : 0), p, dowrite);
    281 				p = q;
    282 			} while(p[0] != '\0');
    283 		}
    284 	}
    285 
    286 	assert(dowrite==0 || cp != &co || Cwoffset(cd) == o+co.len-initlen);
    287 
    288 	if(what & RR_TF) {
    289 		m = CputrripTF(cd, d, TFcreation|TFmodify|TFaccess|TFattributes, 0);
    290 		cp = ensurespace(cd, m, cp, &cn, dowrite);
    291 		CputrripTF(cd, d, TFcreation|TFmodify|TFaccess|TFattributes, dowrite);
    292 	}
    293 	assert(dowrite==0 || cp != &co || Cwoffset(cd) == o+co.len-initlen);
    294 
    295 	if(cp == &cn && dowrite) {
    296 		/* seek out of continuation, but mark our place */
    297 		cd->rrcontin = Cwoffset(cd);
    298 		setcelen(cd, cn.ceoffset, cn.len);
    299 		Cwseek(cd, o+co.len-initlen);
    300 	}
    301 
    302 	if(co.len & 1) {
    303 		co.len++;
    304 		if(dowrite)
    305 			Cputc(cd, 0);
    306 	}
    307 
    308 	if(dowrite) {
    309 		if(Cwoffset(cd) != o+co.len-initlen)
    310 			fprint(2, "offset %lud o+co.len-initlen %lud\n", Cwoffset(cd), o+co.len-initlen);
    311 		assert(Cwoffset(cd) == o+co.len-initlen);
    312 	} else
    313 		assert(Cwoffset(cd) == o);
    314 
    315 	assert(co.len <= 255);
    316 	return co.len - initlen;
    317 }
    318 
    319 static char SUSPrrip[10] = "RRIP_1991A";
    320 static char SUSPdesc[84] = "RRIP <more garbage here>";
    321 static char SUSPsrc[135] = "RRIP <more garbage here>";
    322 
    323 static ulong
    324 CputsuspCE(Cdimg *cd, ulong offset)
    325 {
    326 	ulong o, x;
    327 
    328 	chat("writing SUSP CE record pointing to %ld, %ld\n", offset/Blocksize, offset%Blocksize);
    329 	o = Cwoffset(cd);
    330 	Cputc(cd, 'C');
    331 	Cputc(cd, 'E');
    332 	Cputc(cd, 28);
    333 	Cputc(cd, 1);
    334 	Cputn(cd, offset/Blocksize, 4);
    335 	Cputn(cd, offset%Blocksize, 4);
    336 	x = Cwoffset(cd);
    337 	Cputn(cd, 0, 4);
    338 	assert(Cwoffset(cd) == o+28);
    339 
    340 	return x;
    341 }
    342 
    343 static int
    344 CputsuspER(Cdimg *cd, int dowrite)
    345 {
    346 	assert(cd != nil);
    347 
    348 	if(dowrite) {
    349 		chat("writing SUSP ER record\n");
    350 		Cputc(cd, 'E');           /* ER field marker */
    351 		Cputc(cd, 'R');
    352 		Cputc(cd, 26);            /* Length          */
    353 		Cputc(cd, 1);             /* Version         */
    354 		Cputc(cd, 10);            /* LEN_ID          */
    355 		Cputc(cd, 4);             /* LEN_DESC        */
    356 		Cputc(cd, 4);             /* LEN_SRC         */
    357 		Cputc(cd, 1);             /* EXT_VER         */
    358 		Cputs(cd, SUSPrrip, 10);  /* EXT_ID          */
    359 		Cputs(cd, SUSPdesc, 4);   /* EXT_DESC        */
    360 		Cputs(cd, SUSPsrc, 4);    /* EXT_SRC         */
    361 	}
    362 	return 8+10+4+4;
    363 }
    364 
    365 static int
    366 CputsuspRR(Cdimg *cd, int what, int dowrite)
    367 {
    368 	assert(cd != nil);
    369 
    370 	if(dowrite) {
    371 		Cputc(cd, 'R');           /* RR field marker */
    372 		Cputc(cd, 'R');
    373 		Cputc(cd, 5);             /* Length          */
    374 		Cputc(cd, 1);		  /* Version number  */
    375 		Cputc(cd, what);          /* Flags           */
    376 	}
    377 	return 5;
    378 }
    379 
    380 static int
    381 CputsuspSP(Cdimg *cd, int dowrite)
    382 {
    383 	assert(cd!=0);
    384 
    385 	if(dowrite) {
    386 chat("writing SUSP SP record\n");
    387 		Cputc(cd, 'S');           /* SP field marker */
    388 		Cputc(cd, 'P');
    389 		Cputc(cd, 7);             /* Length          */
    390 		Cputc(cd, 1);             /* Version         */
    391 		Cputc(cd, 0xBE);          /* Magic           */
    392 		Cputc(cd, 0xEF);
    393 		Cputc(cd, 0);
    394 	}
    395 
    396 	return 7;
    397 }
    398 
    399 #ifdef NOTUSED
    400 static int
    401 CputsuspST(Cdimg *cd, int dowrite)
    402 {
    403 	assert(cd!=0);
    404 
    405 	if(dowrite) {
    406 		Cputc(cd, 'S');           /* ST field marker */
    407 		Cputc(cd, 'T');
    408 		Cputc(cd, 4);             /* Length          */
    409 		Cputc(cd, 1);             /* Version         */
    410 	}
    411 	return 4;
    412 }
    413 #endif
    414 
    415 static ulong
    416 suspdirflags(Direc *d, int dot)
    417 {
    418 	uchar flags;
    419 
    420 	USED(d);
    421 	flags = 0;
    422 	switch(dot) {
    423 	default:
    424 		assert(0);
    425 	case DTdot:
    426 	case DTrootdot:
    427 		flags |= NMcurrent;
    428 		break;
    429 	case DTdotdot:
    430 		flags |= NMparent;
    431 		break;
    432 	case DTroot:
    433 		flags |= NMvolroot;
    434 		break;
    435 	case DTiden:
    436 		break;
    437 	}
    438 	return flags;
    439 }
    440 
    441 static int
    442 Cputrripname(Cdimg *cd, char *nm, int flags, char *name, int dowrite)
    443 {
    444 	int l;
    445 
    446 	l = strlen(name);
    447 	if(dowrite) {
    448 		Cputc(cd, nm[0]);                   /* NM field marker */
    449 		Cputc(cd, nm[1]);
    450 		Cputc(cd, l+5);        /* Length          */
    451 		Cputc(cd, 1);                     /* Version         */
    452 		Cputc(cd, flags);                 /* Flags           */
    453 		Cputs(cd, name, l);    /* Alternate name  */
    454 	}
    455 	return 5+l;
    456 }
    457 
    458 static int
    459 CputrripSL(Cdimg *cd, int contin, int flags, char *name, int dowrite)
    460 {
    461 	int l;
    462 
    463 	l = strlen(name);
    464 	if(dowrite) {
    465 		Cputc(cd, 'S');
    466 		Cputc(cd, 'L');
    467 		Cputc(cd, l+7);
    468 		Cputc(cd, 1);
    469 		Cputc(cd, contin ? 1 : 0);
    470 		Cputc(cd, flags);
    471 		Cputc(cd, l);
    472 		Cputs(cd, name, l);
    473 	}
    474 	return 7+l;
    475 }
    476 
    477 static int
    478 CputrripPX(Cdimg *cd, Direc *d, int dot, int dowrite)
    479 {
    480 	assert(cd!=0);
    481 
    482 	if(dowrite) {
    483 		Cputc(cd, 'P');             /* PX field marker */
    484 		Cputc(cd, 'X');
    485 		Cputc(cd, 36);              /* Length          */
    486 		Cputc(cd, 1);               /* Version         */
    487 
    488 		Cputn(cd, mode(d, dot), 4); /* POSIX File mode */
    489 		Cputn(cd, nlink(d), 4);     /* POSIX st_nlink  */
    490 		Cputn(cd, d?d->uidno:0, 4);  /* POSIX st_uid    */
    491 		Cputn(cd, d?d->gidno:0, 4);  /* POSIX st_gid    */
    492 	}
    493 
    494 	return 36;
    495 }
    496 
    497 static int
    498 CputrripTF(Cdimg *cd, Direc *d, int type, int dowrite)
    499 {
    500 	int i, length;
    501 
    502 	assert(cd!=0);
    503 	assert(!(type & TFlongform));
    504 
    505 	length = 0;
    506 	for(i=0; i<7; i++)
    507 		if (type & (1<<i))
    508 			length++;
    509 	assert(length == 4);
    510 
    511 	if(dowrite) {
    512 		Cputc(cd, 'T');				/* TF field marker */
    513 		Cputc(cd, 'F');
    514 		Cputc(cd, 5+7*length);		/* Length		 */
    515 		Cputc(cd, 1);				/* Version		 */
    516 		Cputc(cd, type);					/* Flags (types)	 */
    517 
    518 		if (type & TFcreation)
    519 			Cputdate(cd, d?d->ctime:0);
    520 		if (type & TFmodify)
    521 			Cputdate(cd, d?d->mtime:0);
    522 		if (type & TFaccess)
    523 			Cputdate(cd, d?d->atime:0);
    524 		if (type & TFattributes)
    525 			Cputdate(cd, d?d->ctime:0);
    526 
    527 	/*	if (type & TFbackup) */
    528 	/*		Cputdate(cd, 0); */
    529 	/*	if (type & TFexpiration) */
    530 	/*		Cputdate(cd, 0); */
    531 	/*	if (type & TFeffective) */
    532 	/*		Cputdate(cd, 0); */
    533 	}
    534 	return 5+7*length;
    535 }
    536 
    537 
    538 #define NONPXMODES  (DMDIR & DMAPPEND & DMEXCL & DMMOUNT)
    539 #define POSIXMODEMASK (0177777)
    540 #ifndef S_IFMT
    541 #define S_IFMT  (0170000)
    542 #endif
    543 #ifndef S_IFDIR
    544 #define S_IFDIR (0040000)
    545 #endif
    546 #ifndef S_IFREG
    547 #define S_IFREG (0100000)
    548 #endif
    549 #ifndef S_IFLNK
    550 #define S_IFLNK (0120000)
    551 #endif
    552 #undef  ISTYPE
    553 #define ISTYPE(mode, mask)  (((mode) & S_IFMT) == (mask))
    554 #ifndef S_ISDIR
    555 #define S_ISDIR(mode) ISTYPE(mode, S_IFDIR)
    556 #endif
    557 #ifndef S_ISREG
    558 #define S_ISREG(mode) ISTYPE(mode, S_IREG)
    559 #endif
    560 #ifndef S_ISLNK
    561 #define S_ISLNK(mode) ISTYPE(mode, S_ILNK)
    562 #endif
    563 
    564 
    565 static long
    566 mode(Direc *d, int dot)
    567 {
    568 	long mode;
    569 
    570 	if (!d)
    571 		return 0;
    572 
    573 	if ((dot != DTroot) && (dot != DTrootdot)) {
    574 		mode = (d->mode & ~(NONPXMODES));
    575 		if (d->mode & DMDIR)
    576 			mode |= S_IFDIR;
    577 		else if (d->mode & CHLINK)
    578 			mode |= S_IFLNK;
    579 		else
    580 			mode |= S_IFREG;
    581 	} else
    582 		mode = S_IFDIR | (0755);
    583 
    584 	mode &= POSIXMODEMASK;
    585 
    586 	/* Botch: not all POSIX types supported yet */
    587 	assert(mode & (S_IFDIR|S_IFREG));
    588 
    589 chat("writing PX record mode field %ulo with dot %d and name \"%s\"\n", mode, dot, d->name);
    590 
    591 	return mode;
    592 }
    593 
    594 static long
    595 nlink(Direc *d)   /* Trump up the nlink field for POSIX compliance */
    596 {
    597 	int i;
    598 	long n;
    599 
    600 	if (!d)
    601 		return 0;
    602 
    603 	n = 1;
    604 	if (d->mode & DMDIR)   /* One for "." and one more for ".." */
    605 		n++;
    606 
    607 	for(i=0; i<d->nchild; i++)
    608 		if (d->child[i].mode & DMDIR)
    609 			n++;
    610 
    611 	return n;
    612 }