1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <libsec.h> 5 6 #include "iso9660.h" 7 8 static void 9 writelittlebig4(uchar *buf, ulong x) 10 { 11 buf[0] = buf[7] = x; 12 buf[1] = buf[6] = x>>8; 13 buf[2] = buf[5] = x>>16; 14 buf[3] = buf[4] = x>>24; 15 } 16 17 void 18 rewritedot(Cdimg *cd, Direc *d) 19 { 20 uchar buf[Blocksize]; 21 Cdir *c; 22 23 Creadblock(cd, buf, d->block, Blocksize); 24 c = (Cdir*)buf; 25 assert(c->len != 0); 26 assert(c->namelen == 1 && c->name[0] == '\0'); /* dot */ 27 writelittlebig4(c->dloc, d->block); 28 writelittlebig4(c->dlen, d->length); 29 30 Cwseek(cd, (vlong)d->block * Blocksize); 31 Cwrite(cd, buf, Blocksize); 32 } 33 34 void 35 rewritedotdot(Cdimg *cd, Direc *d, Direc *dparent) 36 { 37 uchar buf[Blocksize]; 38 Cdir *c; 39 40 Creadblock(cd, buf, d->block, Blocksize); 41 c = (Cdir*)buf; 42 assert(c->len != 0); 43 assert(c->namelen == 1 && c->name[0] == '\0'); /* dot */ 44 45 c = (Cdir*)(buf+c->len); 46 assert(c->len != 0); 47 assert(c->namelen == 1 && c->name[0] == '\001'); /* dotdot*/ 48 49 writelittlebig4(c->dloc, dparent->block); 50 writelittlebig4(c->dlen, dparent->length); 51 52 Cwseek(cd, (vlong)d->block * Blocksize); 53 Cwrite(cd, buf, Blocksize); 54 } 55 56 /* 57 * Write each non-directory file. We copy the file to 58 * the cd image, and then if it turns out that we've 59 * seen this stream of bits before, we push the next block 60 * pointer back. This ensures consistency between the MD5s 61 * and the data on the CD image. MD5 summing on one pass 62 * and copying on another would not ensure this. 63 */ 64 void 65 writefiles(Dump *d, Cdimg *cd, Direc *direc) 66 { 67 int i; 68 uchar buf[8192], digest[MD5dlen]; 69 ulong length, n, start; 70 Biobuf *b; 71 DigestState *s; 72 Dumpdir *dd; 73 74 if(direc->mode & DMDIR) { 75 for(i=0; i<direc->nchild; i++) 76 writefiles(d, cd, &direc->child[i]); 77 return; 78 } 79 80 assert(direc->block == 0); 81 82 if((b = Bopen(direc->srcfile, OREAD)) == nil){ 83 fprint(2, "warning: cannot open '%s': %r\n", direc->srcfile); 84 direc->block = 0; 85 direc->length = 0; 86 return; 87 } 88 89 start = cd->nextblock; 90 assert(start != 0); 91 if(blocksize && start%blocksize) 92 start += blocksize-start%blocksize; 93 94 Cwseek(cd, (vlong)start * Blocksize); 95 96 s = md5(nil, 0, nil, nil); 97 length = 0; 98 while((n = Bread(b, buf, sizeof buf)) > 0) { 99 md5(buf, n, nil, s); 100 Cwrite(cd, buf, n); 101 length += n; 102 } 103 md5(nil, 0, digest, s); 104 Bterm(b); 105 Cpadblock(cd); 106 107 if(length != direc->length) { 108 fprint(2, "warning: %s changed size underfoot\n", direc->srcfile); 109 direc->length = length; 110 } 111 112 if(length == 0) 113 direc->block = 0; 114 else if((dd = lookupmd5(d, digest))) { 115 assert(dd->length == length); 116 assert(dd->block != 0); 117 direc->block = dd->block; 118 cd->nextblock = start; 119 } else { 120 direc->block = start; 121 if(chatty > 1) 122 fprint(2, "lookup %.16H %lud (%s) failed\n", digest, length, direc->name); 123 insertmd5(d, atom(direc->name), digest, start, length); 124 } 125 } 126 127 /* 128 * Write a directory tree. We work from the leaves, 129 * and patch the dotdot pointers afterward. 130 */ 131 static void 132 _writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level) 133 { 134 int i, l, ll; 135 ulong start, next; 136 137 if((d->mode & DMDIR) == 0) 138 return; 139 140 if(chatty) 141 fprint(2, "%*s%s\n", 4*level, "", d->name); 142 143 for(i=0; i<d->nchild; i++) 144 _writedirs(cd, &d->child[i], put, level+1); 145 146 l = 0; 147 l += put(cd, d, (level == 0) ? DTrootdot : DTdot, 0, l); 148 l += put(cd, nil, DTdotdot, 0, l); 149 for(i=0; i<d->nchild; i++) 150 l += put(cd, &d->child[i], DTiden, 0, l); 151 152 start = cd->nextblock; 153 cd->nextblock += (l+Blocksize-1)/Blocksize; 154 next = cd->nextblock; 155 156 Cwseek(cd, (vlong)start * Blocksize); 157 ll = 0; 158 ll += put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, ll); 159 ll += put(cd, nil, DTdotdot, 1, ll); 160 for(i=0; i<d->nchild; i++) 161 ll += put(cd, &d->child[i], DTiden, 1, ll); 162 assert(ll == l); 163 Cpadblock(cd); 164 assert(Cwoffset(cd) == (vlong)next * Blocksize); 165 166 d->block = start; 167 d->length = (vlong)(next - start) * Blocksize; 168 rewritedot(cd, d); 169 rewritedotdot(cd, d, d); 170 171 for(i=0; i<d->nchild; i++) 172 if(d->child[i].mode & DMDIR) 173 rewritedotdot(cd, &d->child[i], d); 174 } 175 176 void 177 writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int)) 178 { 179 /* 180 * If we're writing a mk9660 image, then the root really 181 * is the root, so start at level 0. If we're writing a dump image, 182 * then the "root" is really going to be two levels down once 183 * we patch in the dump hierarchy above it, so start at level non-zero. 184 */ 185 if(chatty) 186 fprint(2, ">>> writedirs\n"); 187 _writedirs(cd, d, put, mk9660 ? 0 : 1); 188 } 189 190 191 /* 192 * Write the dump tree. This is like writedirs but once we get to 193 * the roots of the individual days we just patch the parent dotdot blocks. 194 */ 195 static void 196 _writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level) 197 { 198 int i; 199 ulong start; 200 201 switch(level) { 202 case 0: 203 /* write root, list of years, also conform.map */ 204 for(i=0; i<d->nchild; i++) 205 if(d->child[i].mode & DMDIR) 206 _writedumpdirs(cd, &d->child[i], put, level+1); 207 chat("write dump root dir at %lud\n", cd->nextblock); 208 goto Writedir; 209 210 case 1: /* write year, list of days */ 211 for(i=0; i<d->nchild; i++) 212 _writedumpdirs(cd, &d->child[i], put, level+1); 213 chat("write dump %s dir at %lud\n", d->name, cd->nextblock); 214 goto Writedir; 215 216 Writedir: 217 start = cd->nextblock; 218 Cwseek(cd, (vlong)start * Blocksize); 219 220 put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, Cwoffset(cd)); 221 put(cd, nil, DTdotdot, 1, Cwoffset(cd)); 222 for(i=0; i<d->nchild; i++) 223 put(cd, &d->child[i], DTiden, 1, Cwoffset(cd)); 224 Cpadblock(cd); 225 226 d->block = start; 227 d->length = (vlong)(cd->nextblock - start) * Blocksize; 228 229 rewritedot(cd, d); 230 rewritedotdot(cd, d, d); 231 232 for(i=0; i<d->nchild; i++) 233 if(d->child[i].mode & DMDIR) 234 rewritedotdot(cd, &d->child[i], d); 235 break; 236 237 case 2: /* write day: already written, do nothing */ 238 break; 239 240 default: 241 assert(0); 242 } 243 } 244 245 void 246 writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int)) 247 { 248 _writedumpdirs(cd, d, put, 0); 249 } 250 251 static int 252 Cputplan9(Cdimg *cd, Direc *d, int dot, int dowrite) 253 { 254 int l, n; 255 256 if(dot != DTiden) 257 return 0; 258 259 l = 0; 260 if(d->flags & Dbadname) { 261 n = strlen(d->name); 262 l += 1+n; 263 if(dowrite) { 264 Cputc(cd, n); 265 Cputs(cd, d->name, n); 266 } 267 } else { 268 l++; 269 if(dowrite) 270 Cputc(cd, 0); 271 } 272 273 n = strlen(d->uid); 274 l += 1+n; 275 if(dowrite) { 276 Cputc(cd, n); 277 Cputs(cd, d->uid, n); 278 } 279 280 n = strlen(d->gid); 281 l += 1+n; 282 if(dowrite) { 283 Cputc(cd, n); 284 Cputs(cd, d->gid, n); 285 } 286 287 if(l & 1) { 288 l++; 289 if(dowrite) 290 Cputc(cd, 0); 291 } 292 l += 8; 293 if(dowrite) 294 Cputn(cd, d->mode, 4); 295 296 return l; 297 } 298 299 /* 300 * Write a directory entry. 301 */ 302 static int 303 genputdir(Cdimg *cd, Direc *d, int dot, int joliet, int dowrite, int offset) 304 { 305 int f, n, l, lp; 306 vlong o; 307 308 f = 0; 309 if(dot != DTiden || (d->mode & DMDIR)) 310 f |= 2; 311 312 n = 1; 313 if(dot == DTiden) { 314 if(joliet) 315 n = 2*utflen(d->confname); 316 else 317 n = strlen(d->confname); 318 } 319 320 l = 33+n; 321 if(l & 1) 322 l++; 323 assert(l <= 255); 324 325 if(joliet == 0) { 326 if(cd->flags & CDplan9) 327 l += Cputplan9(cd, d, dot, 0); 328 else if(cd->flags & CDrockridge) 329 l += Cputsysuse(cd, d, dot, 0, l); 330 assert(l <= 255); 331 } 332 333 if(dowrite == 0) { 334 if(Blocksize - offset%Blocksize < l) 335 l += Blocksize - offset%Blocksize; 336 return l; 337 } 338 339 assert(offset%Blocksize == Cwoffset(cd)%Blocksize); 340 341 o = Cwoffset(cd); 342 lp = 0; 343 if(Blocksize - Cwoffset(cd)%Blocksize < l) { 344 lp = Blocksize - Cwoffset(cd)%Blocksize; 345 Cpadblock(cd); 346 } 347 348 Cputc(cd, l); /* length of directory record */ 349 Cputc(cd, 0); /* extended attribute record length */ 350 if(d) { 351 if((d->mode & DMDIR) == 0) 352 assert(d->length == 0 || d->block >= 18); 353 354 Cputn(cd, d->block, 4); /* location of extent */ 355 Cputn(cd, d->length, 4); /* data length */ 356 } else { 357 Cputn(cd, 0, 4); 358 Cputn(cd, 0, 4); 359 } 360 Cputdate(cd, d ? d->mtime : now); /* recorded date */ 361 Cputc(cd, f); /* file flags */ 362 Cputc(cd, 0); /* file unit size */ 363 Cputc(cd, 0); /* interleave gap size */ 364 Cputn(cd, 1, 2); /* volume sequence number */ 365 Cputc(cd, n); /* length of file identifier */ 366 367 if(dot == DTiden) { /* identifier */ 368 if(joliet) 369 Cputrscvt(cd, d->confname, n); 370 else 371 Cputs(cd, d->confname, n); 372 }else 373 if(dot == DTdotdot) 374 Cputc(cd, 1); 375 else 376 Cputc(cd, 0); 377 378 if(Cwoffset(cd) & 1) /* pad */ 379 Cputc(cd, 0); 380 381 if(joliet == 0) { 382 if(cd->flags & CDplan9) 383 Cputplan9(cd, d, dot, 1); 384 else if(cd->flags & CDrockridge) 385 Cputsysuse(cd, d, dot, 1, Cwoffset(cd)-(o+lp)); 386 } 387 388 assert(o+lp+l == Cwoffset(cd)); 389 return lp+l; 390 } 391 392 int 393 Cputisodir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset) 394 { 395 return genputdir(cd, d, dot, 0, dowrite, offset); 396 } 397 398 int 399 Cputjolietdir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset) 400 { 401 return genputdir(cd, d, dot, 1, dowrite, offset); 402 } 403 404 void 405 Cputendvd(Cdimg *cd) 406 { 407 Cputc(cd, 255); /* volume descriptor set terminator */ 408 Cputs(cd, "CD001", 5); /* standard identifier */ 409 Cputc(cd, 1); /* volume descriptor version */ 410 Cpadblock(cd); 411 } 412