]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/disk/9660/sysuse.c
fix real cause of iso name truncation
[plan9front.git] / sys / src / cmd / disk / 9660 / sysuse.c
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
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
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         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*
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         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
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
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
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
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
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
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
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
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
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
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
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
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