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