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, 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, 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 92 Cwseek(cd, start*Blocksize); 93 94 s = md5(nil, 0, nil, nil); 95 length = 0; 96 while((n = Bread(b, buf, sizeof buf)) > 0) { 97 md5(buf, n, nil, s); 98 Cwrite(cd, buf, n); 99 length += n; 100 } 101 md5(nil, 0, digest, s); 102 Bterm(b); 103 Cpadblock(cd); 104 105 if(length != direc->length) { 106 fprint(2, "warning: %s changed size underfoot\n", direc->srcfile); 107 direc->length = length; 108 } 109 110 if(length == 0) 111 direc->block = 0; 112 else if((dd = lookupmd5(d, digest))) { 113 assert(dd->length == length); 114 assert(dd->block != 0); 115 direc->block = dd->block; 116 cd->nextblock = start; 117 } else { 118 direc->block = start; 119 insertmd5(d, atom(direc->name), digest, start, length); 120 } 121 } 122 123 /* 124 * Write a directory tree. We work from the leaves, 125 * and patch the dotdot pointers afterward. 126 */ 127 static void 128 _writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level) 129 { 130 int i, l, ll; 131 ulong start, next; 132 133 if((d->mode & DMDIR) == 0) 134 return; 135 136 for(i=0; i<d->nchild; i++) 137 writedirs(cd, &d->child[i], put); 138 139 l = 0; 140 l += put(cd, d, (level == 0) ? DTrootdot : DTdot, 0, l); 141 l += put(cd, nil, DTdotdot, 0, l); 142 for(i=0; i<d->nchild; i++) 143 l += put(cd, &d->child[i], DTiden, 0, l); 144 145 start = cd->nextblock; 146 cd->nextblock += (l+Blocksize-1)/Blocksize; 147 next = cd->nextblock; 148 149 Cwseek(cd, start*Blocksize); 150 ll = 0; 151 ll += put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, ll); 152 ll += put(cd, nil, DTdotdot, 1, ll); 153 for(i=0; i<d->nchild; i++) 154 ll += put(cd, &d->child[i], DTiden, 1, ll); 155 assert(ll == l); 156 Cpadblock(cd); 157 assert(Cwoffset(cd) == next*Blocksize); 158 159 d->block = start; 160 d->length = (next - start) * Blocksize; 161 rewritedot(cd, d); 162 rewritedotdot(cd, d, d); 163 164 for(i=0; i<d->nchild; i++) 165 if(d->child[i].mode & DMDIR) 166 rewritedotdot(cd, &d->child[i], d); 167 } 168 169 void 170 writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int)) 171 { 172 /* 173 * If we're writing a mk9660 image, then the root really 174 * is the root, so start at level 0. If we're writing a dump image, 175 * then the "root" is really going to be two levels down once 176 * we patch in the dump hierarchy above it, so start at level non-zero. 177 */ 178 _writedirs(cd, d, put, mk9660 ? 0 : 1); 179 } 180 181 182 /* 183 * Write the dump tree. This is like writedirs but once we get to 184 * the roots of the individual days we just patch the parent dotdot blocks. 185 */ 186 static void 187 _writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level) 188 { 189 int i; 190 ulong start; 191 192 switch(level) { 193 case 0: 194 /* write root, list of years, also conform.map */ 195 for(i=0; i<d->nchild; i++) 196 if(d->child[i].mode & DMDIR) 197 _writedumpdirs(cd, &d->child[i], put, level+1); 198 chat("write dump root dir at %lud\n", cd->nextblock); 199 goto Writedir; 200 201 case 1: /* write year, list of days */ 202 for(i=0; i<d->nchild; i++) 203 _writedumpdirs(cd, &d->child[i], put, level+1); 204 chat("write dump %s dir at %lud\n", d->name, cd->nextblock); 205 goto Writedir; 206 207 Writedir: 208 start = cd->nextblock; 209 Cwseek(cd, start*Blocksize); 210 211 put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, Cwoffset(cd)); 212 put(cd, nil, DTdotdot, 1, Cwoffset(cd)); 213 for(i=0; i<d->nchild; i++) 214 put(cd, &d->child[i], DTiden, 1, Cwoffset(cd)); 215 Cpadblock(cd); 216 217 d->block = start; 218 d->length = (cd->nextblock - start) * Blocksize; 219 220 rewritedot(cd, d); 221 rewritedotdot(cd, d, d); 222 223 for(i=0; i<d->nchild; i++) 224 if(d->child[i].mode & DMDIR) 225 rewritedotdot(cd, &d->child[i], d); 226 break; 227 228 case 2: /* write day: already written, do nothing */ 229 break; 230 231 default: 232 assert(0); 233 } 234 } 235 236 void 237 writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int)) 238 { 239 _writedumpdirs(cd, d, put, 0); 240 } 241 242 static int 243 Cputplan9(Cdimg *cd, Direc *d, int dot, int dowrite) 244 { 245 int l, n; 246 247 if(dot != DTiden) 248 return 0; 249 250 l = 0; 251 if(d->flags & Dbadname) { 252 n = strlen(d->name); 253 l += 1+n; 254 if(dowrite) { 255 Cputc(cd, n); 256 Cputs(cd, d->name, n); 257 } 258 } else { 259 l++; 260 if(dowrite) 261 Cputc(cd, 0); 262 } 263 264 n = strlen(d->uid); 265 l += 1+n; 266 if(dowrite) { 267 Cputc(cd, n); 268 Cputs(cd, d->uid, n); 269 } 270 271 n = strlen(d->gid); 272 l += 1+n; 273 if(dowrite) { 274 Cputc(cd, n); 275 Cputs(cd, d->gid, n); 276 } 277 278 if(l & 1) { 279 l++; 280 if(dowrite) 281 Cputc(cd, 0); 282 } 283 l += 8; 284 if(dowrite) 285 Cputn(cd, d->mode, 4); 286 287 return l; 288 } 289 290 /* 291 * Write a directory entry. 292 */ 293 static int 294 genputdir(Cdimg *cd, Direc *d, int dot, int joliet, int dowrite, int offset) 295 { 296 int f, n, l, lp; 297 long o; 298 299 f = 0; 300 if(dot != DTiden || (d->mode & DMDIR)) 301 f |= 2; 302 303 n = 1; 304 if(dot == DTiden) { 305 if(joliet) 306 n = 2*utflen(d->confname); 307 else 308 n = strlen(d->confname); 309 } 310 311 l = 33+n; 312 if(l & 1) 313 l++; 314 assert(l <= 255); 315 316 if(joliet == 0) { 317 if(cd->flags & CDplan9) 318 l += Cputplan9(cd, d, dot, 0); 319 else if(cd->flags & CDrockridge) 320 l += Cputsysuse(cd, d, dot, 0, l); 321 assert(l <= 255); 322 } 323 324 if(dowrite == 0) { 325 if(Blocksize - offset%Blocksize < l) 326 l += Blocksize - offset%Blocksize; 327 return l; 328 } 329 330 assert(offset%Blocksize == Cwoffset(cd)%Blocksize); 331 332 o = Cwoffset(cd); 333 lp = 0; 334 if(Blocksize - Cwoffset(cd)%Blocksize < l) { 335 lp = Blocksize - Cwoffset(cd)%Blocksize; 336 Cpadblock(cd); 337 } 338 339 Cputc(cd, l); /* length of directory record */ 340 Cputc(cd, 0); /* extended attribute record length */ 341 if(d) { 342 if((d->mode & DMDIR) == 0) 343 assert(d->length == 0 || d->block >= 18); 344 345 Cputn(cd, d->block, 4); /* location of extent */ 346 Cputn(cd, d->length, 4); /* data length */ 347 } else { 348 Cputn(cd, 0, 4); 349 Cputn(cd, 0, 4); 350 } 351 Cputdate(cd, d ? d->mtime : now); /* recorded date */ 352 Cputc(cd, f); /* file flags */ 353 Cputc(cd, 0); /* file unit size */ 354 Cputc(cd, 0); /* interleave gap size */ 355 Cputn(cd, 1, 2); /* volume sequence number */ 356 Cputc(cd, n); /* length of file identifier */ 357 358 if(dot == DTiden) { /* identifier */ 359 if(joliet) 360 Cputrscvt(cd, d->confname, n); 361 else 362 Cputs(cd, d->confname, n); 363 }else 364 if(dot == DTdotdot) 365 Cputc(cd, 1); 366 else 367 Cputc(cd, 0); 368 369 if(Cwoffset(cd) & 1) /* pad */ 370 Cputc(cd, 0); 371 372 if(joliet == 0) { 373 if(cd->flags & CDplan9) 374 Cputplan9(cd, d, dot, 1); 375 else if(cd->flags & CDrockridge) 376 Cputsysuse(cd, d, dot, 1, Cwoffset(cd)-(o+lp)); 377 } 378 379 assert(o+lp+l == Cwoffset(cd)); 380 return lp+l; 381 } 382 383 int 384 Cputisodir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset) 385 { 386 return genputdir(cd, d, dot, 0, dowrite, offset); 387 } 388 389 int 390 Cputjolietdir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset) 391 { 392 return genputdir(cd, d, dot, 1, dowrite, offset); 393 } 394 395 void 396 Cputendvd(Cdimg *cd) 397 { 398 Cputc(cd, 255); /* volume descriptor set terminator */ 399 Cputs(cd, "CD001", 5); /* standard identifier */ 400 Cputc(cd, 1); /* volume descriptor version */ 401 Cpadblock(cd); 402 } 403