1 /* $OpenBSD: client.c,v 1.19 2003/06/03 02:56:14 millert 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.19 2003/06/03 02:56:14 millert 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 if (IS_ON(opts, DO_NOEXEC)) 795 if (isexec(target, statp)) { 796 debugmsg(DM_MISC, "%s is an executable\n", target); 797 return(US_NOTHING); 798 } 799 800 /* 801 * Check to see if the file exists on the remote machine. 802 */ 803 ENCODE(ername, rname); 804 (void) sendcmd(C_QUERY, "%s", ername); 805 806 for (done = 0; !done;) { 807 n = remline(cp = respbuff, sizeof(respbuff), TRUE); 808 if (n <= 0) { 809 error("update: unexpected control record in response to query"); 810 return(US_NOTHING); 811 } 812 813 switch (*cp++) { 814 case QC_ONNFS: /* Resides on a NFS */ 815 debugmsg(DM_MISC, 816 "update: %s is on a NFS. Skipping...\n", 817 rname); 818 return(US_NOTHING); 819 820 case QC_SYM: /* Is a symbolic link */ 821 debugmsg(DM_MISC, 822 "update: %s is a symlink. Skipping...\n", 823 rname); 824 return(US_NOTHING); 825 826 case QC_ONRO: /* Resides on a Read-Only fs */ 827 debugmsg(DM_MISC, 828 "update: %s is on a RO fs. Skipping...\n", 829 rname); 830 return(US_NOTHING); 831 832 case QC_YES: 833 done = 1; 834 break; 835 836 case QC_NO: /* file doesn't exist so install it */ 837 return(US_NOENT); 838 839 case C_ERRMSG: 840 if (cp) 841 message(MT_NERROR, "%s", cp); 842 return(US_NOTHING); 843 844 case C_FERRMSG: 845 if (cp) 846 message(MT_FERROR, "%s", cp); 847 finish(); 848 849 case C_NOTEMSG: 850 if (cp) 851 message(MT_NOTICE, "%s", cp); 852 break; 853 /* Goto top of loop */ 854 855 default: 856 error("update: unexpected response to query '%s'", respbuff); 857 return(US_NOTHING); 858 } 859 } 860 861 /* 862 * Target exists, but no other info passed 863 */ 864 if (n <= 1 || !S_ISREG(statp->st_mode)) 865 return(US_OUTDATE); 866 867 if (IS_ON(opts, DO_COMPARE)) 868 return(US_DOCOMP); 869 870 /* 871 * Parse size 872 */ 873 size = (off_t) strtol(cp, (char **)&cp, 10); 874 if (*cp++ != ' ') { 875 error("update: size not delimited"); 876 return(US_NOTHING); 877 } 878 879 /* 880 * Parse mtime 881 */ 882 mtime = strtol(cp, (char **)&cp, 10); 883 if (*cp++ != ' ') { 884 error("update: mtime not delimited"); 885 return(US_NOTHING); 886 } 887 888 /* 889 * Parse remote file mode 890 */ 891 rmode = strtol(cp, (char **)&cp, 8); 892 if (cp && *cp) 893 ++cp; 894 895 /* 896 * Be backwards compatible 897 */ 898 if (cp && *cp != CNULL) { 899 /* 900 * Parse remote file owner 901 */ 902 owner = strtok((char *)cp, " "); 903 if (owner == NULL) { 904 error("update: owner not delimited"); 905 return(US_NOTHING); 906 } 907 908 /* 909 * Parse remote file group 910 */ 911 group = strtok(NULL, " "); 912 if (group == NULL) { 913 error("update: group not delimited"); 914 return(US_NOTHING); 915 } 916 } 917 918 /* 919 * File needs to be updated? 920 */ 921 lmode = statp->st_mode & 07777; 922 923 debugmsg(DM_MISC, "update(%s,) local mode %04o remote mode %04o\n", 924 rname, lmode, rmode); 925 debugmsg(DM_MISC, "update(%s,) size %ld mtime %d owner '%s' grp '%s'\n", 926 rname, (long) size, mtime, owner, group); 927 928 if (statp->st_mtime != mtime) { 929 if (statp->st_mtime < mtime && IS_ON(opts, DO_YOUNGER)) { 930 message(MT_WARNING, 931 "%s: Warning: remote copy is newer", 932 target); 933 return(US_NOTHING); 934 } 935 return(US_OUTDATE); 936 } 937 938 if (statp->st_size != size) { 939 debugmsg(DM_MISC, "size does not match (%ld != %ld).\n", 940 (long) statp->st_size, (long) size); 941 return(US_OUTDATE); 942 } 943 944 if (!IS_ON(opts, DO_NOCHKMODE) && lmode != rmode) { 945 debugmsg(DM_MISC, "modes do not match (%04o != %04o).\n", 946 lmode, rmode); 947 return(US_CHMOG); 948 } 949 950 951 /* 952 * Check ownership 953 */ 954 if (!IS_ON(opts, DO_NOCHKOWNER) && owner) { 955 if (!IS_ON(opts, DO_NUMCHKOWNER)) { 956 /* Check by string compare */ 957 if (strcmp(owner, getusername(statp->st_uid, 958 target, opts)) != 0) { 959 debugmsg(DM_MISC, 960 "owner does not match (%s != %s).\n", 961 getusername(statp->st_uid, 962 target, opts), owner); 963 return(US_CHMOG); 964 } 965 } else { 966 /* 967 * Check numerically. 968 * Allow negative numbers. 969 */ 970 while (*owner && !isdigit((unsigned char)*owner) && 971 (*owner != '-')) 972 ++owner; 973 if (owner && (UID_T) atoi(owner) != statp->st_uid) { 974 debugmsg(DM_MISC, 975 "owner does not match (%d != %s).\n", 976 statp->st_uid, owner); 977 return(US_CHMOG); 978 } 979 } 980 } 981 982 if (!IS_ON(opts, DO_NOCHKGROUP) && group) { 983 if (!IS_ON(opts, DO_NUMCHKGROUP)) { 984 /* Check by string compare */ 985 if (strcmp(group, getgroupname(statp->st_gid, 986 target, opts)) != 0) { 987 debugmsg(DM_MISC, 988 "group does not match (%s != %s).\n", 989 getgroupname(statp->st_gid, 990 target, opts), group); 991 return(US_CHMOG); 992 } 993 } else { 994 /* Check numerically */ 995 /* Allow negative gid */ 996 while (*group && !isdigit((unsigned char) *group) && 997 (*group != '-')) 998 ++group; 999 if (group && (UID_T) atoi(group) != statp->st_gid) { 1000 debugmsg(DM_MISC, 1001 "group does not match (%d != %s).\n", 1002 statp->st_gid, group); 1003 return(US_CHMOG); 1004 } 1005 } 1006 } 1007 1008 return(US_NOTHING); 1009 } 1010 1011 /* 1012 * Stat a file 1013 */ 1014 static int 1015 dostat(char *file, struct stat *statbuf, opt_t opts) 1016 { 1017 int s; 1018 1019 if (IS_ON(opts, DO_FOLLOW)) 1020 s = stat(file, statbuf); 1021 else 1022 s = lstat(file, statbuf); 1023 1024 if (s < 0) 1025 error("%s: %s failed: %s", file, 1026 IS_ON(opts, DO_FOLLOW) ? "stat" : "lstat", SYSERR); 1027 return(s); 1028 } 1029 1030 /* 1031 * We need to just change file info. 1032 */ 1033 static int 1034 statupdate(int u, char *target, opt_t opts, char *rname, int destdir, 1035 struct stat *st, char *user, char *group) 1036 { 1037 int rv = 0; 1038 char ername[MAXPATHLEN*4]; 1039 int lmode = st->st_mode & 07777; 1040 1041 if (u == US_CHMOG) { 1042 if (IS_ON(opts, DO_VERIFY)) { 1043 message(MT_INFO, 1044 "%s: need to change to perm %04o, owner %s, group %s", 1045 target, lmode, user, group); 1046 runspecial(target, opts, rname, destdir); 1047 } 1048 else { 1049 message(MT_CHANGE, "%s: change to perm %04o, owner %s, group %s", 1050 target, lmode, user, group); 1051 ENCODE(ername, rname); 1052 (void) sendcmd(C_CHMOG, "%o %04o %s %s %s", 1053 opts, lmode, user, group, ername); 1054 (void) response(); 1055 } 1056 rv = 1; 1057 } 1058 return(rv); 1059 } 1060 1061 1062 /* 1063 * We need to install/update: 1064 */ 1065 static int 1066 fullupdate(int u, char *target, opt_t opts, char *rname, int destdir, 1067 struct stat *st, char *user, char *group) 1068 { 1069 /* 1070 * No entry - need to install 1071 */ 1072 if (u == US_NOENT) { 1073 if (IS_ON(opts, DO_VERIFY)) { 1074 message(MT_INFO, "%s: need to install", target); 1075 runspecial(target, opts, rname, destdir); 1076 return(1); 1077 } 1078 if (!IS_ON(opts, DO_QUIET)) 1079 message(MT_CHANGE, "%s: installing", target); 1080 FLAG_OFF(opts, (DO_COMPARE|DO_REMOVE)); 1081 } 1082 1083 /* 1084 * Handle special file types, including directories and symlinks 1085 */ 1086 if (S_ISDIR(st->st_mode)) { 1087 if (senddir(rname, opts, st, user, group, destdir) > 0) 1088 return(1); 1089 return(0); 1090 } else if (S_ISLNK(st->st_mode)) { 1091 if (u == US_NOENT) 1092 FLAG_ON(opts, DO_COMPARE); 1093 /* 1094 * Since we always send link info to the server 1095 * so the server can determine if the remote link 1096 * is correct, we never get any acknowledge meant 1097 * from the server whether the link was really 1098 * updated or not. 1099 */ 1100 (void) sendlink(rname, opts, st, user, group, destdir); 1101 return(0); 1102 } else if (S_ISREG(st->st_mode)) { 1103 if (u == US_OUTDATE) { 1104 if (IS_ON(opts, DO_VERIFY)) { 1105 message(MT_INFO, "%s: need to update", target); 1106 runspecial(target, opts, rname, destdir); 1107 return(1); 1108 } 1109 if (!IS_ON(opts, DO_QUIET)) 1110 message(MT_CHANGE, "%s: updating", target); 1111 } 1112 return (sendfile(rname, opts, st, user, group, destdir) == 0); 1113 } else { 1114 message(MT_INFO, "%s: unknown file type 0%o", target, 1115 st->st_mode); 1116 return(0); 1117 } 1118 } 1119 1120 /* 1121 * Transfer the file or directory in target[]. 1122 * rname is the name of the file on the remote host. 1123 * 1124 * Return < 0 on error. 1125 * Return 0 if nothing happened. 1126 * Return > 0 if anything is updated. 1127 */ 1128 static int 1129 sendit(char *rname, opt_t opts, int destdir) 1130 { 1131 static struct stat stb; 1132 char *user, *group; 1133 int u, len; 1134 1135 /* 1136 * Remove possible accidental newline 1137 */ 1138 len = strlen(rname); 1139 if (len > 0 && rname[len-1] == '\n') 1140 rname[len-1] = CNULL; 1141 1142 if (checkfilename(rname) != 0) 1143 return(-1); 1144 1145 debugmsg(DM_CALL, "sendit(%s, 0x%x) called\n", rname, opts); 1146 1147 if (except(target)) 1148 return(0); 1149 1150 if (dostat(target, &stb, opts) < 0) 1151 return(-1); 1152 1153 /* 1154 * Does rname need updating? 1155 */ 1156 u = update(rname, opts, &stb); 1157 debugmsg(DM_MISC, "sendit(%s, 0x%x): update status of %s is %d\n", 1158 rname, opts, target, u); 1159 1160 /* 1161 * Don't need to update the file, but we may need to save hardlink 1162 * info. 1163 */ 1164 if (u == US_NOTHING) { 1165 if (S_ISREG(stb.st_mode) && stb.st_nlink > 1) 1166 (void) linkinfo(&stb); 1167 return(0); 1168 } 1169 1170 user = getusername(stb.st_uid, target, opts); 1171 group = getgroupname(stb.st_gid, target, opts); 1172 1173 if (u == US_CHMOG && IS_OFF(opts, DO_UPDATEPERM)) 1174 u = US_OUTDATE; 1175 1176 if (u == US_NOENT || u == US_OUTDATE || u == US_DOCOMP) 1177 return(fullupdate(u, target, opts, rname, destdir, &stb, 1178 user, group)); 1179 1180 if (u == US_CHMOG) 1181 return(statupdate(u, target, opts, rname, destdir, &stb, 1182 user, group)); 1183 1184 return(0); 1185 } 1186 1187 /* 1188 * Remove temporary files and do any cleanup operations before exiting. 1189 */ 1190 void 1191 cleanup(int dummy) 1192 { 1193 char *file; 1194 #ifdef USE_STATDB 1195 extern char statfile[]; 1196 1197 (void) unlink(statfile); 1198 #endif 1199 1200 if ((file = getnotifyfile()) != NULL) 1201 (void) unlink(file); 1202 } 1203 1204 /* 1205 * Update the file(s) if they are different. 1206 * destdir = 1 if destination should be a directory 1207 * (i.e., more than one source is being copied to the same destination). 1208 * 1209 * Return < 0 on error. 1210 * Return 0 if nothing updated. 1211 * Return > 0 if something was updated. 1212 */ 1213 int 1214 install(char *src, char *dest, int ddir, int destdir, opt_t opts) 1215 { 1216 static char destcopy[MAXPATHLEN]; 1217 char *rname; 1218 int didupdate = 0; 1219 char ername[MAXPATHLEN*4]; 1220 1221 debugmsg(DM_CALL, 1222 "install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%d) start\n", 1223 (src?src:"NULL"), (dest?dest:"NULL"), ddir, destdir, opts); 1224 /* 1225 * Save source name 1226 */ 1227 if (IS_ON(opts, DO_WHOLE)) 1228 source[0] = CNULL; 1229 else 1230 (void) strlcpy(source, src, sizeof(source)); 1231 1232 if (dest == NULL) { 1233 FLAG_OFF(opts, DO_WHOLE); /* WHOLE only useful if renaming */ 1234 dest = src; 1235 } 1236 1237 if (checkfilename(dest) != 0) 1238 return(-1); 1239 1240 if (nflag || debug) { 1241 static char buff[BUFSIZ]; 1242 char *cp; 1243 1244 cp = getondistoptlist(opts); 1245 (void) snprintf(buff, sizeof(buff), "%s%s%s %s %s", 1246 IS_ON(opts, DO_VERIFY) ? "verify" : "install", 1247 (cp) ? " -o" : "", (cp) ? cp : "", 1248 src, dest); 1249 if (nflag) { 1250 printf("%s\n", buff); 1251 return(0); 1252 } else 1253 debugmsg(DM_MISC, "%s\n", buff); 1254 } 1255 1256 rname = exptilde(target, src, sizeof(target)); 1257 if (rname == NULL) 1258 return(-1); 1259 ptarget = target; 1260 while (*ptarget) 1261 ptarget++; 1262 /* 1263 * If we are renaming a directory and we want to preserve 1264 * the directory heirarchy (-w), we must strip off the leading 1265 * directory name and preserve the rest. 1266 */ 1267 if (IS_ON(opts, DO_WHOLE)) { 1268 while (*rname == '/') 1269 rname++; 1270 ddir = 1; 1271 destdir = 1; 1272 } else { 1273 rname = strrchr(target, '/'); 1274 /* Check if no '/' or target ends in '/' */ 1275 if (rname == NULL || 1276 rname+1 == NULL || 1277 *(rname+1) == CNULL) 1278 rname = target; 1279 else 1280 rname++; 1281 } 1282 1283 debugmsg(DM_MISC, 1284 "install: target=%s src=%s rname=%s dest='%s' destdir=%d, ddir=%d\n", 1285 target, source, rname, dest, destdir, ddir); 1286 1287 /* 1288 * Pass the destination file/directory name to remote. 1289 */ 1290 ENCODE(ername, dest); 1291 if (ddir) 1292 (void) sendcmd(C_DIRTARGET, "%o %s", opts, ername); 1293 else 1294 (void) sendcmd(C_TARGET, "%o %s", opts, ername); 1295 if (response() < 0) 1296 return(-1); 1297 1298 /* 1299 * Save the name of the remote target destination if we are 1300 * in WHOLE mode (destdir > 0) or if the source and destination 1301 * are not the same. This info will be used later for maintaining 1302 * hardlink info. 1303 */ 1304 if (destdir || (src && dest && strcmp(src, dest))) { 1305 (void) strlcpy(destcopy, dest, sizeof(destcopy)); 1306 Tdest = destcopy; 1307 } 1308 1309 didupdate = sendit(rname, opts, destdir); 1310 Tdest = 0; 1311 1312 return(didupdate); 1313 } 1314