17#include <errno.h> 18#include <unistd.h> 19#include <sys/types.h> 20#include <sys/stat.h> 21#include <fcntl.h> 22#include <limits.h> 23#include "error.h" 24#include "options.h" 25 26#define CTM_STATUS ".ctm_status" 27 28char *piece_dir = NULL; /* Where to store pieces of deltas. */ 29char *delta_dir = NULL; /* Where to store completed deltas. */ 30char *base_dir = NULL; /* The tree to apply deltas to. */ 31int delete_after = 0; /* Delete deltas after ctm applies them. */ 32 33void apply_complete(void); 34int read_piece(char *input_file); 35int combine_if_complete(char *delta, int pce, int npieces); 36int combine(char *delta, int npieces, char *dname, char *pname, char *tname); 37int decode_line(char *line, char *out_buf); 38int lock_file(char *name); 39 40/* 41 * If given a '-p' flag, read encoded delta pieces from stdin or file 42 * arguments, decode them and assemble any completed deltas. If given 43 * a '-b' flag, pass any completed deltas to 'ctm' for application to 44 * the source tree. The '-d' flag is mandatory, but either of '-p' or 45 * '-b' can be omitted. If given the '-l' flag, notes and errors will 46 * be timestamped and written to the given file. 47 * 48 * Exit status is 0 for success or 1 for indigestible input. That is, 49 * 0 means the encode input pieces were decoded and stored, and 1 means 50 * some input was discarded. If a delta fails to apply, this won't be 51 * reflected in the exit status. In this case, the delta is left in 52 * 'deltadir'. 53 */ 54int 55main(int argc, char **argv) 56 { 57 char *log_file = NULL; 58 int status = 0; 59 int fork_ctm = 0; 60 61 err_prog_name(argv[0]); 62 63 OPTIONS("[-Df] [-p piecedir] [-d deltadir] [-b basedir] [-l log] [file ...]") 64 FLAG('D', delete_after) 65 FLAG('f', fork_ctm) 66 STRING('p', piece_dir) 67 STRING('d', delta_dir) 68 STRING('b', base_dir) 69 STRING('l', log_file) 70 ENDOPTS 71 72 if (delta_dir == NULL) 73 usage(); 74 75 if (piece_dir == NULL && (base_dir == NULL || argc > 1)) 76 usage(); 77 78 if (log_file != NULL) 79 err_set_log(log_file); 80 81 /* 82 * Digest each file in turn, or just stdin if no files were given. 83 */ 84 if (argc <= 1) 85 { 86 if (piece_dir != NULL) 87 status = read_piece(NULL); 88 } 89 else 90 { 91 while (*++argv != NULL) 92 status |= read_piece(*argv); 93 } 94 95 /* 96 * Maybe it's time to look for and apply completed deltas with ctm. 97 * 98 * Shall we report back to sendmail immediately, and let a child do 99 * the work? Sendmail will be waiting for us to complete, delaying 100 * other mail, and possibly some intermediate process (like MH slocal) 101 * will terminate us if we take too long! 102 * 103 * If fork() fails, it's unlikely we'll be able to run ctm, so give up. 104 * Also, the child exit status is unimportant. 105 */ 106 if (base_dir != NULL) 107 if (!fork_ctm || fork() == 0) 108 apply_complete(); 109 110 return status; 111 } 112 113 114/* 115 * Construct the file name of a piece of a delta. 116 */ 117#define mk_piece_name(fn,d,p,n) \ 118 sprintf((fn), "%s/%s+%03d-%03d", piece_dir, (d), (p), (n)) 119 120/* 121 * Construct the file name of an assembled delta. 122 */ 123#define mk_delta_name(fn,d) \ 124 sprintf((fn), "%s/%s", delta_dir, (d)) 125 126/* 127 * If the next required delta is now present, let ctm lunch on it and any 128 * contiguous deltas. 129 */ 130void 131apply_complete() 132 { 133 int i, dn; 134 int lfd; 135 FILE *fp, *ctm; 136 struct stat sb; 137 char class[20]; 138 char delta[30]; 139 char junk[2]; 140 char fname[PATH_MAX]; 141 char here[PATH_MAX]; 142 char buf[PATH_MAX*2]; 143 144 /* 145 * Grab a lock on the ctm mutex file so that we can be sure we are 146 * working alone, not fighting another ctm_rmail! 147 */ 148 strcpy(fname, delta_dir); 149 strcat(fname, "/.mutex_apply"); 150 if ((lfd = lock_file(fname)) < 0) 151 return; 152 153 /* 154 * Find out which delta ctm needs next. 155 */ 156 sprintf(fname, "%s/%s", base_dir, CTM_STATUS); 157 if ((fp = fopen(fname, "r")) == NULL) 158 { 159 close(lfd); 160 return; 161 } 162 163 i = fscanf(fp, "%s %d %c", class, &dn, junk); 164 fclose(fp); 165 if (i != 2) 166 { 167 close(lfd); 168 return; 169 } 170 171 /* 172 * We might need to convert the delta filename to an absolute pathname. 173 */ 174 here[0] = '\0'; 175 if (delta_dir[0] != '/') 176 { 177 getcwd(here, sizeof(here)-1); 178 i = strlen(here) - 1; 179 if (i >= 0 && here[i] != '/') 180 { 181 here[++i] = '/'; 182 here[++i] = '\0'; 183 } 184 } 185 186 /* 187 * Keep applying deltas until we run out or something bad happens. 188 */ 189 for (;;) 190 { 191 sprintf(delta, "%s.%04d.gz", class, ++dn); 192 mk_delta_name(fname, delta); 193 194 if (stat(fname, &sb) < 0) 195 break; 196 197 sprintf(buf, "(cd %s && ctm %s%s) 2>&1", base_dir, here, fname); 198 if ((ctm = popen(buf, "r")) == NULL) 199 { 200 err("ctm failed to apply %s", delta); 201 break; 202 } 203 204 while (fgets(buf, sizeof(buf), ctm) != NULL) 205 { 206 i = strlen(buf) - 1; 207 if (i >= 0 && buf[i] == '\n') 208 buf[i] = '\0'; 209 err("ctm: %s", buf); 210 } 211 212 if (pclose(ctm) != 0) 213 { 214 err("ctm failed to apply %s", delta); 215 break; 216 } 217 218 if (delete_after) 219 unlink(fname); 220 221 err("%s applied%s", delta, delete_after ? " and deleted" : ""); 222 } 223 224 /* 225 * Closing the lock file clears the lock. 226 */ 227 close(lfd); 228 } 229 230 231/* 232 * This cheap plastic checksum effectively rotates our checksum-so-far 233 * left one, then adds the character. We only want 16 bits of it, and 234 * don't care what happens to the rest. It ain't much, but it's small. 235 */ 236#define add_ck(sum,x) \ 237 ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0)) 238 239 240/* 241 * Decode the data between BEGIN and END, and stash it in the staging area. 242 * Multiple pieces can be present in a single file, bracketed by BEGIN/END. 243 * If we have all pieces of a delta, combine them. Returns 0 on success, 244 * and 1 for any sort of failure. 245 */ 246int 247read_piece(char *input_file) 248 { 249 int status = 0; 250 FILE *ifp, *ofp = 0; 251 int decoding = 0; 252 int got_one = 0; 253 int line_no = 0; 254 int i, n; 255 int pce, npieces; 256 unsigned claimed_cksum; 257 unsigned short cksum = 0; 258 char out_buf[200]; 259 char line[200]; 260 char delta[30]; 261 char pname[PATH_MAX]; 262 char tname[PATH_MAX]; 263 char junk[2]; 264 265 ifp = stdin; 266 if (input_file != NULL && (ifp = fopen(input_file, "r")) == NULL) 267 { 268 err("cannot open '%s' for reading", input_file); 269 return 1; 270 } 271 272 while (fgets(line, sizeof(line), ifp) != NULL) 273 { 274 line_no++; 275 276 /*
|
284 * Look for the beginning of an encoded piece. 285 */ 286 if (!decoding) 287 { 288 char *s; 289 290 if (sscanf(line, "CTM_MAIL BEGIN %s %d %d %c", 291 delta, &pce, &npieces, junk) != 3) 292 continue; 293 294 while ((s = strchr(delta, '/')) != NULL) 295 *s = '_'; 296 297 got_one++; 298 strcpy(tname, piece_dir); 299 strcat(tname, "/p.XXXXXX"); 300 if (mktemp(tname) == NULL) 301 { 302 err("*mktemp: '%s'", tname); 303 status++; 304 continue; 305 } 306 if ((ofp = fopen(tname, "w")) == NULL) 307 { 308 err("cannot open '%s' for writing", tname); 309 status++; 310 continue; 311 } 312 313 cksum = 0xffff; 314 decoding++; 315 continue; 316 } 317 318 /* 319 * We are decoding. Stop if we see the end flag. 320 */ 321 if (sscanf(line, "CTM_MAIL END %d %c", &claimed_cksum, junk) == 1) 322 { 323 int e; 324 325 decoding = 0; 326 327 fflush(ofp); 328 e = ferror(ofp); 329 fclose(ofp); 330 331 if (e) 332 err("error writing %s", tname); 333 334 if (cksum != claimed_cksum) 335 err("checksum: read %d, calculated %d", claimed_cksum, cksum); 336 337 if (e || cksum != claimed_cksum) 338 { 339 err("%s %d/%d discarded", delta, pce, npieces); 340 unlink(tname); 341 status++; 342 continue; 343 } 344 345 mk_piece_name(pname, delta, pce, npieces); 346 if (rename(tname, pname) < 0) 347 { 348 err("*rename: '%s' to '%s'", tname, pname); 349 err("%s %d/%d lost!", delta, pce, npieces); 350 unlink(tname); 351 status++; 352 continue; 353 } 354 355 err("%s %d/%d stored", delta, pce, npieces); 356 357 if (!combine_if_complete(delta, pce, npieces)) 358 status++; 359 continue; 360 } 361 362 /* 363 * Must be a line of encoded data. Decode it, sum it, and save it. 364 */ 365 n = decode_line(line, out_buf); 366 if (n <= 0) 367 { 368 err("line %d: illegal character: '%c'", line_no, line[-n]); 369 err("%s %d/%d discarded", delta, pce, npieces); 370 371 fclose(ofp); 372 unlink(tname); 373 374 status++; 375 decoding = 0; 376 continue; 377 } 378 379 for (i = 0; i < n; i++) 380 add_ck(cksum, out_buf[i]); 381 382 fwrite(out_buf, sizeof(char), n, ofp); 383 } 384 385 if (decoding) 386 { 387 err("truncated file"); 388 err("%s %d/%d discarded", delta, pce, npieces); 389 390 fclose(ofp); 391 unlink(tname); 392 393 status++; 394 } 395 396 if (ferror(ifp)) 397 { 398 err("error reading %s", input_file == NULL ? "stdin" : input_file); 399 status++; 400 } 401 402 if (input_file != NULL) 403 fclose(ifp); 404 405 if (!got_one) 406 { 407 err("message contains no delta"); 408 status++; 409 } 410 411 return (status != 0); 412 } 413 414 415/* 416 * Put the pieces together to form a delta, if they are all present. 417 * Returns 1 on success (even if we didn't do anything), and 0 on failure. 418 */ 419int 420combine_if_complete(char *delta, int pce, int npieces) 421 { 422 int i, e; 423 int lfd; 424 struct stat sb; 425 char pname[PATH_MAX]; 426 char dname[PATH_MAX]; 427 char tname[PATH_MAX]; 428 429 /* 430 * We can probably just rename() it into place if it is a small delta. 431 */ 432 if (npieces == 1) 433 { 434 mk_delta_name(dname, delta); 435 mk_piece_name(pname, delta, 1, 1); 436 if (rename(pname, dname) == 0) 437 { 438 err("%s complete", delta); 439 return 1; 440 } 441 } 442 443 /* 444 * Grab a lock on the reassembly mutex file so that we can be sure we are 445 * working alone, not fighting another ctm_rmail! 446 */ 447 strcpy(tname, delta_dir); 448 strcat(tname, "/.mutex_build"); 449 if ((lfd = lock_file(tname)) < 0) 450 return 0; 451 452 /* 453 * Are all of the pieces present? Of course the current one is, 454 * unless all pieces are missing because another ctm_rmail has 455 * processed them already. 456 */ 457 for (i = 1; i <= npieces; i++) 458 { 459 if (i == pce) 460 continue; 461 mk_piece_name(pname, delta, i, npieces); 462 if (stat(pname, &sb) < 0) 463 { 464 close(lfd); 465 return 1; 466 } 467 } 468 469 /* 470 * Stick them together. Let combine() use our file name buffers, since 471 * we're such good buddies. :-) 472 */ 473 e = combine(delta, npieces, dname, pname, tname); 474 close(lfd); 475 return e; 476 } 477 478 479/* 480 * Put the pieces together to form a delta. 481 * Returns 1 on success, and 0 on failure. 482 * Note: dname, pname, and tname are room for some file names that just 483 * happened to by lying around in the calling routine. Waste not, want not! 484 */ 485int 486combine(char *delta, int npieces, char *dname, char *pname, char *tname) 487 { 488 FILE *dfp, *pfp; 489 int i, n, e; 490 char buf[BUFSIZ]; 491 492 strcpy(tname, delta_dir); 493 strcat(tname, "/d.XXXXXX"); 494 if (mktemp(tname) == NULL) 495 { 496 err("*mktemp: '%s'", tname); 497 return 0; 498 } 499 if ((dfp = fopen(tname, "w")) == NULL) 500 { 501 err("cannot open '%s' for writing", tname); 502 return 0; 503 } 504 505 /* 506 * Reconstruct the delta by reading each piece in order. 507 */ 508 for (i = 1; i <= npieces; i++) 509 { 510 mk_piece_name(pname, delta, i, npieces); 511 if ((pfp = fopen(pname, "r")) == NULL) 512 { 513 err("cannot open '%s' for reading", pname); 514 fclose(dfp); 515 unlink(tname); 516 return 0; 517 } 518 while ((n = fread(buf, sizeof(char), sizeof(buf), pfp)) != 0) 519 fwrite(buf, sizeof(char), n, dfp); 520 e = ferror(pfp); 521 fclose(pfp); 522 if (e) 523 { 524 err("error reading '%s'", pname); 525 fclose(dfp); 526 unlink(tname); 527 return 0; 528 } 529 } 530 fflush(dfp); 531 e = ferror(dfp); 532 fclose(dfp); 533 if (e) 534 { 535 err("error writing '%s'", tname); 536 unlink(tname); 537 return 0; 538 } 539 540 mk_delta_name(dname, delta); 541 if (rename(tname, dname) < 0) 542 { 543 err("*rename: '%s' to '%s'", tname, dname); 544 unlink(tname); 545 return 0; 546 } 547 548 /* 549 * Throw the pieces away. 550 */ 551 for (i = 1; i <= npieces; i++) 552 { 553 mk_piece_name(pname, delta, i, npieces); 554 if (unlink(pname) < 0) 555 err("*unlink: '%s'", pname); 556 } 557 558 err("%s complete", delta); 559 return 1; 560 } 561 562 563/* 564 * MIME BASE64 decode table. 565 */ 566static unsigned char from_b64[0x80] = 567 { 568 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 569 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 570 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 571 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 572 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 573 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, 574 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 575 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 576 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 577 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 578 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 579 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 580 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 581 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 582 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 583 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff 584 }; 585 586 587/* 588 * Decode a line of ASCII into binary. Returns the number of bytes in 589 * the output buffer, or < 0 on indigestable input. Error output is 590 * the negative of the index of the inedible character. 591 */ 592int 593decode_line(char *line, char *out_buf) 594 { 595 unsigned char *ip = (unsigned char *)line; 596 unsigned char *op = (unsigned char *)out_buf; 597 unsigned long bits; 598 unsigned x; 599 600 for (;;) 601 { 602 if (*ip >= 0x80 || (x = from_b64[*ip]) >= 0x40) 603 break; 604 bits = x << 18; 605 ip++; 606 if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40) 607 { 608 bits |= x << 12; 609 *op++ = bits >> 16; 610 ip++; 611 if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40) 612 { 613 bits |= x << 6; 614 *op++ = bits >> 8; 615 ip++; 616 if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40) 617 { 618 bits |= x; 619 *op++ = bits; 620 ip++; 621 } 622 } 623 } 624 } 625 626 if (*ip == '\0' || *ip == '\n') 627 return op - (unsigned char *)out_buf; 628 else 629 return -(ip - (unsigned char *)line); 630 } 631 632 633/* 634 * Create and lock the given file. 635 * 636 * Clearing the lock is as simple as closing the file descriptor we return. 637 */ 638int 639lock_file(char *name) 640 { 641 int lfd; 642 643 if ((lfd = open(name, O_WRONLY|O_CREAT, 0600)) < 0) 644 { 645 err("*open: '%s'", name); 646 return -1; 647 } 648 if (flock(lfd, LOCK_EX) < 0) 649 { 650 close(lfd); 651 err("*flock: '%s'", name); 652 return -1; 653 } 654 return lfd; 655 }
|