xref: /plan9/sys/src/cmd/disk/9660/sysuse.c (revision 73ee67a1775bb9176ad6f77775e030678f092526)
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, vlong 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,vlong woffset,ulong len)32 setcelen(Cdimg *cd, vlong woffset, ulong len)
33 {
34 	vlong 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 	uvlong	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 	uvlong 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 = (vlong)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 	uvlong 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 %llud o+co.len-initlen %llud\n",
311 				Cwoffset(cd), o+co.len-initlen);
312 		assert(Cwoffset(cd) == o+co.len-initlen);
313 	} else
314 		assert(Cwoffset(cd) == o);
315 
316 	assert(co.len <= 255);
317 	return co.len - initlen;
318 }
319 
320 static char SUSPrrip[10] = "RRIP_1991A";
321 static char SUSPdesc[84] = "RRIP <more garbage here>";
322 static char SUSPsrc[135] = "RRIP <more garbage here>";
323 
324 static ulong
CputsuspCE(Cdimg * cd,vlong offset)325 CputsuspCE(Cdimg *cd, vlong offset)
326 {
327 	vlong o, x;
328 
329 	chat("writing SUSP CE record pointing to %ld, %ld\n",
330 		offset/Blocksize, offset%Blocksize);
331 	o = Cwoffset(cd);
332 	Cputc(cd, 'C');
333 	Cputc(cd, 'E');
334 	Cputc(cd, 28);
335 	Cputc(cd, 1);
336 	Cputn(cd, offset/Blocksize, 4);
337 	Cputn(cd, offset%Blocksize, 4);
338 	x = Cwoffset(cd);
339 	Cputn(cd, 0, 4);
340 	assert(Cwoffset(cd) == o+28);
341 
342 	return x;
343 }
344 
345 static int
CputsuspER(Cdimg * cd,int dowrite)346 CputsuspER(Cdimg *cd, int dowrite)
347 {
348 	assert(cd != nil);
349 
350 	if(dowrite) {
351 		chat("writing SUSP ER record\n");
352 		Cputc(cd, 'E');           /* ER field marker */
353 		Cputc(cd, 'R');
354 		Cputc(cd, 26);            /* Length          */
355 		Cputc(cd, 1);             /* Version         */
356 		Cputc(cd, 10);            /* LEN_ID          */
357 		Cputc(cd, 4);             /* LEN_DESC        */
358 		Cputc(cd, 4);             /* LEN_SRC         */
359 		Cputc(cd, 1);             /* EXT_VER         */
360 		Cputs(cd, SUSPrrip, 10);  /* EXT_ID          */
361 		Cputs(cd, SUSPdesc, 4);   /* EXT_DESC        */
362 		Cputs(cd, SUSPsrc, 4);    /* EXT_SRC         */
363 	}
364 	return 8+10+4+4;
365 }
366 
367 static int
CputsuspRR(Cdimg * cd,int what,int dowrite)368 CputsuspRR(Cdimg *cd, int what, int dowrite)
369 {
370 	assert(cd != nil);
371 
372 	if(dowrite) {
373 		Cputc(cd, 'R');           /* RR field marker */
374 		Cputc(cd, 'R');
375 		Cputc(cd, 5);             /* Length          */
376 		Cputc(cd, 1);		  /* Version number  */
377 		Cputc(cd, what);          /* Flags           */
378 	}
379 	return 5;
380 }
381 
382 static int
CputsuspSP(Cdimg * cd,int dowrite)383 CputsuspSP(Cdimg *cd, int dowrite)
384 {
385 	assert(cd!=0);
386 
387 	if(dowrite) {
388 chat("writing SUSP SP record\n");
389 		Cputc(cd, 'S');           /* SP field marker */
390 		Cputc(cd, 'P');
391 		Cputc(cd, 7);             /* Length          */
392 		Cputc(cd, 1);             /* Version         */
393 		Cputc(cd, 0xBE);          /* Magic           */
394 		Cputc(cd, 0xEF);
395 		Cputc(cd, 0);
396 	}
397 
398 	return 7;
399 }
400 
401 #ifdef NOTUSED
402 static int
CputsuspST(Cdimg * cd,int dowrite)403 CputsuspST(Cdimg *cd, int dowrite)
404 {
405 	assert(cd!=0);
406 
407 	if(dowrite) {
408 		Cputc(cd, 'S');           /* ST field marker */
409 		Cputc(cd, 'T');
410 		Cputc(cd, 4);             /* Length          */
411 		Cputc(cd, 1);             /* Version         */
412 	}
413 	return 4;
414 }
415 #endif
416 
417 static ulong
suspdirflags(Direc * d,int dot)418 suspdirflags(Direc *d, int dot)
419 {
420 	uchar flags;
421 
422 	USED(d);
423 	flags = 0;
424 	switch(dot) {
425 	default:
426 		assert(0);
427 	case DTdot:
428 	case DTrootdot:
429 		flags |= NMcurrent;
430 		break;
431 	case DTdotdot:
432 		flags |= NMparent;
433 		break;
434 	case DTroot:
435 		flags |= NMvolroot;
436 		break;
437 	case DTiden:
438 		break;
439 	}
440 	return flags;
441 }
442 
443 static int
Cputrripname(Cdimg * cd,char * nm,int flags,char * name,int dowrite)444 Cputrripname(Cdimg *cd, char *nm, int flags, char *name, int dowrite)
445 {
446 	int l;
447 
448 	l = strlen(name);
449 	if(dowrite) {
450 		Cputc(cd, nm[0]);                   /* NM field marker */
451 		Cputc(cd, nm[1]);
452 		Cputc(cd, l+5);        /* Length          */
453 		Cputc(cd, 1);                     /* Version         */
454 		Cputc(cd, flags);                 /* Flags           */
455 		Cputs(cd, name, l);    /* Alternate name  */
456 	}
457 	return 5+l;
458 }
459 
460 static int
CputrripSL(Cdimg * cd,int contin,int flags,char * name,int dowrite)461 CputrripSL(Cdimg *cd, int contin, int flags, char *name, int dowrite)
462 {
463 	int l;
464 
465 	l = strlen(name);
466 	if(dowrite) {
467 		Cputc(cd, 'S');
468 		Cputc(cd, 'L');
469 		Cputc(cd, l+7);
470 		Cputc(cd, 1);
471 		Cputc(cd, contin ? 1 : 0);
472 		Cputc(cd, flags);
473 		Cputc(cd, l);
474 		Cputs(cd, name, l);
475 	}
476 	return 7+l;
477 }
478 
479 static int
CputrripPX(Cdimg * cd,Direc * d,int dot,int dowrite)480 CputrripPX(Cdimg *cd, Direc *d, int dot, int dowrite)
481 {
482 	assert(cd!=0);
483 
484 	if(dowrite) {
485 		Cputc(cd, 'P');             /* PX field marker */
486 		Cputc(cd, 'X');
487 		Cputc(cd, 36);              /* Length          */
488 		Cputc(cd, 1);               /* Version         */
489 
490 		Cputn(cd, mode(d, dot), 4); /* POSIX File mode */
491 		Cputn(cd, nlink(d), 4);     /* POSIX st_nlink  */
492 		Cputn(cd, d?d->uidno:0, 4);  /* POSIX st_uid    */
493 		Cputn(cd, d?d->gidno:0, 4);  /* POSIX st_gid    */
494 	}
495 
496 	return 36;
497 }
498 
499 static int
CputrripTF(Cdimg * cd,Direc * d,int type,int dowrite)500 CputrripTF(Cdimg *cd, Direc *d, int type, int dowrite)
501 {
502 	int i, length;
503 
504 	assert(cd!=0);
505 	assert(!(type & TFlongform));
506 
507 	length = 0;
508 	for(i=0; i<7; i++)
509 		if (type & (1<<i))
510 			length++;
511 	assert(length == 4);
512 
513 	if(dowrite) {
514 		Cputc(cd, 'T');				/* TF field marker */
515 		Cputc(cd, 'F');
516 		Cputc(cd, 5+7*length);		/* Length		 */
517 		Cputc(cd, 1);				/* Version		 */
518 		Cputc(cd, type);					/* Flags (types)	 */
519 
520 		if (type & TFcreation)
521 			Cputdate(cd, d?d->ctime:0);
522 		if (type & TFmodify)
523 			Cputdate(cd, d?d->mtime:0);
524 		if (type & TFaccess)
525 			Cputdate(cd, d?d->atime:0);
526 		if (type & TFattributes)
527 			Cputdate(cd, d?d->ctime:0);
528 
529 	//	if (type & TFbackup)
530 	//		Cputdate(cd, 0);
531 	//	if (type & TFexpiration)
532 	//		Cputdate(cd, 0);
533 	//	if (type & TFeffective)
534 	//		Cputdate(cd, 0);
535 	}
536 	return 5+7*length;
537 }
538 
539 
540 #define NONPXMODES  (DMDIR | DMAPPEND | DMEXCL | DMMOUNT)
541 #define POSIXMODEMASK (0177777)
542 #ifndef S_IFMT
543 #define S_IFMT  (0170000)
544 #endif
545 #ifndef S_IFDIR
546 #define S_IFDIR (0040000)
547 #endif
548 #ifndef S_IFREG
549 #define S_IFREG (0100000)
550 #endif
551 #ifndef S_IFLNK
552 #define S_IFLNK (0120000)
553 #endif
554 #undef  ISTYPE
555 #define ISTYPE(mode, mask)  (((mode) & S_IFMT) == (mask))
556 #ifndef S_ISDIR
557 #define S_ISDIR(mode) ISTYPE(mode, S_IFDIR)
558 #endif
559 #ifndef S_ISREG
560 #define S_ISREG(mode) ISTYPE(mode, S_IREG)
561 #endif
562 #ifndef S_ISLNK
563 #define S_ISLNK(mode) ISTYPE(mode, S_ILNK)
564 #endif
565 
566 
567 static long
mode(Direc * d,int dot)568 mode(Direc *d, int dot)
569 {
570 	long mode;
571 
572 	if (!d)
573 		return 0;
574 
575 	if ((dot != DTroot) && (dot != DTrootdot)) {
576 		mode = (d->mode & ~(NONPXMODES));
577 		if (d->mode & DMDIR)
578 			mode |= S_IFDIR;
579 		else if (d->mode & CHLINK)
580 			mode |= S_IFLNK;
581 		else
582 			mode |= S_IFREG;
583 	} else
584 		mode = S_IFDIR | (0755);
585 
586 	mode &= POSIXMODEMASK;
587 
588 	/* Botch: not all POSIX types supported yet */
589 	assert(mode & (S_IFDIR|S_IFREG));
590 
591 chat("writing PX record mode field %ulo with dot %d and name \"%s\"\n", mode, dot, d->name);
592 
593 	return mode;
594 }
595 
596 static long
nlink(Direc * d)597 nlink(Direc *d)   /* Trump up the nlink field for POSIX compliance */
598 {
599 	int i;
600 	long n;
601 
602 	if (!d)
603 		return 0;
604 
605 	n = 1;
606 	if (d->mode & DMDIR)   /* One for "." and one more for ".." */
607 		n++;
608 
609 	for(i=0; i<d->nchild; i++)
610 		if (d->child[i].mode & DMDIR)
611 			n++;
612 
613 	return n;
614 }
615 
616