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
setcelen(Cdimg * cd,ulong woffset,ulong len)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
freespace(Cbuf * cp)72 freespace(Cbuf *cp)
73 {
74 	return (254-28) - cp->len;
75 }
76 
77 static Cbuf*
ensurespace(Cdimg * cd,int n,Cbuf * co,Cbuf * cn,int dowrite)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*
Cputstring(Cdimg * cd,Cbuf * cp,Cbuf * cn,char * nm,char * p,int flags,int dowrite)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
Cputsysuse(Cdimg * cd,Direc * d,int dot,int dowrite,int initlen)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
CputsuspCE(Cdimg * cd,ulong offset)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
CputsuspER(Cdimg * cd,int dowrite)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
CputsuspRR(Cdimg * cd,int what,int dowrite)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
CputsuspSP(Cdimg * cd,int dowrite)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
CputsuspST(Cdimg * cd,int dowrite)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
suspdirflags(Direc * d,int dot)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
Cputrripname(Cdimg * cd,char * nm,int flags,char * name,int dowrite)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
CputrripSL(Cdimg * cd,int contin,int flags,char * name,int dowrite)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
CputrripPX(Cdimg * cd,Direc * d,int dot,int dowrite)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
CputrripTF(Cdimg * cd,Direc * d,int type,int dowrite)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
mode(Direc * d,int dot)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
nlink(Direc * d)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 }
613