1 /* $OpenBSD: client.c,v 1.31 2014/07/12 03:48:04 guenther 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 <dirent.h> 33 34 #include "defs.h" 35 #include "y.tab.h" 36 37 /* 38 * Routines used in client mode to communicate with remove server. 39 */ 40 41 42 /* 43 * Update status 44 */ 45 #define US_NOTHING 0 /* No update needed */ 46 #define US_NOENT 1 /* Entry does not exist */ 47 #define US_OUTDATE 2 /* Entry is out of date */ 48 #define US_DOCOMP 3 /* Do a binary comparison */ 49 #define US_CHMOG 4 /* Modes or ownership of file differ */ 50 51 struct linkbuf *ihead = NULL; /* list of files with more than one link */ 52 char buf[BUFSIZ]; /* general purpose buffer */ 53 u_char respbuff[BUFSIZ]; /* Response buffer */ 54 char target[BUFSIZ]; /* target/source directory name */ 55 char source[BUFSIZ]; /* source directory name */ 56 char *ptarget; /* pointer to end of target name */ 57 char *Tdest; /* pointer to last T dest*/ 58 struct namelist *updfilelist = NULL; /* List of updated files */ 59 60 static void runspecial(char *, opt_t, char *, int); 61 static void addcmdspecialfile(char *, char *, int); 62 static void freecmdspecialfiles(void); 63 static struct linkbuf *linkinfo(struct stat *); 64 static int sendhardlink(opt_t, struct linkbuf *, char *, int); 65 static int sendfile(char *, opt_t, struct stat *, char *, char *, int); 66 static int rmchk(opt_t); 67 static int senddir(char *, opt_t, struct stat *, char *, char *, int); 68 static int sendlink(char *, opt_t, struct stat *, char *, char *, int); 69 static int update(char *, opt_t, struct stat *); 70 static int dostat(char *, struct stat *, opt_t); 71 static int statupdate(int, char *, opt_t, char *, int, struct stat *, char *, char *); 72 static int fullupdate(int, char *, opt_t, char *, int, struct stat *, char *, char *); 73 static int sendit(char *, opt_t, int); 74 75 /* 76 * return remote file pathname (relative from target) 77 */ 78 char * 79 remfilename(char *src, char *dest, char *path, char *rname, int destdir) 80 { 81 extern struct namelist *filelist; 82 char *lname, *cp; 83 static char buff[BUFSIZ]; 84 int srclen, pathlen; 85 char *p; 86 87 88 debugmsg(DM_MISC, 89 "remfilename: src=%s dest=%s path=%s rname=%s destdir=%d\n", 90 A(src), A(dest), A(path), A(rname), destdir); 91 92 if (!dest) { 93 debugmsg(DM_MISC, "remfilename: remote filename=%s\n", path); 94 return(path); 95 } 96 97 if (!destdir) { 98 debugmsg(DM_MISC, "remfilename: remote filename=%s\n", dest); 99 return(dest); 100 } 101 102 buff[0] = CNULL; 103 lname = buff; 104 if (path && *path) { 105 cp = strrchr(path, '/'); 106 if (cp == NULL) 107 (void) snprintf(buff, sizeof(buff), "%s/%s", dest, path); 108 else { 109 srclen = strlen(src); 110 pathlen = strlen(path); 111 if (srclen >= pathlen) 112 cp++; /* xbasename(path) */ 113 else { 114 if (filelist && filelist->n_next == NULL) 115 /* path relative to src */ 116 cp = path + srclen; 117 else { 118 if ((p = strrchr(src, '/'))) 119 cp = path + srclen - strlen(p); 120 else 121 cp = path; 122 } 123 } 124 if ((*cp != '/') && *cp) 125 (void) snprintf(buff, sizeof(buff), "%s/%s", 126 dest, cp); 127 else 128 (void) snprintf(buff, sizeof(buff), "%s%s", 129 dest, cp); 130 } 131 } else 132 (void) strlcpy(lname, dest, buf + sizeof buff - lname); 133 134 debugmsg(DM_MISC, "remfilename: remote filename=%s\n", lname); 135 136 return(lname); 137 } 138 139 /* 140 * Return true if name is in the list. 141 */ 142 int 143 inlist(struct namelist *list, char *file) 144 { 145 struct namelist *nl; 146 147 for (nl = list; nl != NULL; nl = nl->n_next) 148 if (strcmp(file, nl->n_name) == 0) 149 return(1); 150 return(0); 151 } 152 153 /* 154 * Run any special commands for this file 155 */ 156 static void 157 runspecial(char *starget, opt_t opts, char *rname, int destdir) 158 { 159 struct subcmd *sc; 160 extern struct subcmd *subcmds; 161 char *rfile; 162 163 rfile = remfilename(source, Tdest, target, rname, destdir); 164 165 for (sc = subcmds; sc != NULL; sc = sc->sc_next) { 166 if (sc->sc_type != SPECIAL) 167 continue; 168 if (sc->sc_args != NULL && !inlist(sc->sc_args, starget)) 169 continue; 170 message(MT_CHANGE, "special \"%s\"", sc->sc_name); 171 if (IS_ON(opts, DO_VERIFY)) 172 continue; 173 (void) sendcmd(C_SPECIAL, 174 "%s=%s;%s=%s;%s=%s;export %s %s %s;%s", 175 E_LOCFILE, starget, 176 E_REMFILE, rfile, 177 E_BASEFILE, xbasename(rfile), 178 E_LOCFILE, E_REMFILE, E_BASEFILE, 179 sc->sc_name); 180 while (response() > 0) 181 ; 182 } 183 } 184 185 /* 186 * If we're doing a target with a "cmdspecial" in it, then 187 * save the name of the file being updated for use with "cmdspecial". 188 */ 189 static void 190 addcmdspecialfile(char *starget, char *rname, int destdir) 191 { 192 char *rfile; 193 struct namelist *new; 194 struct subcmd *sc; 195 extern struct subcmd *subcmds; 196 int isokay = 0; 197 198 rfile = remfilename(source, Tdest, target, rname, destdir); 199 200 for (sc = subcmds; sc != NULL && !isokay; sc = sc->sc_next) { 201 if (sc->sc_type != CMDSPECIAL) 202 continue; 203 if (sc->sc_args != NULL && !inlist(sc->sc_args, starget)) 204 continue; 205 isokay = TRUE; 206 } 207 208 if (isokay) { 209 new = xmalloc(sizeof *new); 210 new->n_name = xstrdup(rfile); 211 new->n_regex = NULL; 212 new->n_next = updfilelist; 213 updfilelist = new; 214 } 215 } 216 217 /* 218 * Free the file list 219 */ 220 static void 221 freecmdspecialfiles(void) 222 { 223 struct namelist *ptr, *save; 224 225 for (ptr = updfilelist; ptr; ) { 226 if (ptr->n_name) (void) free(ptr->n_name); 227 save = ptr->n_next; 228 (void) free(ptr); 229 if (save) 230 ptr = save->n_next; 231 else 232 ptr = NULL; 233 } 234 updfilelist = NULL; 235 } 236 237 /* 238 * Run commands for an entire cmd 239 */ 240 void 241 runcmdspecial(struct cmd *cmd, opt_t opts) 242 { 243 struct subcmd *sc; 244 struct namelist *f; 245 int first = TRUE; 246 247 for (sc = cmd->c_cmds; sc != NULL; sc = sc->sc_next) { 248 if (sc->sc_type != CMDSPECIAL) 249 continue; 250 message(MT_CHANGE, "cmdspecial \"%s\"", sc->sc_name); 251 if (IS_ON(opts, DO_VERIFY)) 252 continue; 253 /* Send all the file names */ 254 for (f = updfilelist; f != NULL; f = f->n_next) { 255 if (first) { 256 (void) sendcmd(C_CMDSPECIAL, NULL); 257 if (response() < 0) 258 return; 259 first = FALSE; 260 } 261 (void) sendcmd(RC_FILE, "%s", f->n_name); 262 if (response() < 0) 263 return; 264 } 265 if (first) { 266 (void) sendcmd(C_CMDSPECIAL, NULL); 267 if (response() < 0) 268 return; 269 first = FALSE; 270 } 271 /* Send command to run and wait for it to complete */ 272 (void) sendcmd(RC_COMMAND, "%s", sc->sc_name); 273 while (response() > 0) 274 ; 275 first = TRUE; /* Reset in case there are more CMDSPECIAL's */ 276 } 277 freecmdspecialfiles(); 278 } 279 280 /* 281 * For security, reject filenames that contains a newline 282 */ 283 int 284 checkfilename(char *name) 285 { 286 char *cp; 287 288 if (strchr(name, '\n')) { 289 for (cp = name; *cp; cp++) 290 if (*cp == '\n') 291 *cp = '?'; 292 message(MT_NERROR, 293 "Refuse to handle filename containing newline: %s", 294 name); 295 return(-1); 296 } 297 298 return(0); 299 } 300 301 void 302 freelinkinfo(struct linkbuf *lp) 303 { 304 if (lp->pathname) 305 free(lp->pathname); 306 if (lp->src) 307 free(lp->src); 308 if (lp->target) 309 free(lp->target); 310 free(lp); 311 } 312 313 /* 314 * Save and retrieve hard link info 315 */ 316 static struct linkbuf * 317 linkinfo(struct stat *statp) 318 { 319 struct linkbuf *lp; 320 321 /* XXX - linear search doesn't scale with many links */ 322 for (lp = ihead; lp != NULL; lp = lp->nextp) 323 if (lp->inum == statp->st_ino && lp->devnum == statp->st_dev) { 324 lp->count--; 325 return(lp); 326 } 327 328 lp = xmalloc(sizeof(*lp)); 329 lp->nextp = ihead; 330 ihead = lp; 331 lp->inum = statp->st_ino; 332 lp->devnum = statp->st_dev; 333 lp->count = statp->st_nlink - 1; 334 lp->pathname = xstrdup(target); 335 lp->src = xstrdup(source); 336 if (Tdest) 337 lp->target = xstrdup(Tdest); 338 else 339 lp->target = NULL; 340 341 return(NULL); 342 } 343 344 /* 345 * Send a hardlink 346 */ 347 static int 348 sendhardlink(opt_t opts, struct linkbuf *lp, char *rname, int destdir) 349 { 350 static char buff[MAXPATHLEN]; 351 char *lname; /* name of file to link to */ 352 char ername[MAXPATHLEN*4], elname[MAXPATHLEN*4]; 353 354 debugmsg(DM_MISC, 355 "sendhardlink: rname='%s' pathname='%s' src='%s' target='%s'\n", 356 rname, lp->pathname ? lp->pathname : "", 357 lp->src ? lp->src : "", lp->target ? lp->target : ""); 358 359 if (lp->target == NULL) 360 lname = lp->pathname; 361 else { 362 lname = buff; 363 strlcpy(lname, remfilename(lp->src, lp->target, 364 lp->pathname, rname, 365 destdir), sizeof(buff)); 366 debugmsg(DM_MISC, "sendhardlink: lname=%s\n", lname); 367 } 368 ENCODE(elname, lname); 369 ENCODE(ername, rname); 370 (void) sendcmd(C_RECVHARDLINK, "%lo %s %s", 371 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[MAXPATHLEN*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)) < 0) { 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, "%lo %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[MAXPATHLEN*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) < 0) { 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[MAXPATHLEN*4]; 588 589 /* 590 * Send recvdir command in recvit() format. 591 */ 592 ENCODE(ername, rname); 593 (void) sendcmd(C_RECVDIR, "%lo %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) >= MAXPATHLEN - 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[MAXPATHLEN]; 660 u_char *s; 661 char ername[MAXPATHLEN*4]; 662 663 debugmsg(DM_CALL, "sendlink(%s, %lx, 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, "%lo %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[MAXPATHLEN*4]; 781 782 debugmsg(DM_CALL, "update(%s, 0x%lx, %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 < 0) 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[MAXPATHLEN*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, "%lo %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 0%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, 0x%lx) 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, 0x%lx): 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[MAXPATHLEN]; 1218 char *rname; 1219 int didupdate = 0; 1220 char ername[MAXPATHLEN*4]; 1221 1222 debugmsg(DM_CALL, 1223 "install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%ld) 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, "%lo %s", opts, ername); 1294 else 1295 (void) sendcmd(C_TARGET, "%lo %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