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 616