sysuse.c (13953B)
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 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 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 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* 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 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 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 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 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 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 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 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 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 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 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 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 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 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 }