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