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