1/*- 2 * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29#include <assert.h> 30#include <errno.h> 31#include <stdlib.h> 32#include <string.h> 33#include <stdio.h> 34 35#include <sys/types.h> 36#include <sys/stat.h> 37#include <unistd.h> 38 39#include "config.h" 40#include "detailer.h" 41#include "fixups.h" 42#include "globtree.h" 43#include "misc.h" 44#include "mux.h" 45#include "proto.h" 46#include "rcsfile.h" 47#include "rsyncfile.h" 48#include "status.h" 49#include "stream.h" 50 51/* Internal error codes. */ 52#define DETAILER_ERR_PROTO (-1) /* Protocol error. */ 53#define DETAILER_ERR_MSG (-2) /* Error is in detailer->errmsg. */ 54#define DETAILER_ERR_READ (-3) /* Error reading from server. */ 55#define DETAILER_ERR_WRITE (-4) /* Error writing to server. */ 56 57struct detailer { 58 struct config *config; 59 struct stream *rd; 60 struct stream *wr; 61 char *errmsg; 62}; 63 64static int detailer_batch(struct detailer *); 65static int detailer_coll(struct detailer *, struct coll *, 66 struct status *); 67static int detailer_dofile_co(struct detailer *, struct coll *, 68 struct status *, char *); 69static int detailer_dofile_rcs(struct detailer *, struct coll *, 70 char *, char *); 71static int detailer_dofile_regular(struct detailer *, char *, char *); 72static int detailer_dofile_rsync(struct detailer *, char *, char *); 73static int detailer_checkrcsattr(struct detailer *, struct coll *, char *, 74 struct fattr *, int); 75int detailer_send_details(struct detailer *, struct coll *, char *, 76 char *, struct fattr *); 77 78void * 79detailer(void *arg) 80{ 81 struct thread_args *args; 82 struct detailer dbuf, *d; 83 int error; 84 85 args = arg; 86 87 d = &dbuf; 88 d->config = args->config; 89 d->rd = args->rd; 90 d->wr = args->wr; 91 d->errmsg = NULL; 92 93 error = detailer_batch(d); 94 switch (error) { 95 case DETAILER_ERR_PROTO: 96 xasprintf(&args->errmsg, "Detailer failed: Protocol error"); 97 args->status = STATUS_FAILURE; 98 break; 99 case DETAILER_ERR_MSG: 100 xasprintf(&args->errmsg, "Detailer failed: %s", d->errmsg); 101 free(d->errmsg); 102 args->status = STATUS_FAILURE; 103 break; 104 case DETAILER_ERR_READ: 105 if (stream_eof(d->rd)) { 106 xasprintf(&args->errmsg, "Detailer failed: " 107 "Premature EOF from server"); 108 } else { 109 xasprintf(&args->errmsg, "Detailer failed: " 110 "Network read failure: %s", strerror(errno)); 111 } 112 args->status = STATUS_TRANSIENTFAILURE; 113 break; 114 case DETAILER_ERR_WRITE: 115 xasprintf(&args->errmsg, "Detailer failed: " 116 "Network write failure: %s", strerror(errno)); 117 args->status = STATUS_TRANSIENTFAILURE; 118 break; 119 default: 120 assert(error == 0); 121 args->status = STATUS_SUCCESS; 122 } 123 return (NULL); 124} 125 126static int 127detailer_batch(struct detailer *d) 128{ 129 struct config *config; 130 struct stream *rd, *wr; 131 struct coll *coll; 132 struct status *st; 133 struct fixup *fixup; 134 char *cmd, *collname, *line, *release; 135 int error, fixupseof; 136 137 config = d->config; 138 rd = d->rd; 139 wr = d->wr; 140 STAILQ_FOREACH(coll, &config->colls, co_next) { 141 if (coll->co_options & CO_SKIP) 142 continue; 143 line = stream_getln(rd, NULL); 144 cmd = proto_get_ascii(&line); 145 collname = proto_get_ascii(&line); 146 release = proto_get_ascii(&line); 147 error = proto_get_time(&line, &coll->co_scantime); 148 if (error || line != NULL || strcmp(cmd, "COLL") != 0 || 149 strcmp(collname, coll->co_name) != 0 || 150 strcmp(release, coll->co_release) != 0) 151 return (DETAILER_ERR_PROTO); 152 error = proto_printf(wr, "COLL %s %s\n", coll->co_name, 153 coll->co_release); 154 if (error) 155 return (DETAILER_ERR_WRITE); 156 stream_flush(wr); 157 if (coll->co_options & CO_COMPRESS) { 158 stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL); 159 stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL); 160 } 161 st = status_open(coll, -1, &d->errmsg); 162 if (st == NULL) 163 return (DETAILER_ERR_MSG); 164 error = detailer_coll(d, coll, st); 165 status_close(st, NULL); 166 if (error) 167 return (error); 168 if (coll->co_options & CO_COMPRESS) { 169 stream_filter_stop(rd); 170 stream_filter_stop(wr); 171 } 172 stream_flush(wr); 173 } 174 line = stream_getln(rd, NULL); 175 if (line == NULL) 176 return (DETAILER_ERR_READ); 177 if (strcmp(line, ".") != 0) 178 return (DETAILER_ERR_PROTO); 179 error = proto_printf(wr, ".\n"); 180 if (error) 181 return (DETAILER_ERR_WRITE); 182 stream_flush(wr); 183 184 /* Now send fixups if needed. */ 185 fixup = NULL; 186 fixupseof = 0; 187 STAILQ_FOREACH(coll, &config->colls, co_next) { 188 if (coll->co_options & CO_SKIP) 189 continue; 190 error = proto_printf(wr, "COLL %s %s\n", coll->co_name, 191 coll->co_release); 192 if (error) 193 return (DETAILER_ERR_WRITE); 194 if (coll->co_options & CO_COMPRESS) 195 stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL); 196 while (!fixupseof) { 197 if (fixup == NULL) 198 fixup = fixups_get(config->fixups); 199 if (fixup == NULL) { 200 fixupseof = 1; 201 break; 202 } 203 if (fixup->f_coll != coll) 204 break; 205 if (coll->co_options & CO_CHECKOUTMODE) 206 error = proto_printf(wr, "Y %s %s %s\n", 207 fixup->f_name, coll->co_tag, coll->co_date); 208 else { 209 error = proto_printf(wr, "A %s\n", 210 fixup->f_name); 211 } 212 if (error) 213 return (DETAILER_ERR_WRITE); 214 fixup = NULL; 215 } 216 error = proto_printf(wr, ".\n"); 217 if (error) 218 return (DETAILER_ERR_WRITE); 219 if (coll->co_options & CO_COMPRESS) 220 stream_filter_stop(wr); 221 stream_flush(wr); 222 } 223 error = proto_printf(wr, ".\n"); 224 if (error) 225 return (DETAILER_ERR_WRITE); 226 return (0); 227} 228 229static int 230detailer_coll(struct detailer *d, struct coll *coll, struct status *st) 231{ 232 struct fattr *rcsattr; 233 struct stream *rd, *wr; 234 char *attr, *cmd, *file, *line, *msg, *path, *target; 235 int error, attic; 236 237 rd = d->rd; 238 wr = d->wr; 239 attic = 0; 240 line = stream_getln(rd, NULL); 241 if (line == NULL) 242 return (DETAILER_ERR_READ); 243 while (strcmp(line, ".") != 0) { 244 cmd = proto_get_ascii(&line); 245 if (cmd == NULL || strlen(cmd) != 1) 246 return (DETAILER_ERR_PROTO); 247 switch (cmd[0]) { 248 case 'D': 249 /* Delete file. */ 250 file = proto_get_ascii(&line); 251 if (file == NULL || line != NULL) 252 return (DETAILER_ERR_PROTO); 253 error = proto_printf(wr, "D %s\n", file); 254 if (error) 255 return (DETAILER_ERR_WRITE); 256 break; 257 case 'I': 258 case 'i': 259 case 'j': 260 /* Directory operations. */ 261 file = proto_get_ascii(&line); 262 if (file == NULL || line != NULL) 263 return (DETAILER_ERR_PROTO); 264 error = proto_printf(wr, "%s %s\n", cmd, file); 265 if (error) 266 return (DETAILER_ERR_WRITE); 267 break; 268 case 'J': 269 /* Set directory attributes. */ 270 file = proto_get_ascii(&line); 271 attr = proto_get_ascii(&line); 272 if (file == NULL || line != NULL || attr == NULL) 273 return (DETAILER_ERR_PROTO); 274 error = proto_printf(wr, "%s %s %s\n", cmd, file, attr); 275 if (error) 276 return (DETAILER_ERR_WRITE); 277 break; 278 case 'H': 279 case 'h': 280 /* Create a hard link. */ 281 file = proto_get_ascii(&line); 282 target = proto_get_ascii(&line); 283 if (file == NULL || target == NULL) 284 return (DETAILER_ERR_PROTO); 285 error = proto_printf(wr, "%s %s %s\n", cmd, file, 286 target); 287 break; 288 case 't': 289 file = proto_get_ascii(&line); 290 attr = proto_get_ascii(&line); 291 if (file == NULL || attr == NULL || line != NULL) { 292 return (DETAILER_ERR_PROTO); 293 } 294 rcsattr = fattr_decode(attr); 295 if (rcsattr == NULL) { 296 return (DETAILER_ERR_PROTO); 297 } 298 error = detailer_checkrcsattr(d, coll, file, rcsattr, 299 1); 300 break; 301 302 case 'T': 303 file = proto_get_ascii(&line); 304 attr = proto_get_ascii(&line); 305 if (file == NULL || attr == NULL || line != NULL) 306 return (DETAILER_ERR_PROTO); 307 rcsattr = fattr_decode(attr); 308 if (rcsattr == NULL) 309 return (DETAILER_ERR_PROTO); 310 error = detailer_checkrcsattr(d, coll, file, rcsattr, 311 0); 312 break; 313 314 case 'U': 315 /* Add or update file. */ 316 file = proto_get_ascii(&line); 317 if (file == NULL || line != NULL) 318 return (DETAILER_ERR_PROTO); 319 if (coll->co_options & CO_CHECKOUTMODE) { 320 error = detailer_dofile_co(d, coll, st, file); 321 } else { 322 path = cvspath(coll->co_prefix, file, 0); 323 rcsattr = fattr_frompath(path, FATTR_NOFOLLOW); 324 error = detailer_send_details(d, coll, file, 325 path, rcsattr); 326 if (rcsattr != NULL) 327 fattr_free(rcsattr); 328 free(path); 329 } 330 if (error) 331 return (error); 332 break; 333 case '!': 334 /* Warning from server. */ 335 msg = proto_get_rest(&line); 336 if (msg == NULL) 337 return (DETAILER_ERR_PROTO); 338 lprintf(-1, "Server warning: %s\n", msg); 339 break; 340 default: 341 return (DETAILER_ERR_PROTO); 342 } 343 stream_flush(wr); 344 line = stream_getln(rd, NULL); 345 if (line == NULL) 346 return (DETAILER_ERR_READ); 347 } 348 error = proto_printf(wr, ".\n"); 349 if (error) 350 return (DETAILER_ERR_WRITE); 351 return (0); 352} 353 354/* 355 * Tell the server to update a regular file. 356 */ 357static int 358detailer_dofile_regular(struct detailer *d, char *name, char *path) 359{ 360 struct stream *wr; 361 struct stat st; 362 char md5[MD5_DIGEST_SIZE]; 363 int error; 364 365 wr = d->wr; 366 error = stat(path, &st); 367 /* If we don't have it or it's unaccessible, we want it again. */ 368 if (error) { 369 proto_printf(wr, "A %s\n", name); 370 return (0); 371 } 372 373 /* If not, we want the file to be updated. */ 374 error = MD5_File(path, md5); 375 if (error) { 376 lprintf(-1, "Error reading \"%s\"\n", name); 377 return (error); 378 } 379 error = proto_printf(wr, "R %s %O %s\n", name, st.st_size, md5); 380 if (error) 381 return (DETAILER_ERR_WRITE); 382 return (0); 383} 384 385/* 386 * Tell the server to update a file with the rsync algorithm. 387 */ 388static int 389detailer_dofile_rsync(struct detailer *d, char *name, char *path) 390{ 391 struct stream *wr; 392 struct rsyncfile *rf; 393 394 wr = d->wr; 395 rf = rsync_open(path, 0, 1); 396 if (rf == NULL) { 397 /* Fallback if we fail in opening it. */ 398 proto_printf(wr, "A %s\n", name); 399 return (0); 400 } 401 proto_printf(wr, "r %s %z %z\n", name, rsync_filesize(rf), 402 rsync_blocksize(rf)); 403 /* Detail the blocks. */ 404 while (rsync_nextblock(rf) != 0) 405 proto_printf(wr, "%s %s\n", rsync_rsum(rf), rsync_blockmd5(rf)); 406 proto_printf(wr, ".\n"); 407 rsync_close(rf); 408 return (0); 409} 410 411/* 412 * Tell the server to update an RCS file that we have, or send it if we don't. 413 */ 414static int 415detailer_dofile_rcs(struct detailer *d, struct coll *coll, char *name, 416 char *path) 417{ 418 struct stream *wr; 419 struct fattr *fa; 420 struct rcsfile *rf; 421 int error; 422 423 wr = d->wr; 424 path = atticpath(coll->co_prefix, name); 425 fa = fattr_frompath(path, FATTR_NOFOLLOW); 426 if (fa == NULL) { 427 /* We don't have it, so send request to get it. */ 428 error = proto_printf(wr, "A %s\n", name); 429 if (error) 430 return (DETAILER_ERR_WRITE); 431 free(path); 432 return (0); 433 } 434 435 rf = rcsfile_frompath(path, name, coll->co_cvsroot, coll->co_tag, 1); 436 free(path); 437 if (rf == NULL) { 438 error = proto_printf(wr, "A %s\n", name); 439 if (error) 440 return (DETAILER_ERR_WRITE); 441 return (0); 442 } 443 /* Tell to update the RCS file. The client version details follow. */ 444 rcsfile_send_details(rf, wr); 445 rcsfile_free(rf); 446 fattr_free(fa); 447 return (0); 448} 449 450static int 451detailer_dofile_co(struct detailer *d, struct coll *coll, struct status *st, 452 char *file) 453{ 454 struct stream *wr; 455 struct fattr *fa; 456 struct statusrec *sr; 457 char md5[MD5_DIGEST_SIZE]; 458 char *path; 459 int error, ret; 460 461 wr = d->wr; 462 path = checkoutpath(coll->co_prefix, file); 463 if (path == NULL) 464 return (DETAILER_ERR_PROTO); 465 fa = fattr_frompath(path, FATTR_NOFOLLOW); 466 if (fa == NULL) { 467 /* We don't have the file, so the only option at this 468 point is to tell the server to send it. The server 469 may figure out that the file is dead, in which case 470 it will tell us. */ 471 error = proto_printf(wr, "C %s %s %s\n", 472 file, coll->co_tag, coll->co_date); 473 free(path); 474 if (error) 475 return (DETAILER_ERR_WRITE); 476 return (0); 477 } 478 ret = status_get(st, file, 0, 0, &sr); 479 if (ret == -1) { 480 d->errmsg = status_errmsg(st); 481 free(path); 482 return (DETAILER_ERR_MSG); 483 } 484 if (ret == 0) 485 sr = NULL; 486 487 /* If our recorded information doesn't match the file that the 488 client has, then ignore the recorded information. */ 489 if (sr != NULL && (sr->sr_type != SR_CHECKOUTLIVE || 490 !fattr_equal(sr->sr_clientattr, fa))) 491 sr = NULL; 492 fattr_free(fa); 493 if (sr != NULL && strcmp(sr->sr_revdate, ".") != 0) { 494 error = proto_printf(wr, "U %s %s %s %s %s\n", file, 495 coll->co_tag, coll->co_date, sr->sr_revnum, sr->sr_revdate); 496 free(path); 497 if (error) 498 return (DETAILER_ERR_WRITE); 499 return (0); 500 } 501 502 /* 503 * We don't have complete and/or accurate recorded information 504 * about what version of the file we have. Compute the file's 505 * checksum as an aid toward identifying which version it is. 506 */ 507 error = MD5_File(path, md5); 508 if (error) { 509 xasprintf(&d->errmsg, 510 "Cannot calculate checksum for \"%s\": %s", path, 511 strerror(errno)); 512 return (DETAILER_ERR_MSG); 513 } 514 free(path); 515 if (sr == NULL) { 516 error = proto_printf(wr, "S %s %s %s %s\n", file, 517 coll->co_tag, coll->co_date, md5); 518 } else { 519 error = proto_printf(wr, "s %s %s %s %s %s\n", file, 520 coll->co_tag, coll->co_date, sr->sr_revnum, md5); 521 } 522 if (error) 523 return (DETAILER_ERR_WRITE); 524 return (0); 525} 526 527int 528detailer_checkrcsattr(struct detailer *d, struct coll *coll, char *name, 529 struct fattr *server_attr, int attic) 530{ 531 struct fattr *client_attr; 532 char *attr, *path; 533 int error; 534 535 /* 536 * I don't think we can use the status file, since it only records file 537 * attributes in cvsmode. 538 */ 539 client_attr = NULL; 540 path = cvspath(coll->co_prefix, name, attic); 541 if (path == NULL) { 542 return (DETAILER_ERR_PROTO); 543 } 544 545 if (access(path, F_OK) == 0 && 546 ((client_attr = fattr_frompath(path, FATTR_NOFOLLOW)) != NULL) && 547 fattr_equal(client_attr, server_attr)) { 548 attr = fattr_encode(client_attr, NULL, 0); 549 if (attic) { 550 error = proto_printf(d->wr, "l %s %s\n", name, attr); 551 } else { 552 error = proto_printf(d->wr, "L %s %s\n", name, attr); 553 } 554 free(attr); 555 free(path); 556 fattr_free(client_attr); 557 if (error) 558 return (DETAILER_ERR_WRITE); 559 return (0); 560 } 561 /* We don't have it, so tell the server to send it. */ 562 error = detailer_send_details(d, coll, name, path, client_attr); 563 fattr_free(client_attr); 564 free(path); 565 return (error); 566} 567 568int 569detailer_send_details(struct detailer *d, struct coll *coll, char *name, 570 char *path, struct fattr *fa) 571{ 572 int error; 573 size_t len; 574 575 /* 576 * Try to check if the file exists either live or dead to see if we can 577 * edit it and put it live or dead, rather than receiving the entire 578 * file. 579 */ 580 if (fa == NULL) { 581 path = atticpath(coll->co_prefix, name); 582 fa = fattr_frompath(path, FATTR_NOFOLLOW); 583 } 584 if (fa == NULL) { 585 error = proto_printf(d->wr, "A %s\n", name); 586 if (error) 587 return (DETAILER_ERR_WRITE); 588 } else if (fattr_type(fa) == FT_FILE) { 589 if (isrcs(name, &len) && !(coll->co_options & CO_NORCS)) { 590 detailer_dofile_rcs(d, coll, name, path); 591 } else if (!(coll->co_options & CO_NORSYNC) && 592 !globtree_test(coll->co_norsync, name)) { 593 detailer_dofile_rsync(d, name, path); 594 } else { 595 detailer_dofile_regular(d, name, path); 596 } 597 } else { 598 error = proto_printf(d->wr, "N %s\n", name); 599 if (error) 600 return (DETAILER_ERR_WRITE); 601 } 602 return (0); 603} 604