1 /* $OpenBSD: client.c,v 1.21 2009/02/15 22:20:54 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 1983 Regents of the University of California. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include "defs.h" 33 #include "y.tab.h" 34 35 #ifndef lint 36 #if 0 37 static char RCSid[] __attribute__((__unused__)) = 38 "$From: client.c,v 1.13 1999/11/01 00:22:14 christos Exp $"; 39 #else 40 static char RCSid[] __attribute__((__unused__)) = 41 "$OpenBSD: client.c,v 1.21 2009/02/15 22:20:54 deraadt Exp $"; 42 #endif 43 44 static char sccsid[] __attribute__((__unused__)) = 45 "@(#)client.c"; 46 47 static char copyright[] __attribute__((__unused__)) = 48 "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 49 All rights reserved.\n"; 50 #endif /* not lint */ 51 52 /* 53 * Routines used in client mode to communicate with remove server. 54 */ 55 56 57 /* 58 * Update status 59 */ 60 #define US_NOTHING 0 /* No update needed */ 61 #define US_NOENT 1 /* Entry does not exist */ 62 #define US_OUTDATE 2 /* Entry is out of date */ 63 #define US_DOCOMP 3 /* Do a binary comparison */ 64 #define US_CHMOG 4 /* Modes or ownership of file differ */ 65 66 struct linkbuf *ihead = NULL; /* list of files with more than one link */ 67 char buf[BUFSIZ]; /* general purpose buffer */ 68 u_char respbuff[BUFSIZ]; /* Response buffer */ 69 char target[BUFSIZ]; /* target/source directory name */ 70 char source[BUFSIZ]; /* source directory name */ 71 char *ptarget; /* pointer to end of target name */ 72 char *Tdest; /* pointer to last T dest*/ 73 struct namelist *updfilelist = NULL; /* List of updated files */ 74 75 static void runspecial(char *, opt_t, char *, int); 76 static void addcmdspecialfile(char *, char *, int); 77 static void freecmdspecialfiles(void); 78 static struct linkbuf *linkinfo(struct stat *); 79 static int sendhardlink(opt_t, struct linkbuf *, char *, int); 80 static int sendfile(char *, opt_t, struct stat *, char *, char *, int); 81 static int rmchk(opt_t); 82 static int senddir(char *, opt_t, struct stat *, char *, char *, int); 83 static int sendlink(char *, opt_t, struct stat *, char *, char *, int); 84 static int update(char *, opt_t, struct stat *); 85 static int dostat(char *, struct stat *, opt_t); 86 static int statupdate(int, char *, opt_t, char *, int, struct stat *, char *, char *); 87 static int fullupdate(int, char *, opt_t, char *, int, struct stat *, char *, char *); 88 static int sendit(char *, opt_t, int); 89 90 /* 91 * return remote file pathname (relative from target) 92 */ 93 char * 94 remfilename(char *src, char *dest, char *path, char *rname, int destdir) 95 { 96 extern struct namelist *filelist; 97 char *lname, *cp; 98 static char buff[BUFSIZ]; 99 int srclen, pathlen; 100 char *p; 101 102 103 debugmsg(DM_MISC, 104 "remfilename: src=%s dest=%s path=%s rname=%s destdir=%d\n", 105 A(src), A(dest), A(path), A(rname), destdir); 106 107 if (!dest) { 108 debugmsg(DM_MISC, "remfilename: remote filename=%s\n", path); 109 return(path); 110 } 111 112 if (!destdir) { 113 debugmsg(DM_MISC, "remfilename: remote filename=%s\n", dest); 114 return(dest); 115 } 116 117 buff[0] = CNULL; 118 lname = buff; 119 if (path && *path) { 120 cp = strrchr(path, '/'); 121 if (cp == NULL) 122 (void) snprintf(buff, sizeof(buff), "%s/%s", dest, path); 123 else { 124 srclen = strlen(src); 125 pathlen = strlen(path); 126 if (srclen >= pathlen) 127 cp++; /* xbasename(path) */ 128 else { 129 if (filelist && filelist->n_next == NULL) 130 /* path relative to src */ 131 cp = path + srclen; 132 else { 133 if ((p = strrchr(src, '/'))) 134 cp = path + srclen - strlen(p); 135 else 136 cp = path; 137 } 138 } 139 if ((*cp != '/') && *cp) 140 (void) snprintf(buff, sizeof(buff), "%s/%s", 141 dest, cp); 142 else 143 (void) snprintf(buff, sizeof(buff), "%s%s", 144 dest, cp); 145 } 146 } else 147 (void) strlcpy(lname, dest, buf + sizeof buff - lname); 148 149 debugmsg(DM_MISC, "remfilename: remote filename=%s\n", lname); 150 151 return(lname); 152 } 153 154 /* 155 * Return true if name is in the list. 156 */ 157 int 158 inlist(struct namelist *list, char *file) 159 { 160 struct namelist *nl; 161 162 for (nl = list; nl != NULL; nl = nl->n_next) 163 if (strcmp(file, nl->n_name) == 0) 164 return(1); 165 return(0); 166 } 167 168 /* 169 * Run any special commands for this file 170 */ 171 static void 172 runspecial(char *starget, opt_t opts, char *rname, int destdir) 173 { 174 struct subcmd *sc; 175 extern struct subcmd *subcmds; 176 char *rfile; 177 178 rfile = remfilename(source, Tdest, target, rname, destdir); 179 180 for (sc = subcmds; sc != NULL; sc = sc->sc_next) { 181 if (sc->sc_type != SPECIAL) 182 continue; 183 if (sc->sc_args != NULL && !inlist(sc->sc_args, starget)) 184 continue; 185 message(MT_CHANGE, "special \"%s\"", sc->sc_name); 186 if (IS_ON(opts, DO_VERIFY)) 187 continue; 188 (void) sendcmd(C_SPECIAL, 189 "%s=%s;%s=%s;%s=%s;export %s %s %s;%s", 190 E_LOCFILE, starget, 191 E_REMFILE, rfile, 192 E_BASEFILE, xbasename(rfile), 193 E_LOCFILE, E_REMFILE, E_BASEFILE, 194 sc->sc_name); 195 while (response() > 0) 196 ; 197 } 198 } 199 200 /* 201 * If we're doing a target with a "cmdspecial" in it, then 202 * save the name of the file being updated for use with "cmdspecial". 203 */ 204 static void 205 addcmdspecialfile(char *starget, char *rname, int destdir) 206 { 207 char *rfile; 208 struct namelist *new; 209 struct subcmd *sc; 210 extern struct subcmd *subcmds; 211 int isokay = 0; 212 213 rfile = remfilename(source, Tdest, target, rname, destdir); 214 215 for (sc = subcmds; sc != NULL && !isokay; sc = sc->sc_next) { 216 if (sc->sc_type != CMDSPECIAL) 217 continue; 218 if (sc->sc_args != NULL && !inlist(sc->sc_args, starget)) 219 continue; 220 isokay = TRUE; 221 } 222 223 if (isokay) { 224 new = (struct namelist *) xmalloc(sizeof(struct namelist)); 225 new->n_name = xstrdup(rfile); 226 new->n_regex = NULL; 227 new->n_next = updfilelist; 228 updfilelist = new; 229 } 230 } 231 232 /* 233 * Free the file list 234 */ 235 static void 236 freecmdspecialfiles(void) 237 { 238 struct namelist *ptr, *save; 239 240 for (ptr = updfilelist; ptr; ) { 241 if (ptr->n_name) (void) free(ptr->n_name); 242 save = ptr->n_next; 243 (void) free(ptr); 244 if (save) 245 ptr = save->n_next; 246 else 247 ptr = NULL; 248 } 249 updfilelist = NULL; 250 } 251 252 /* 253 * Run commands for an entire cmd 254 */ 255 void 256 runcmdspecial(struct cmd *cmd, opt_t opts) 257 { 258 struct subcmd *sc; 259 struct namelist *f; 260 int first = TRUE; 261 262 for (sc = cmd->c_cmds; sc != NULL; sc = sc->sc_next) { 263 if (sc->sc_type != CMDSPECIAL) 264 continue; 265 message(MT_CHANGE, "cmdspecial \"%s\"", sc->sc_name); 266 if (IS_ON(opts, DO_VERIFY)) 267 continue; 268 /* Send all the file names */ 269 for (f = updfilelist; f != NULL; f = f->n_next) { 270 if (first) { 271 (void) sendcmd(C_CMDSPECIAL, NULL); 272 if (response() < 0) 273 return; 274 first = FALSE; 275 } 276 (void) sendcmd(RC_FILE, f->n_name); 277 if (response() < 0) 278 return; 279 } 280 if (first) { 281 (void) sendcmd(C_CMDSPECIAL, NULL); 282 if (response() < 0) 283 return; 284 first = FALSE; 285 } 286 /* Send command to run and wait for it to complete */ 287 (void) sendcmd(RC_COMMAND, sc->sc_name); 288 while (response() > 0) 289 ; 290 first = TRUE; /* Reset in case there are more CMDSPECIAL's */ 291 } 292 freecmdspecialfiles(); 293 } 294 295 /* 296 * For security, reject filenames that contains a newline 297 */ 298 int 299 checkfilename(char *name) 300 { 301 char *cp; 302 303 if (strchr(name, '\n')) { 304 for (cp = name; *cp; cp++) 305 if (*cp == '\n') 306 *cp = '?'; 307 message(MT_NERROR, 308 "Refuse to handle filename containing newline: %s", 309 name); 310 return(-1); 311 } 312 313 return(0); 314 } 315 316 void 317 freelinkinfo(struct linkbuf *lp) 318 { 319 if (lp->pathname) 320 free(lp->pathname); 321 if (lp->src) 322 free(lp->src); 323 if (lp->target) 324 free(lp->target); 325 free(lp); 326 } 327 328 /* 329 * Save and retrieve hard link info 330 */ 331 static struct linkbuf * 332 linkinfo(struct stat *statp) 333 { 334 struct linkbuf *lp; 335 336 /* XXX - linear search doesn't scale with many links */ 337 for (lp = ihead; lp != NULL; lp = lp->nextp) 338 if (lp->inum == statp->st_ino && lp->devnum == statp->st_dev) { 339 lp->count--; 340 return(lp); 341 } 342 343 lp = (struct linkbuf *) xmalloc(sizeof(*lp)); 344 lp->nextp = ihead; 345 ihead = lp; 346 lp->inum = statp->st_ino; 347 lp->devnum = statp->st_dev; 348 lp->count = statp->st_nlink - 1; 349 lp->pathname = xstrdup(target); 350 lp->src = xstrdup(source); 351 if (Tdest) 352 lp->target = xstrdup(Tdest); 353 else 354 lp->target = NULL; 355 356 return(NULL); 357 } 358 359 /* 360 * Send a hardlink 361 */ 362 static int 363 sendhardlink(opt_t opts, struct linkbuf *lp, char *rname, int destdir) 364 { 365 static char buff[MAXPATHLEN]; 366 char *lname; /* name of file to link to */ 367 char ername[MAXPATHLEN*4], elname[MAXPATHLEN*4]; 368 369 debugmsg(DM_MISC, 370 "sendhardlink: rname='%s' pathname='%s' src='%s' target='%s'\n", 371 rname, lp->pathname ? lp->pathname : "", 372 lp->src ? lp->src : "", lp->target ? lp->target : ""); 373 374 if (lp->target == NULL) 375 lname = lp->pathname; 376 else { 377 lname = buff; 378 strlcpy(lname, remfilename(lp->src, lp->target, 379 lp->pathname, rname, 380 destdir), sizeof(buff)); 381 debugmsg(DM_MISC, "sendhardlink: lname=%s\n", lname); 382 } 383 ENCODE(elname, lname); 384 ENCODE(ername, rname); 385 (void) sendcmd(C_RECVHARDLINK, "%o %s %s", 386 opts, elname, ername); 387 388 return(response()); 389 } 390 391 /* 392 * Send a file 393 */ 394 static int 395 sendfile(char *rname, opt_t opts, struct stat *stb, char *user, 396 char *group, int destdir) 397 { 398 int goterr, f; 399 off_t i; 400 char ername[MAXPATHLEN*4]; 401 402 if (stb->st_nlink > 1) { 403 struct linkbuf *lp; 404 405 if ((lp = linkinfo(stb)) != NULL) 406 return(sendhardlink(opts, lp, rname, destdir)); 407 } 408 409 if ((f = open(target, O_RDONLY)) < 0) { 410 error("%s: open for read failed: %s", target, SYSERR); 411 return(-1); 412 } 413 414 /* 415 * Send file info 416 */ 417 ENCODE(ername, rname); 418 419 (void) sendcmd(C_RECVREG, "%o %04o %ld %ld %ld %s %s %s", 420 opts, stb->st_mode & 07777, (long) stb->st_size, 421 stb->st_mtime, stb->st_atime, 422 user, group, ername); 423 if (response() < 0) { 424 (void) close(f); 425 return(-1); 426 } 427 428 429 debugmsg(DM_MISC, "Send file '%s' %ld bytes\n", rname, 430 (long) stb->st_size); 431 432 /* 433 * Set remote time out alarm handler. 434 */ 435 (void) signal(SIGALRM, sighandler); 436 437 /* 438 * Actually transfer the file 439 */ 440 goterr = 0; 441 for (i = 0; i < stb->st_size; i += BUFSIZ) { 442 off_t amt = BUFSIZ; 443 444 (void) alarm(rtimeout); 445 if (i + amt > stb->st_size) 446 amt = stb->st_size - i; 447 if (read(f, buf, (size_t) amt) != (ssize_t) amt) { 448 error("%s: File changed size", target); 449 err(); 450 ++goterr; 451 /* 452 * XXX - We have to keep going because the 453 * server expects to receive a fixed number 454 * of bytes that we specified as the file size. 455 * We need Out Of Band communication to handle 456 * this situation gracefully. 457 */ 458 } 459 if (xwrite(rem_w, buf, (size_t) amt) < 0) { 460 error("%s: Error writing to client: %s", 461 target, SYSERR); 462 err(); 463 ++goterr; 464 break; 465 } 466 (void) alarm(0); 467 } 468 469 (void) alarm(0); /* Insure alarm is off */ 470 (void) close(f); 471 472 debugmsg(DM_MISC, "Send file '%s' %s.\n", 473 (goterr) ? "failed" : "complete", rname); 474 475 /* 476 * Check for errors and end send 477 */ 478 if (goterr) 479 return(-1); 480 else { 481 ack(); 482 f = response(); 483 if (f < 0) 484 return(-1); 485 else if (f == 0 && IS_ON(opts, DO_COMPARE)) 486 return(0); 487 488 runspecial(target, opts, rname, destdir); 489 addcmdspecialfile(target, rname, destdir); 490 491 return(0); 492 } 493 } 494 495 /* 496 * Check for files on the machine being updated that are not on the master 497 * machine and remove them. 498 * 499 * Return < 0 on error. 500 * Return 0 if nothing happened. 501 * Return > 0 if anything is updated. 502 */ 503 static int 504 rmchk(opt_t opts) 505 { 506 u_char *s; 507 struct stat stb; 508 int didupdate = 0; 509 int n; 510 char targ[MAXPATHLEN*4]; 511 512 debugmsg(DM_CALL, "rmchk()\n"); 513 514 /* 515 * Tell the remote to clean the files from the last directory sent. 516 */ 517 (void) sendcmd(C_CLEAN, "%o", IS_ON(opts, DO_VERIFY)); 518 if (response() < 0) 519 return(-1); 520 521 for ( ; ; ) { 522 n = remline(s = respbuff, sizeof(respbuff), TRUE); 523 if (n <= 0) { 524 error("rmchk: unexpected control record"); 525 return(didupdate); 526 } 527 528 switch (*s++) { 529 case CC_QUERY: /* Query if file should be removed */ 530 /* 531 * Return the following codes to remove query. 532 * CC_NO -- file exists - DON'T remove. 533 * CC_YES -- file doesn't exist - REMOVE. 534 */ 535 if (DECODE(targ, (char *) s) == -1) { 536 error("rmchk: cannot decode file"); 537 return(-1); 538 } 539 (void) snprintf(ptarget, 540 sizeof(target) - (ptarget - target), 541 "%s%s", 542 (ptarget[-1] == '/' ? "" : "/"), 543 targ); 544 debugmsg(DM_MISC, "check %s\n", target); 545 if (except(target)) 546 (void) sendcmd(CC_NO, NULL); 547 else if (lstat(target, &stb) < 0) { 548 if (sendcmd(CC_YES, NULL) == 0) 549 didupdate = 1; 550 } else 551 (void) sendcmd(CC_NO, NULL); 552 break; 553 554 case CC_END: 555 *ptarget = CNULL; 556 ack(); 557 return(didupdate); 558 559 case C_LOGMSG: 560 if (n > 0) 561 message(MT_INFO, "%s", s); 562 break; 563 564 case C_NOTEMSG: 565 if (n > 0) 566 message(MT_NOTICE, "%s", s); 567 break; 568 /* Goto top of loop */ 569 570 case C_ERRMSG: 571 message(MT_NERROR, "%s", s); 572 return(didupdate); 573 574 case C_FERRMSG: 575 message(MT_FERROR, "%s", s); 576 finish(); 577 578 default: 579 error("rmchk: unexpected response '%s'", respbuff); 580 err(); 581 } 582 } 583 /*NOTREACHED*/ 584 } 585 586 /* 587 * Send a directory 588 * 589 * Return < 0 on error. 590 * Return 0 if nothing happened. 591 * Return > 0 if anything is updated. 592 */ 593 static int 594 senddir(char *rname, opt_t opts, struct stat *stb, char *user, 595 char *group, int destdir) 596 { 597 DIRENTRY *dp; 598 DIR *d; 599 char *optarget, *cp; 600 int len; 601 int didupdate = 0; 602 char ername[MAXPATHLEN*4]; 603 604 /* 605 * Send recvdir command in recvit() format. 606 */ 607 ENCODE(ername, rname); 608 (void) sendcmd(C_RECVDIR, "%o %04o 0 0 0 %s %s %s", 609 opts, stb->st_mode & 07777, user, group, ername); 610 if (response() < 0) 611 return(-1); 612 613 /* 614 * Don't descend into directory 615 */ 616 if (IS_ON(opts, DO_NODESCEND)) 617 return(0); 618 619 if (IS_ON(opts, DO_REMOVE)) 620 if (rmchk(opts) > 0) 621 ++didupdate; 622 623 if ((d = opendir(target)) == NULL) { 624 error("%s: opendir failed: %s", target, SYSERR); 625 return(-1); 626 } 627 628 optarget = ptarget; 629 len = ptarget - target; 630 while ((dp = readdir(d)) != NULL) { 631 if (!strcmp(dp->d_name, ".") || 632 !strcmp(dp->d_name, "..")) 633 continue; 634 if (len + 1 + (int) strlen(dp->d_name) >= MAXPATHLEN - 1) { 635 error("%s/%s: Name too long", target, 636 dp->d_name); 637 continue; 638 } 639 ptarget = optarget; 640 if (ptarget[-1] != '/') 641 *ptarget++ = '/'; 642 cp = dp->d_name; 643 while ((*ptarget++ = *cp++) != '\0') 644 continue; 645 ptarget--; 646 if (sendit(dp->d_name, opts, destdir) > 0) 647 didupdate = 1; 648 } 649 (void) closedir(d); 650 651 (void) sendcmd(C_END, NULL); 652 (void) response(); 653 654 ptarget = optarget; 655 *ptarget = CNULL; 656 657 return(didupdate); 658 } 659 660 /* 661 * Send a link 662 */ 663 static int 664 sendlink(char *rname, opt_t opts, struct stat *stb, char *user, 665 char *group, int destdir) 666 { 667 int f, n; 668 static char tbuf[BUFSIZ]; 669 char lbuf[MAXPATHLEN]; 670 u_char *s; 671 char ername[MAXPATHLEN*4]; 672 673 debugmsg(DM_CALL, "sendlink(%s, %x, stb, %d)\n", rname, opts, destdir); 674 675 if (stb->st_nlink > 1) { 676 struct linkbuf *lp; 677 678 if ((lp = linkinfo(stb)) != NULL) 679 return(sendhardlink(opts, lp, rname, destdir)); 680 } 681 682 /* 683 * Gather and send basic link info 684 */ 685 ENCODE(ername, rname); 686 (void) sendcmd(C_RECVSYMLINK, "%o %04o %ld %ld %ld %s %s %s", 687 opts, stb->st_mode & 07777, (long) stb->st_size, 688 stb->st_mtime, stb->st_atime, 689 user, group, ername); 690 if (response() < 0) 691 return(-1); 692 693 /* 694 * Gather and send additional link info 695 */ 696 if ((n = readlink(target, lbuf, sizeof(lbuf)-1)) != -1) 697 lbuf[n] = '\0'; 698 else { 699 error("%s: readlink failed", target); 700 err(); 701 } 702 (void) snprintf(tbuf, sizeof(tbuf), "%.*s", (int) stb->st_size, lbuf); 703 ENCODE(ername, tbuf); 704 (void) sendcmd(C_NONE, "%s\n", ername); 705 706 if (n != stb->st_size) { 707 error("%s: file changed size", target); 708 err(); 709 } else 710 ack(); 711 712 /* 713 * Check response 714 */ 715 f = response(); 716 if (f < 0) 717 return(-1); 718 else if (f == 0 && IS_ON(opts, DO_COMPARE)) 719 return(0); 720 721 /* 722 * Read and process responses from server. 723 * The server may send multiple messages regarding 724 * file deletes if the remote target is a directory. 725 */ 726 for (;;) { 727 n = remline(s = respbuff, sizeof(respbuff), TRUE); 728 if (n == -1) /* normal EOF */ 729 return(0); 730 if (n == 0) { 731 error("expected control record"); 732 continue; 733 } 734 735 switch (*s++) { 736 case C_END: /* End of send operation */ 737 *ptarget = CNULL; 738 ack(); 739 runspecial(target, opts, rname, destdir); 740 addcmdspecialfile(target, rname, destdir); 741 return(0); 742 743 case C_LOGMSG: 744 if (n > 0) 745 message(MT_INFO, "%s", s); 746 break; 747 748 case C_NOTEMSG: 749 if (n > 0) 750 message(MT_NOTICE, "%s", s); 751 break; 752 /* Goto top of loop */ 753 754 case C_ERRMSG: 755 message(MT_NERROR, "%s", s); 756 return(-1); 757 758 case C_FERRMSG: 759 message(MT_FERROR, "%s", s); 760 finish(); 761 762 default: 763 error("install link: unexpected response '%s'", 764 respbuff); 765 err(); 766 } 767 } 768 /*NOTREACHED*/ 769 } 770 771 /* 772 * Check to see if file needs to be updated on the remote machine. 773 * Returns: 774 * US_NOTHING - no update 775 * US_NOENT - remote doesn't exist 776 * US_OUTDATE - out of date 777 * US_DOCOMP - comparing binaries to determine if out of date 778 * US_CHMOG - File modes or ownership do not match 779 */ 780 static int 781 update(char *rname, opt_t opts, struct stat *statp) 782 { 783 off_t size; 784 time_t mtime; 785 unsigned short lmode; 786 unsigned short rmode; 787 char *owner = NULL, *group = NULL; 788 int done, n; 789 u_char *cp; 790 char ername[MAXPATHLEN*4]; 791 792 debugmsg(DM_CALL, "update(%s, 0x%x, 0x%x)\n", rname, opts, statp); 793 794 switch (statp->st_mode & S_IFMT) { 795 case S_IFBLK: 796 debugmsg(DM_MISC, "%s is a block special; skipping\n", target); 797 return(US_NOTHING); 798 case S_IFCHR: 799 debugmsg(DM_MISC, "%s is a character special; skipping\n", 800 target); 801 return(US_NOTHING); 802 case S_IFIFO: 803 debugmsg(DM_MISC, "%s is a fifo; skipping\n", target); 804 return(US_NOTHING); 805 case S_IFSOCK: 806 debugmsg(DM_MISC, "%s is a socket; skipping\n", target); 807 return(US_NOTHING); 808 } 809 810 if (IS_ON(opts, DO_NOEXEC)) 811 if (isexec(target, statp)) { 812 debugmsg(DM_MISC, "%s is an executable\n", target); 813 return(US_NOTHING); 814 } 815 816 /* 817 * Check to see if the file exists on the remote machine. 818 */ 819 ENCODE(ername, rname); 820 (void) sendcmd(C_QUERY, "%s", ername); 821 822 for (done = 0; !done;) { 823 n = remline(cp = respbuff, sizeof(respbuff), TRUE); 824 if (n <= 0) { 825 error("update: unexpected control record in response to query"); 826 return(US_NOTHING); 827 } 828 829 switch (*cp++) { 830 case QC_ONNFS: /* Resides on a NFS */ 831 debugmsg(DM_MISC, 832 "update: %s is on a NFS. Skipping...\n", 833 rname); 834 return(US_NOTHING); 835 836 case QC_SYM: /* Is a symbolic link */ 837 debugmsg(DM_MISC, 838 "update: %s is a symlink. Skipping...\n", 839 rname); 840 return(US_NOTHING); 841 842 case QC_ONRO: /* Resides on a Read-Only fs */ 843 debugmsg(DM_MISC, 844 "update: %s is on a RO fs. Skipping...\n", 845 rname); 846 return(US_NOTHING); 847 848 case QC_YES: 849 done = 1; 850 break; 851 852 case QC_NO: /* file doesn't exist so install it */ 853 return(US_NOENT); 854 855 case C_ERRMSG: 856 if (cp) 857 message(MT_NERROR, "%s", cp); 858 return(US_NOTHING); 859 860 case C_FERRMSG: 861 if (cp) 862 message(MT_FERROR, "%s", cp); 863 finish(); 864 865 case C_NOTEMSG: 866 if (cp) 867 message(MT_NOTICE, "%s", cp); 868 break; 869 /* Goto top of loop */ 870 871 default: 872 error("update: unexpected response to query '%s'", respbuff); 873 return(US_NOTHING); 874 } 875 } 876 877 /* 878 * Target exists, but no other info passed 879 */ 880 if (n <= 1 || !S_ISREG(statp->st_mode)) 881 return(US_OUTDATE); 882 883 if (IS_ON(opts, DO_COMPARE)) 884 return(US_DOCOMP); 885 886 /* 887 * Parse size 888 */ 889 size = (off_t) strtol(cp, (char **)&cp, 10); 890 if (*cp++ != ' ') { 891 error("update: size not delimited"); 892 return(US_NOTHING); 893 } 894 895 /* 896 * Parse mtime 897 */ 898 mtime = strtol(cp, (char **)&cp, 10); 899 if (*cp++ != ' ') { 900 error("update: mtime not delimited"); 901 return(US_NOTHING); 902 } 903 904 /* 905 * Parse remote file mode 906 */ 907 rmode = strtol(cp, (char **)&cp, 8); 908 if (cp && *cp) 909 ++cp; 910 911 /* 912 * Be backwards compatible 913 */ 914 if (cp && *cp != CNULL) { 915 /* 916 * Parse remote file owner 917 */ 918 owner = strtok((char *)cp, " "); 919 if (owner == NULL) { 920 error("update: owner not delimited"); 921 return(US_NOTHING); 922 } 923 924 /* 925 * Parse remote file group 926 */ 927 group = strtok(NULL, " "); 928 if (group == NULL) { 929 error("update: group not delimited"); 930 return(US_NOTHING); 931 } 932 } 933 934 /* 935 * File needs to be updated? 936 */ 937 lmode = statp->st_mode & 07777; 938 939 debugmsg(DM_MISC, "update(%s,) local mode %04o remote mode %04o\n", 940 rname, lmode, rmode); 941 debugmsg(DM_MISC, "update(%s,) size %ld mtime %d owner '%s' grp '%s'\n", 942 rname, (long) size, mtime, owner, group); 943 944 if (statp->st_mtime != mtime) { 945 if (statp->st_mtime < mtime && IS_ON(opts, DO_YOUNGER)) { 946 message(MT_WARNING, 947 "%s: Warning: remote copy is newer", 948 target); 949 return(US_NOTHING); 950 } 951 return(US_OUTDATE); 952 } 953 954 if (statp->st_size != size) { 955 debugmsg(DM_MISC, "size does not match (%ld != %ld).\n", 956 (long) statp->st_size, (long) size); 957 return(US_OUTDATE); 958 } 959 960 if (!IS_ON(opts, DO_NOCHKMODE) && lmode != rmode) { 961 debugmsg(DM_MISC, "modes do not match (%04o != %04o).\n", 962 lmode, rmode); 963 return(US_CHMOG); 964 } 965 966 967 /* 968 * Check ownership 969 */ 970 if (!IS_ON(opts, DO_NOCHKOWNER) && owner) { 971 if (!IS_ON(opts, DO_NUMCHKOWNER)) { 972 /* Check by string compare */ 973 if (strcmp(owner, getusername(statp->st_uid, 974 target, opts)) != 0) { 975 debugmsg(DM_MISC, 976 "owner does not match (%s != %s).\n", 977 getusername(statp->st_uid, 978 target, opts), owner); 979 return(US_CHMOG); 980 } 981 } else { 982 /* 983 * Check numerically. 984 * Allow negative numbers. 985 */ 986 while (*owner && !isdigit((unsigned char)*owner) && 987 (*owner != '-')) 988 ++owner; 989 if (owner && (UID_T) atoi(owner) != statp->st_uid) { 990 debugmsg(DM_MISC, 991 "owner does not match (%d != %s).\n", 992 statp->st_uid, owner); 993 return(US_CHMOG); 994 } 995 } 996 } 997 998 if (!IS_ON(opts, DO_NOCHKGROUP) && group) { 999 if (!IS_ON(opts, DO_NUMCHKGROUP)) { 1000 /* Check by string compare */ 1001 if (strcmp(group, getgroupname(statp->st_gid, 1002 target, opts)) != 0) { 1003 debugmsg(DM_MISC, 1004 "group does not match (%s != %s).\n", 1005 getgroupname(statp->st_gid, 1006 target, opts), group); 1007 return(US_CHMOG); 1008 } 1009 } else { 1010 /* Check numerically */ 1011 /* Allow negative gid */ 1012 while (*group && !isdigit((unsigned char) *group) && 1013 (*group != '-')) 1014 ++group; 1015 if (group && (UID_T) atoi(group) != statp->st_gid) { 1016 debugmsg(DM_MISC, 1017 "group does not match (%d != %s).\n", 1018 statp->st_gid, group); 1019 return(US_CHMOG); 1020 } 1021 } 1022 } 1023 1024 return(US_NOTHING); 1025 } 1026 1027 /* 1028 * Stat a file 1029 */ 1030 static int 1031 dostat(char *file, struct stat *statbuf, opt_t opts) 1032 { 1033 int s; 1034 1035 if (IS_ON(opts, DO_FOLLOW)) 1036 s = stat(file, statbuf); 1037 else 1038 s = lstat(file, statbuf); 1039 1040 if (s < 0) 1041 error("%s: %s failed: %s", file, 1042 IS_ON(opts, DO_FOLLOW) ? "stat" : "lstat", SYSERR); 1043 return(s); 1044 } 1045 1046 /* 1047 * We need to just change file info. 1048 */ 1049 static int 1050 statupdate(int u, char *target, opt_t opts, char *rname, int destdir, 1051 struct stat *st, char *user, char *group) 1052 { 1053 int rv = 0; 1054 char ername[MAXPATHLEN*4]; 1055 int lmode = st->st_mode & 07777; 1056 1057 if (u == US_CHMOG) { 1058 if (IS_ON(opts, DO_VERIFY)) { 1059 message(MT_INFO, 1060 "%s: need to change to perm %04o, owner %s, group %s", 1061 target, lmode, user, group); 1062 runspecial(target, opts, rname, destdir); 1063 } 1064 else { 1065 message(MT_CHANGE, "%s: change to perm %04o, owner %s, group %s", 1066 target, lmode, user, group); 1067 ENCODE(ername, rname); 1068 (void) sendcmd(C_CHMOG, "%o %04o %s %s %s", 1069 opts, lmode, user, group, ername); 1070 (void) response(); 1071 } 1072 rv = 1; 1073 } 1074 return(rv); 1075 } 1076 1077 1078 /* 1079 * We need to install/update: 1080 */ 1081 static int 1082 fullupdate(int u, char *target, opt_t opts, char *rname, int destdir, 1083 struct stat *st, char *user, char *group) 1084 { 1085 /* 1086 * No entry - need to install 1087 */ 1088 if (u == US_NOENT) { 1089 if (IS_ON(opts, DO_VERIFY)) { 1090 message(MT_INFO, "%s: need to install", target); 1091 runspecial(target, opts, rname, destdir); 1092 return(1); 1093 } 1094 if (!IS_ON(opts, DO_QUIET)) 1095 message(MT_CHANGE, "%s: installing", target); 1096 FLAG_OFF(opts, (DO_COMPARE|DO_REMOVE)); 1097 } 1098 1099 /* 1100 * Handle special file types, including directories and symlinks 1101 */ 1102 if (S_ISDIR(st->st_mode)) { 1103 if (senddir(rname, opts, st, user, group, destdir) > 0) 1104 return(1); 1105 return(0); 1106 } else if (S_ISLNK(st->st_mode)) { 1107 if (u == US_NOENT) 1108 FLAG_ON(opts, DO_COMPARE); 1109 /* 1110 * Since we always send link info to the server 1111 * so the server can determine if the remote link 1112 * is correct, we never get any acknowledgement 1113 * from the server whether the link was really 1114 * updated or not. 1115 */ 1116 (void) sendlink(rname, opts, st, user, group, destdir); 1117 return(0); 1118 } else if (S_ISREG(st->st_mode)) { 1119 if (u == US_OUTDATE) { 1120 if (IS_ON(opts, DO_VERIFY)) { 1121 message(MT_INFO, "%s: need to update", target); 1122 runspecial(target, opts, rname, destdir); 1123 return(1); 1124 } 1125 if (!IS_ON(opts, DO_QUIET)) 1126 message(MT_CHANGE, "%s: updating", target); 1127 } 1128 return (sendfile(rname, opts, st, user, group, destdir) == 0); 1129 } else { 1130 message(MT_INFO, "%s: unknown file type 0%o", target, 1131 st->st_mode); 1132 return(0); 1133 } 1134 } 1135 1136 /* 1137 * Transfer the file or directory in target[]. 1138 * rname is the name of the file on the remote host. 1139 * 1140 * Return < 0 on error. 1141 * Return 0 if nothing happened. 1142 * Return > 0 if anything is updated. 1143 */ 1144 static int 1145 sendit(char *rname, opt_t opts, int destdir) 1146 { 1147 static struct stat stb; 1148 char *user, *group; 1149 int u, len; 1150 1151 /* 1152 * Remove possible accidental newline 1153 */ 1154 len = strlen(rname); 1155 if (len > 0 && rname[len-1] == '\n') 1156 rname[len-1] = CNULL; 1157 1158 if (checkfilename(rname) != 0) 1159 return(-1); 1160 1161 debugmsg(DM_CALL, "sendit(%s, 0x%x) called\n", rname, opts); 1162 1163 if (except(target)) 1164 return(0); 1165 1166 if (dostat(target, &stb, opts) < 0) 1167 return(-1); 1168 1169 /* 1170 * Does rname need updating? 1171 */ 1172 u = update(rname, opts, &stb); 1173 debugmsg(DM_MISC, "sendit(%s, 0x%x): update status of %s is %d\n", 1174 rname, opts, target, u); 1175 1176 /* 1177 * Don't need to update the file, but we may need to save hardlink 1178 * info. 1179 */ 1180 if (u == US_NOTHING) { 1181 if (S_ISREG(stb.st_mode) && stb.st_nlink > 1) 1182 (void) linkinfo(&stb); 1183 return(0); 1184 } 1185 1186 user = getusername(stb.st_uid, target, opts); 1187 group = getgroupname(stb.st_gid, target, opts); 1188 1189 if (u == US_CHMOG && IS_OFF(opts, DO_UPDATEPERM)) 1190 u = US_OUTDATE; 1191 1192 if (u == US_NOENT || u == US_OUTDATE || u == US_DOCOMP) 1193 return(fullupdate(u, target, opts, rname, destdir, &stb, 1194 user, group)); 1195 1196 if (u == US_CHMOG) 1197 return(statupdate(u, target, opts, rname, destdir, &stb, 1198 user, group)); 1199 1200 return(0); 1201 } 1202 1203 /* 1204 * Remove temporary files and do any cleanup operations before exiting. 1205 */ 1206 void 1207 cleanup(int dummy) 1208 { 1209 char *file; 1210 #ifdef USE_STATDB 1211 extern char statfile[]; 1212 1213 (void) unlink(statfile); 1214 #endif 1215 1216 if ((file = getnotifyfile()) != NULL) 1217 (void) unlink(file); 1218 } 1219 1220 /* 1221 * Update the file(s) if they are different. 1222 * destdir = 1 if destination should be a directory 1223 * (i.e., more than one source is being copied to the same destination). 1224 * 1225 * Return < 0 on error. 1226 * Return 0 if nothing updated. 1227 * Return > 0 if something was updated. 1228 */ 1229 int 1230 install(char *src, char *dest, int ddir, int destdir, opt_t opts) 1231 { 1232 static char destcopy[MAXPATHLEN]; 1233 char *rname; 1234 int didupdate = 0; 1235 char ername[MAXPATHLEN*4]; 1236 1237 debugmsg(DM_CALL, 1238 "install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%d) start\n", 1239 (src?src:"NULL"), (dest?dest:"NULL"), ddir, destdir, opts); 1240 /* 1241 * Save source name 1242 */ 1243 if (IS_ON(opts, DO_WHOLE)) 1244 source[0] = CNULL; 1245 else 1246 (void) strlcpy(source, src, sizeof(source)); 1247 1248 if (dest == NULL) { 1249 FLAG_OFF(opts, DO_WHOLE); /* WHOLE only useful if renaming */ 1250 dest = src; 1251 } 1252 1253 if (checkfilename(dest) != 0) 1254 return(-1); 1255 1256 if (nflag || debug) { 1257 static char buff[BUFSIZ]; 1258 char *cp; 1259 1260 cp = getondistoptlist(opts); 1261 (void) snprintf(buff, sizeof(buff), "%s%s%s %s %s", 1262 IS_ON(opts, DO_VERIFY) ? "verify" : "install", 1263 (cp) ? " -o" : "", (cp) ? cp : "", 1264 src, dest); 1265 if (nflag) { 1266 printf("%s\n", buff); 1267 return(0); 1268 } else 1269 debugmsg(DM_MISC, "%s\n", buff); 1270 } 1271 1272 rname = exptilde(target, src, sizeof(target)); 1273 if (rname == NULL) 1274 return(-1); 1275 ptarget = target; 1276 while (*ptarget) 1277 ptarget++; 1278 /* 1279 * If we are renaming a directory and we want to preserve 1280 * the directory hierarchy (-w), we must strip off the leading 1281 * directory name and preserve the rest. 1282 */ 1283 if (IS_ON(opts, DO_WHOLE)) { 1284 while (*rname == '/') 1285 rname++; 1286 ddir = 1; 1287 destdir = 1; 1288 } else { 1289 rname = strrchr(target, '/'); 1290 /* Check if no '/' or target ends in '/' */ 1291 if (rname == NULL || 1292 rname+1 == NULL || 1293 *(rname+1) == CNULL) 1294 rname = target; 1295 else 1296 rname++; 1297 } 1298 1299 debugmsg(DM_MISC, 1300 "install: target=%s src=%s rname=%s dest='%s' destdir=%d, ddir=%d\n", 1301 target, source, rname, dest, destdir, ddir); 1302 1303 /* 1304 * Pass the destination file/directory name to remote. 1305 */ 1306 ENCODE(ername, dest); 1307 if (ddir) 1308 (void) sendcmd(C_DIRTARGET, "%o %s", opts, ername); 1309 else 1310 (void) sendcmd(C_TARGET, "%o %s", opts, ername); 1311 if (response() < 0) 1312 return(-1); 1313 1314 /* 1315 * Save the name of the remote target destination if we are 1316 * in WHOLE mode (destdir > 0) or if the source and destination 1317 * are not the same. This info will be used later for maintaining 1318 * hardlink info. 1319 */ 1320 if (destdir || (src && dest && strcmp(src, dest))) { 1321 (void) strlcpy(destcopy, dest, sizeof(destcopy)); 1322 Tdest = destcopy; 1323 } 1324 1325 didupdate = sendit(rname, opts, destdir); 1326 Tdest = 0; 1327 1328 return(didupdate); 1329 } 1330