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