rcp.c revision 169848
11539Srgrimes/*- 21539Srgrimes * Copyright (c) 1983, 1990, 1992, 1993 31539Srgrimes * The Regents of the University of California. All rights reserved. 41539Srgrimes * Copyright (c) 2002 Networks Associates Technology, Inc. 51539Srgrimes * All rights reserved. 61539Srgrimes * 71539Srgrimes * Portions of this software were developed for the FreeBSD Project by 81539Srgrimes * ThinkSec AS and NAI Labs, the Security Research Division of Network 91539Srgrimes * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 101539Srgrimes * ("CBOSS"), as part of the DARPA CHATS research program. 111539Srgrimes * 121539Srgrimes * Redistribution and use in source and binary forms, with or without 131539Srgrimes * modification, are permitted provided that the following conditions 141539Srgrimes * are met: 151539Srgrimes * 1. Redistributions of source code must retain the above copyright 161539Srgrimes * notice, this list of conditions and the following disclaimer. 171539Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 181539Srgrimes * notice, this list of conditions and the following disclaimer in the 191539Srgrimes * documentation and/or other materials provided with the distribution. 201539Srgrimes * 4. Neither the name of the University nor the names of its contributors 21203964Simp * may be used to endorse or promote products derived from this software 221539Srgrimes * without specific prior written permission. 231539Srgrimes * 241539Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251539Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261539Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271539Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281539Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291539Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301539Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311539Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321539Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331539Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341539Srgrimes * SUCH DAMAGE. 351539Srgrimes */ 361539Srgrimes 371539Srgrimes#if 0 3854746Sphantom#ifndef lint 391539Srgrimesstatic char const copyright[] = 401539Srgrimes"@(#) Copyright (c) 1983, 1990, 1992, 1993\n\ 417655Sbde The Regents of the University of California. All rights reserved.\n"; 427655Sbde#endif /* not lint */ 431539Srgrimes 44103113Smike#ifndef lint 45103113Smikestatic char sccsid[] = "@(#)rcp.c 8.2 (Berkeley) 4/2/94"; 46133559Stjr#endif /* not lint */ 471539Srgrimes#endif 487655Sbde#include <sys/cdefs.h> 4993032Simp__FBSDID("$FreeBSD: head/bin/rcp/rcp.c 169848 2007-05-22 04:21:00Z cperciva $"); 5093032Simp 5193032Simp#include <sys/param.h> 5293032Simp#include <sys/stat.h> 5393032Simp#include <sys/time.h> 5493032Simp#include <sys/socket.h> 5593032Simp#include <netinet/in.h> 5693032Simp#include <netinet/in_systm.h> 5793032Simp#include <netinet/ip.h> 5893032Simp 5993032Simp#include <ctype.h> 6093032Simp#include <dirent.h> 6193032Simp#include <err.h> 621539Srgrimes#include <errno.h> 63102998Smike#include <fcntl.h> 64102998Smike#include <libutil.h> 65102998Smike#include <limits.h> 66102998Smike#include <netdb.h> 67102998Smike#include <paths.h> 68128523Stjr#include <pwd.h> 69128523Stjr#include <signal.h> 70128523Stjr#include <stdint.h> 71128523Stjr#include <stdio.h> 72102998Smike#include <stdlib.h> 7393032Simp#include <string.h> 7493032Simp#include <string.h> 7593032Simp#include <unistd.h> 7693032Simp 7793032Simp#include "extern.h" 7893032Simp 7993032Simp#define OPTIONS "46dfprt" 807655Sbde 817655Sbdestruct passwd *pwd; 827655Sbdeu_short port; 83172619Sacheuid_t userid; 84172619Sacheint errs, rem; 85172619Sacheint pflag, iamremote, iamrecursive, targetshouldbedirectory; 8657035Sobrienint family = PF_UNSPEC; 87172619Sache 88172619Sachestatic int argc_copy; 89172619Sachestatic const char **argv_copy; 90172619Sache 91172619Sachestatic char period[] = "."; 92172619Sache 9357035Sobrien#define CMDNEEDS 64 94172619Sachechar cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 95172619Sache 967655Sbdeint response(void); 97102998Smikevoid rsource(char *, struct stat *); 98102998Smikevoid sink(int, char *[]); 99102998Smikevoid source(int, char *[]); 100102998Smikevoid tolocal(int, char *[]); 101102998Smikevoid toremote(char *, int, char *[]); 102102998Smikevoid usage(void); 103102998Smike 104102998Smikeint 105102998Smikemain(int argc, char *argv[]) 106102998Smike{ 107102998Smike struct servent *sp; 108102998Smike int ch, fflag, i, tflag; 109172619Sache char *targ; 110172619Sache 111102998Smike /* 112102998Smike * Prepare for execing ourselves. 113102998Smike */ 114102998Smike argc_copy = argc + 1; 115128523Stjr argv_copy = malloc((argc_copy + 1) * sizeof(*argv_copy)); 116172619Sache if (argv_copy == NULL) 117128523Stjr err(1, "malloc"); 118128523Stjr argv_copy[0] = argv[0]; 119102998Smike argv_copy[1] = "-K"; 120172619Sache for (i = 1; i < argc; ++i) { 121172619Sache argv_copy[i + 1] = strdup(argv[i]); 122172619Sache if (argv_copy[i + 1] == NULL) 123172619Sache errx(1, "strdup: out of memory"); 124172619Sache } 125172619Sache argv_copy[argc + 1] = NULL; 126172619Sache 1271539Srgrimes fflag = tflag = 0; 1281539Srgrimes while ((ch = getopt(argc, argv, OPTIONS)) != -1) 1291539Srgrimes switch(ch) { /* User-visible flags. */ 130 case '4': 131 family = PF_INET; 132 break; 133 134 case '6': 135 family = PF_INET6; 136 break; 137 138 case 'p': 139 pflag = 1; 140 break; 141 case 'r': 142 iamrecursive = 1; 143 break; 144 /* Server options. */ 145 case 'd': 146 targetshouldbedirectory = 1; 147 break; 148 case 'f': /* "from" */ 149 iamremote = 1; 150 fflag = 1; 151 break; 152 case 't': /* "to" */ 153 iamremote = 1; 154 tflag = 1; 155 break; 156 case '?': 157 default: 158 usage(); 159 } 160 argc -= optind; 161 argv += optind; 162 163 sp = getservbyname("shell", "tcp"); 164 if (sp == NULL) 165 errx(1, "shell/tcp: unknown service"); 166 port = sp->s_port; 167 168 if ((pwd = getpwuid(userid = getuid())) == NULL) 169 errx(1, "unknown user %d", (int)userid); 170 171 rem = STDIN_FILENO; /* XXX */ 172 173 if (fflag) { /* Follow "protocol", send data. */ 174 (void)response(); 175 (void)setuid(userid); 176 source(argc, argv); 177 exit(errs); 178 } 179 180 if (tflag) { /* Receive data. */ 181 (void)setuid(userid); 182 sink(argc, argv); 183 exit(errs); 184 } 185 186 if (argc < 2) 187 usage(); 188 if (argc > 2) 189 targetshouldbedirectory = 1; 190 191 rem = -1; 192 /* Command to be executed on remote system using "rsh". */ 193 (void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s", 194 iamrecursive ? " -r" : "", pflag ? " -p" : "", 195 targetshouldbedirectory ? " -d" : ""); 196 197 (void)signal(SIGPIPE, lostconn); 198 199 if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 200 toremote(targ, argc, argv); 201 else { 202 tolocal(argc, argv); /* Dest is local host. */ 203 if (targetshouldbedirectory) 204 verifydir(argv[argc - 1]); 205 } 206 exit(errs); 207} 208 209void 210toremote(char *targ, int argc, char *argv[]) 211{ 212 int i, tos; 213 char *bp, *host, *src, *suser, *thost, *tuser; 214 215 *targ++ = 0; 216 if (*targ == 0) 217 targ = period; 218 219 if ((thost = strchr(argv[argc - 1], '@'))) { 220 /* user@host */ 221 *thost++ = 0; 222 tuser = argv[argc - 1]; 223 if (*tuser == '\0') 224 tuser = NULL; 225 else if (!okname(tuser)) 226 exit(1); 227 } else { 228 thost = argv[argc - 1]; 229 tuser = NULL; 230 } 231 232 for (i = 0; i < argc - 1; i++) { 233 src = colon(argv[i]); 234 if (src) { /* remote to remote */ 235 *src++ = 0; 236 if (*src == 0) 237 src = period; 238 host = strchr(argv[i], '@'); 239 if (host) { 240 *host++ = 0; 241 suser = argv[i]; 242 if (*suser == '\0') 243 suser = pwd->pw_name; 244 else if (!okname(suser)) { 245 ++errs; 246 continue; 247 } 248 if (asprintf(&bp, 249 "%s %s -l %s -n %s %s '%s%s%s:%s'", 250 _PATH_RSH, host, suser, cmd, src, 251 tuser ? tuser : "", tuser ? "@" : "", 252 thost, targ) == -1) 253 err(1, "asprintf"); 254 } else 255 if (asprintf(&bp, 256 "exec %s %s -n %s %s '%s%s%s:%s'", 257 _PATH_RSH, argv[i], cmd, src, 258 tuser ? tuser : "", tuser ? "@" : "", 259 thost, targ) == -1) 260 err(1, "asprintf"); 261 (void)susystem(bp, userid); 262 (void)free(bp); 263 } else { /* local to remote */ 264 if (rem == -1) { 265 if (asprintf(&bp, "%s -t %s", cmd, targ) 266 == -1) 267 err(1, "asprintf"); 268 host = thost; 269 rem = rcmd_af(&host, port, 270 pwd->pw_name, 271 tuser ? tuser : pwd->pw_name, 272 bp, 0, family); 273 if (rem < 0) 274 exit(1); 275 if (family == PF_INET) { 276 tos = IPTOS_THROUGHPUT; 277 if (setsockopt(rem, IPPROTO_IP, IP_TOS, 278 &tos, sizeof(int)) < 0) 279 warn("TOS (ignored)"); 280 } 281 if (response() < 0) 282 exit(1); 283 (void)free(bp); 284 (void)setuid(userid); 285 } 286 source(1, argv+i); 287 } 288 } 289} 290 291void 292tolocal(int argc, char *argv[]) 293{ 294 int i, len, tos; 295 char *bp, *host, *src, *suser; 296 297 for (i = 0; i < argc - 1; i++) { 298 if (!(src = colon(argv[i]))) { /* Local to local. */ 299 len = strlen(_PATH_CP) + strlen(argv[i]) + 300 strlen(argv[argc - 1]) + 20; 301 if (!(bp = malloc(len))) 302 err(1, "malloc"); 303 (void)snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP, 304 iamrecursive ? " -PR" : "", pflag ? " -p" : "", 305 argv[i], argv[argc - 1]); 306 if (susystem(bp, userid)) 307 ++errs; 308 (void)free(bp); 309 continue; 310 } 311 *src++ = 0; 312 if (*src == 0) 313 src = period; 314 if ((host = strchr(argv[i], '@')) == NULL) { 315 host = argv[i]; 316 suser = pwd->pw_name; 317 } else { 318 *host++ = 0; 319 suser = argv[i]; 320 if (*suser == '\0') 321 suser = pwd->pw_name; 322 else if (!okname(suser)) { 323 ++errs; 324 continue; 325 } 326 } 327 len = strlen(src) + CMDNEEDS + 20; 328 if ((bp = malloc(len)) == NULL) 329 err(1, "malloc"); 330 (void)snprintf(bp, len, "%s -f %s", cmd, src); 331 rem = rcmd_af(&host, port, pwd->pw_name, suser, bp, 0, 332 family); 333 (void)free(bp); 334 if (rem < 0) { 335 ++errs; 336 continue; 337 } 338 (void)seteuid(userid); 339 if (family == PF_INET) { 340 tos = IPTOS_THROUGHPUT; 341 if (setsockopt(rem, IPPROTO_IP, IP_TOS, &tos, 342 sizeof(int)) < 0) 343 warn("TOS (ignored)"); 344 } 345 sink(1, argv + argc - 1); 346 (void)seteuid(0); 347 (void)close(rem); 348 rem = -1; 349 } 350} 351 352void 353source(int argc, char *argv[]) 354{ 355 struct stat stb; 356 static BUF buffer; 357 BUF *bp; 358 off_t i; 359 int amt, fd, haderr, indx, result; 360 char *last, *name, buf[BUFSIZ]; 361 362 for (indx = 0; indx < argc; ++indx) { 363 name = argv[indx]; 364 if ((fd = open(name, O_RDONLY, 0)) < 0) 365 goto syserr; 366 if (fstat(fd, &stb)) { 367syserr: run_err("%s: %s", name, strerror(errno)); 368 goto next; 369 } 370 switch (stb.st_mode & S_IFMT) { 371 case S_IFREG: 372 break; 373 case S_IFDIR: 374 if (iamrecursive) { 375 rsource(name, &stb); 376 goto next; 377 } 378 /* FALLTHROUGH */ 379 default: 380 run_err("%s: not a regular file", name); 381 goto next; 382 } 383 if ((last = strrchr(name, '/')) == NULL) 384 last = name; 385 else 386 ++last; 387 if (pflag) { 388 /* 389 * Make it compatible with possible future 390 * versions expecting microseconds. 391 */ 392 (void)snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n", 393 (long)stb.st_mtimespec.tv_sec, 394 (long)stb.st_atimespec.tv_sec); 395 (void)write(rem, buf, strlen(buf)); 396 if (response() < 0) 397 goto next; 398 } 399#define MODEMASK (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO) 400 (void)snprintf(buf, sizeof(buf), "C%04o %jd %s\n", 401 stb.st_mode & MODEMASK, (intmax_t)stb.st_size, last); 402 (void)write(rem, buf, strlen(buf)); 403 if (response() < 0) 404 goto next; 405 if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) { 406next: if (fd >= 0) 407 (void)close(fd); 408 continue; 409 } 410 411 /* Keep writing after an error so that we stay sync'd up. */ 412 for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 413 amt = bp->cnt; 414 if (i + amt > stb.st_size) 415 amt = stb.st_size - i; 416 if (!haderr) { 417 result = read(fd, bp->buf, amt); 418 if (result != amt) 419 haderr = result >= 0 ? EIO : errno; 420 } 421 if (haderr) 422 (void)write(rem, bp->buf, amt); 423 else { 424 result = write(rem, bp->buf, amt); 425 if (result != amt) 426 haderr = result >= 0 ? EIO : errno; 427 } 428 } 429 if (close(fd) && !haderr) 430 haderr = errno; 431 if (!haderr) 432 (void)write(rem, "", 1); 433 else 434 run_err("%s: %s", name, strerror(haderr)); 435 (void)response(); 436 } 437} 438 439void 440rsource(char *name, struct stat *statp) 441{ 442 DIR *dirp; 443 struct dirent *dp; 444 char *last, *vect[1], path[PATH_MAX]; 445 446 if (!(dirp = opendir(name))) { 447 run_err("%s: %s", name, strerror(errno)); 448 return; 449 } 450 last = strrchr(name, '/'); 451 if (last == 0) 452 last = name; 453 else 454 last++; 455 if (pflag) { 456 (void)snprintf(path, sizeof(path), "T%ld 0 %ld 0\n", 457 (long)statp->st_mtimespec.tv_sec, 458 (long)statp->st_atimespec.tv_sec); 459 (void)write(rem, path, strlen(path)); 460 if (response() < 0) { 461 closedir(dirp); 462 return; 463 } 464 } 465 (void)snprintf(path, sizeof(path), 466 "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last); 467 (void)write(rem, path, strlen(path)); 468 if (response() < 0) { 469 closedir(dirp); 470 return; 471 } 472 while ((dp = readdir(dirp))) { 473 if (dp->d_ino == 0) 474 continue; 475 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 476 continue; 477 if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path)) { 478 run_err("%s/%s: name too long", name, dp->d_name); 479 continue; 480 } 481 (void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name); 482 vect[0] = path; 483 source(1, vect); 484 } 485 (void)closedir(dirp); 486 (void)write(rem, "E\n", 2); 487 (void)response(); 488} 489 490void 491sink(int argc, char *argv[]) 492{ 493 static BUF buffer; 494 struct stat stb; 495 struct timeval tv[2]; 496 enum { YES, NO, DISPLAYED } wrerr; 497 BUF *bp; 498 off_t i, j, size; 499 int amt, exists, first, mask, mode, ofd, omode; 500 size_t count; 501 int setimes, targisdir, wrerrno = 0; 502 char ch, *cp, *np, *targ, *vect[1], buf[BUFSIZ], path[PATH_MAX]; 503 const char *why; 504 505#define atime tv[0] 506#define mtime tv[1] 507#define SCREWUP(str) { why = str; goto screwup; } 508 509 setimes = targisdir = 0; 510 mask = umask(0); 511 if (!pflag) 512 (void)umask(mask); 513 if (argc != 1) { 514 run_err("ambiguous target"); 515 exit(1); 516 } 517 targ = *argv; 518 if (targetshouldbedirectory) 519 verifydir(targ); 520 (void)write(rem, "", 1); 521 if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 522 targisdir = 1; 523 for (first = 1;; first = 0) { 524 cp = buf; 525 if (read(rem, cp, 1) <= 0) 526 return; 527 if (*cp++ == '\n') 528 SCREWUP("unexpected <newline>"); 529 do { 530 if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) 531 SCREWUP("lost connection"); 532 *cp++ = ch; 533 } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); 534 *cp = 0; 535 536 if (buf[0] == '\01' || buf[0] == '\02') { 537 if (iamremote == 0) 538 (void)write(STDERR_FILENO, 539 buf + 1, strlen(buf + 1)); 540 if (buf[0] == '\02') 541 exit(1); 542 ++errs; 543 continue; 544 } 545 if (buf[0] == 'E') { 546 (void)write(rem, "", 1); 547 return; 548 } 549 550 if (ch == '\n') 551 *--cp = 0; 552 553 cp = buf; 554 if (*cp == 'T') { 555 setimes++; 556 cp++; 557 mtime.tv_sec = strtol(cp, &cp, 10); 558 if (!cp || *cp++ != ' ') 559 SCREWUP("mtime.sec not delimited"); 560 mtime.tv_usec = strtol(cp, &cp, 10); 561 if (!cp || *cp++ != ' ') 562 SCREWUP("mtime.usec not delimited"); 563 atime.tv_sec = strtol(cp, &cp, 10); 564 if (!cp || *cp++ != ' ') 565 SCREWUP("atime.sec not delimited"); 566 atime.tv_usec = strtol(cp, &cp, 10); 567 if (!cp || *cp++ != '\0') 568 SCREWUP("atime.usec not delimited"); 569 (void)write(rem, "", 1); 570 continue; 571 } 572 if (*cp != 'C' && *cp != 'D') { 573 /* 574 * Check for the case "rcp remote:foo\* local:bar". 575 * In this case, the line "No match." can be returned 576 * by the shell before the rcp command on the remote is 577 * executed so the ^Aerror_message convention isn't 578 * followed. 579 */ 580 if (first) { 581 run_err("%s", cp); 582 exit(1); 583 } 584 SCREWUP("expected control record"); 585 } 586 mode = 0; 587 for (++cp; cp < buf + 5; cp++) { 588 if (*cp < '0' || *cp > '7') 589 SCREWUP("bad mode"); 590 mode = (mode << 3) | (*cp - '0'); 591 } 592 if (*cp++ != ' ') 593 SCREWUP("mode not delimited"); 594 595 for (size = 0; isdigit(*cp);) 596 size = size * 10 + (*cp++ - '0'); 597 if (*cp++ != ' ') 598 SCREWUP("size not delimited"); 599 if (targisdir) { 600 if (strlen(targ) + (*targ ? 1 : 0) + strlen(cp) 601 >= sizeof(path)) { 602 run_err("%s%s%s: name too long", targ, 603 *targ ? "/" : "", cp); 604 exit(1); 605 } 606 (void)snprintf(path, sizeof(path), "%s%s%s", targ, 607 *targ ? "/" : "", cp); 608 np = path; 609 } else 610 np = targ; 611 exists = stat(np, &stb) == 0; 612 if (buf[0] == 'D') { 613 int mod_flag = pflag; 614 if (exists) { 615 if (!S_ISDIR(stb.st_mode)) { 616 errno = ENOTDIR; 617 goto bad; 618 } 619 if (pflag) 620 (void)chmod(np, mode); 621 } else { 622 /* Handle copying from a read-only directory */ 623 mod_flag = 1; 624 if (mkdir(np, mode | S_IRWXU) < 0) 625 goto bad; 626 } 627 vect[0] = np; 628 sink(1, vect); 629 if (setimes) { 630 setimes = 0; 631 if (utimes(np, tv) < 0) 632 run_err("%s: set times: %s", 633 np, strerror(errno)); 634 } 635 if (mod_flag) 636 (void)chmod(np, mode); 637 continue; 638 } 639 omode = mode; 640 mode |= S_IWRITE; 641 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 642bad: run_err("%s: %s", np, strerror(errno)); 643 continue; 644 } 645 (void)write(rem, "", 1); 646 if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) { 647 (void)close(ofd); 648 continue; 649 } 650 cp = bp->buf; 651 wrerr = NO; 652 for (count = i = 0; i < size; i += BUFSIZ) { 653 amt = BUFSIZ; 654 if (i + amt > size) 655 amt = size - i; 656 count += amt; 657 do { 658 j = read(rem, cp, amt); 659 if (j <= 0) { 660 run_err("%s", j ? strerror(errno) : 661 "dropped connection"); 662 exit(1); 663 } 664 amt -= j; 665 cp += j; 666 } while (amt > 0); 667 if (count == bp->cnt) { 668 /* Keep reading so we stay sync'd up. */ 669 if (wrerr == NO) { 670 j = write(ofd, bp->buf, count); 671 if (j != (off_t)count) { 672 wrerr = YES; 673 wrerrno = j >= 0 ? EIO : errno; 674 } 675 } 676 count = 0; 677 cp = bp->buf; 678 } 679 } 680 if (count != 0 && wrerr == NO && 681 (j = write(ofd, bp->buf, count)) != (off_t)count) { 682 wrerr = YES; 683 wrerrno = j >= 0 ? EIO : errno; 684 } 685 if (ftruncate(ofd, size)) { 686 run_err("%s: truncate: %s", np, strerror(errno)); 687 wrerr = DISPLAYED; 688 } 689 if (pflag) { 690 if (exists || omode != mode) 691 if (fchmod(ofd, omode)) 692 run_err("%s: set mode: %s", 693 np, strerror(errno)); 694 } else { 695 if (!exists && omode != mode) 696 if (fchmod(ofd, omode & ~mask)) 697 run_err("%s: set mode: %s", 698 np, strerror(errno)); 699 } 700 (void)close(ofd); 701 (void)response(); 702 if (setimes && wrerr == NO) { 703 setimes = 0; 704 if (utimes(np, tv) < 0) { 705 run_err("%s: set times: %s", 706 np, strerror(errno)); 707 wrerr = DISPLAYED; 708 } 709 } 710 switch(wrerr) { 711 case YES: 712 run_err("%s: %s", np, strerror(wrerrno)); 713 break; 714 case NO: 715 (void)write(rem, "", 1); 716 break; 717 case DISPLAYED: 718 break; 719 } 720 } 721screwup: 722 run_err("protocol error: %s", why); 723 exit(1); 724} 725 726int 727response(void) 728{ 729 char ch, *cp, resp, rbuf[BUFSIZ]; 730 731 if (read(rem, &resp, sizeof(resp)) != sizeof(resp)) 732 lostconn(0); 733 734 cp = rbuf; 735 switch(resp) { 736 case 0: /* ok */ 737 return (0); 738 default: 739 *cp++ = resp; 740 /* FALLTHROUGH */ 741 case 1: /* error, followed by error msg */ 742 case 2: /* fatal error, "" */ 743 do { 744 if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) 745 lostconn(0); 746 *cp++ = ch; 747 } while (cp < &rbuf[BUFSIZ] && ch != '\n'); 748 749 if (!iamremote) 750 (void)write(STDERR_FILENO, rbuf, cp - rbuf); 751 ++errs; 752 if (resp == 1) 753 return (-1); 754 exit(1); 755 } 756 /* NOTREACHED */ 757} 758 759void 760usage(void) 761{ 762 (void)fprintf(stderr, "%s\n%s\n", 763 "usage: rcp [-46p] file1 file2", 764 " rcp [-46pr] file ... directory"); 765 exit(1); 766} 767 768#include <stdarg.h> 769 770void 771run_err(const char *fmt, ...) 772{ 773 static FILE *fp; 774 va_list ap; 775 776 ++errs; 777 if (fp == NULL && !(fp = fdopen(rem, "w"))) 778 return; 779 (void)fprintf(fp, "%c", 0x01); 780 (void)fprintf(fp, "rcp: "); 781 va_start(ap, fmt); 782 (void)vfprintf(fp, fmt, ap); 783 va_end(ap); 784 (void)fprintf(fp, "\n"); 785 (void)fflush(fp); 786 787 if (!iamremote) { 788 va_start(ap, fmt); 789 vwarnx(fmt, ap); 790 va_end(ap); 791 } 792 793 va_end(ap); 794} 795