xref: /original-bsd/bin/rcp/rcp.c (revision 3b6250d9)
1 /*
2  * Copyright (c) 1983, 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)rcp.c	5.32.1.1 (Berkeley) 08/20/91";
16 #endif /* not lint */
17 
18 /*
19  * rcp
20  */
21 #include <sys/param.h>
22 #include <sys/stat.h>
23 #include <sys/time.h>
24 #include <sys/ioctl.h>
25 #include <sys/socket.h>
26 #include <sys/wait.h>
27 #include <netinet/in.h>
28 #include <netinet/in_systm.h>
29 #include <netinet/ip.h>
30 #include <dirent.h>
31 #include <fcntl.h>
32 #include <signal.h>
33 #include <pwd.h>
34 #include <netdb.h>
35 #include <errno.h>
36 #include <unistd.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include "pathnames.h"
42 
43 #ifdef KERBEROS
44 #include <kerberosIV/des.h>
45 #include <kerberosIV/krb.h>
46 char	dst_realm_buf[REALM_SZ];
47 char	*dest_realm = NULL;
48 int	use_kerberos = 1;
49 CREDENTIALS 	cred;
50 Key_schedule	schedule;
51 extern	char	*krb_realmofhost();
52 #define	OPTIONS	"dfk:prt"
53 #else
54 #define	OPTIONS "dfprt"
55 #endif
56 
57 struct passwd *pwd;
58 u_short	port;
59 uid_t	userid;
60 int errs, rem;
61 int pflag, iamremote, iamrecursive, targetshouldbedirectory;
62 
63 #define	CMDNEEDS	64
64 char cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
65 
66 typedef struct _buf {
67 	int	cnt;
68 	char	*buf;
69 } BUF;
70 
71 void lostconn();
72 
73 main(argc, argv)
74 	int argc;
75 	char **argv;
76 {
77 	extern int optind;
78 	extern char *optarg;
79 	struct servent *sp;
80 	int ch, fflag, tflag;
81 	char *targ, *shell, *colon();
82 
83 	fflag = tflag = 0;
84 	while ((ch = getopt(argc, argv, OPTIONS)) != EOF)
85 		switch(ch) {
86 		/* user-visible flags */
87 		case 'p':			/* preserve access/mod times */
88 			++pflag;
89 			break;
90 		case 'r':
91 			++iamrecursive;
92 			break;
93 #ifdef	KERBEROS
94 		case 'k':
95 			strncpy(dst_realm_buf, optarg, REALM_SZ);
96 			dest_realm = dst_realm_buf;
97 			break;
98 #endif
99 		/* rshd-invoked options (server) */
100 		case 'd':
101 			targetshouldbedirectory = 1;
102 			break;
103 		case 'f':			/* "from" */
104 			iamremote = 1;
105 			fflag = 1;
106 			break;
107 		case 't':			/* "to" */
108 			iamremote = 1;
109 			tflag = 1;
110 			break;
111 
112 		case '?':
113 		default:
114 			usage();
115 		}
116 	argc -= optind;
117 	argv += optind;
118 
119 #ifdef KERBEROS
120 	shell = "kshell";
121 	sp = getservbyname(shell, "tcp");
122 	if (sp == NULL) {
123 		char	msgbuf[64];
124 		use_kerberos = 0;
125 		(void)snprintf(msgbuf, sizeof(msgbuf),
126 		    "can't get entry for %s/tcp service", shell);
127 		old_warning(msgbuf);
128 		sp = getservbyname(shell = "shell", "tcp");
129 	}
130 #else
131 	sp = getservbyname(shell = "shell", "tcp");
132 #endif
133 	if (sp == NULL) {
134 		(void)fprintf(stderr, "rcp: %s/tcp: unknown service\n", shell);
135 		exit(1);
136 	}
137 	port = sp->s_port;
138 
139 	if (!(pwd = getpwuid(userid = getuid()))) {
140 		(void)fprintf(stderr, "rcp: unknown user %d.\n", (int)userid);
141 		exit(1);
142 	}
143 
144 	if (fflag) {
145 		/* follow "protocol", send data */
146 		(void)response();
147 		(void)setuid(userid);
148 		source(argc, argv);
149 		exit(errs);
150 	}
151 
152 	if (tflag) {
153 		/* receive data */
154 		(void)setuid(userid);
155 		sink(argc, argv);
156 		exit(errs);
157 	}
158 
159 	if (argc < 2)
160 		usage();
161 	if (argc > 2)
162 		targetshouldbedirectory = 1;
163 
164 	rem = -1;
165 	/* command to be executed on remote system using "rsh" */
166 #ifdef	KERBEROS
167 	(void)snprintf(cmd, sizeof(cmd),
168 	    "rcp%s%s%s%s", iamrecursive ? " -r" : "",
169 	    "",
170 	    pflag ? " -p" : "", targetshouldbedirectory ? " -d" : "");
171 #else
172 	(void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s",
173 	    iamrecursive ? " -r" : "", pflag ? " -p" : "",
174 	    targetshouldbedirectory ? " -d" : "");
175 #endif
176 
177 	(void)signal(SIGPIPE, lostconn);
178 
179 	if (targ = colon(argv[argc - 1]))
180 		toremote(targ, argc, argv);	/* destination is remote host */
181 	else {
182 		tolocal(argc, argv);		/* destination is local host */
183 		if (targetshouldbedirectory)
184 			verifydir(argv[argc - 1]);
185 	}
186 	exit(errs);
187 }
188 
189 toremote(targ, argc, argv)
190 	char *targ;
191 	int argc;
192 	char **argv;
193 {
194 	int i, len, tos;
195 	char *bp, *host, *src, *suser, *thost, *tuser;
196 	char *colon();
197 
198 	*targ++ = 0;
199 	if (*targ == 0)
200 		targ = ".";
201 
202 	if (thost = index(argv[argc - 1], '@')) {
203 		/* user@host */
204 		*thost++ = 0;
205 		tuser = argv[argc - 1];
206 		if (*tuser == '\0')
207 			tuser = NULL;
208 		else if (!okname(tuser))
209 			exit(1);
210 	} else {
211 		thost = argv[argc - 1];
212 		tuser = NULL;
213 	}
214 
215 	for (i = 0; i < argc - 1; i++) {
216 		src = colon(argv[i]);
217 		if (src) {			/* remote to remote */
218 			*src++ = 0;
219 			if (*src == 0)
220 				src = ".";
221 			host = index(argv[i], '@');
222 			len = strlen(_PATH_RSH) + strlen(argv[i]) +
223 			    strlen(src) + (tuser ? strlen(tuser) : 0) +
224 			    strlen(thost) + strlen(targ) + CMDNEEDS + 20;
225 			if (!(bp = malloc(len)))
226 				nospace();
227 			if (host) {
228 				*host++ = 0;
229 				suser = argv[i];
230 				if (*suser == '\0')
231 					suser = pwd->pw_name;
232 				else if (!okname(suser))
233 					continue;
234 				(void)snprintf(bp, len,
235 				    "%s %s -l %s -n %s %s '%s%s%s:%s'",
236 				    _PATH_RSH, host, suser, cmd, src,
237 				    tuser ? tuser : "", tuser ? "@" : "",
238 				    thost, targ);
239 			} else
240 				(void)snprintf(bp, len,
241 				    "%s %s -n %s %s '%s%s%s:%s'",
242 				    _PATH_RSH, argv[i], cmd, src,
243 				    tuser ? tuser : "", tuser ? "@" : "",
244 				    thost, targ);
245 			(void)susystem(bp);
246 			(void)free(bp);
247 		} else {			/* local to remote */
248 			if (rem == -1) {
249 				len = strlen(targ) + CMDNEEDS + 20;
250 				if (!(bp = malloc(len)))
251 					nospace();
252 				(void)snprintf(bp, len, "%s -t %s", cmd, targ);
253 				host = thost;
254 #ifdef KERBEROS
255 				if (use_kerberos)
256 					rem = kerberos(&host, bp,
257 					    pwd->pw_name,
258 					    tuser ? tuser : pwd->pw_name);
259 				else
260 #endif
261 					rem = rcmd(&host, port, pwd->pw_name,
262 					    tuser ? tuser : pwd->pw_name,
263 					    bp, 0);
264 				if (rem < 0)
265 					exit(1);
266 				tos = IPTOS_THROUGHPUT;
267 				if (setsockopt(rem, IPPROTO_IP, IP_TOS,
268 				    (char *)&tos, sizeof(int)) < 0)
269 					perror("rcp: setsockopt TOS (ignored)");
270 				if (response() < 0)
271 					exit(1);
272 				(void)free(bp);
273 				(void)setuid(userid);
274 			}
275 			source(1, argv+i);
276 		}
277 	}
278 }
279 
280 tolocal(argc, argv)
281 	int argc;
282 	char **argv;
283 {
284 	int i, len, tos;
285 	char *bp, *host, *src, *suser;
286 	char *colon();
287 
288 	for (i = 0; i < argc - 1; i++) {
289 		if (!(src = colon(argv[i]))) {	/* local to local */
290 			len = strlen(_PATH_CP) + strlen(argv[i]) +
291 			    strlen(argv[argc - 1]) + 20;
292 			if (!(bp = malloc(len)))
293 				nospace();
294 			(void)snprintf(bp, len, "%s%s%s %s %s", _PATH_CP,
295 			    iamrecursive ? " -r" : "", pflag ? " -p" : "",
296 			    argv[i], argv[argc - 1]);
297 			(void)susystem(bp);
298 			(void)free(bp);
299 			continue;
300 		}
301 		*src++ = 0;
302 		if (*src == 0)
303 			src = ".";
304 		host = index(argv[i], '@');
305 		if (host) {
306 			*host++ = 0;
307 			suser = argv[i];
308 			if (*suser == '\0')
309 				suser = pwd->pw_name;
310 			else if (!okname(suser))
311 				continue;
312 		} else {
313 			host = argv[i];
314 			suser = pwd->pw_name;
315 		}
316 		len = strlen(src) + CMDNEEDS + 20;
317 		if (!(bp = malloc(len)))
318 			nospace();
319 		(void)snprintf(bp, len, "%s -f %s", cmd, src);
320 #ifdef KERBEROS
321 		if (use_kerberos)
322 			rem = kerberos(&host, bp, pwd->pw_name, suser);
323 		else
324 #endif
325 			rem = rcmd(&host, port, pwd->pw_name, suser, bp, 0);
326 		(void)free(bp);
327 		if (rem < 0)
328 			continue;
329 		(void)seteuid(userid);
330 		tos = IPTOS_THROUGHPUT;
331 		if (setsockopt(rem, IPPROTO_IP, IP_TOS,
332 		    (char *)&tos, sizeof(int)) < 0)
333 			perror("rcp: setsockopt TOS (ignored)");
334 		sink(1, argv + argc - 1);
335 		(void)seteuid(0);
336 		(void)close(rem);
337 		rem = -1;
338 	}
339 }
340 
341 verifydir(cp)
342 	char *cp;
343 {
344 	struct stat stb;
345 
346 	if (stat(cp, &stb) >= 0) {
347 		if ((stb.st_mode & S_IFMT) == S_IFDIR)
348 			return;
349 		errno = ENOTDIR;
350 	}
351 	error("rcp: %s: %s.\n", cp, strerror(errno));
352 	exit(1);
353 }
354 
355 char *
356 colon(cp)
357 	register char *cp;
358 {
359 	for (; *cp; ++cp) {
360 		if (*cp == ':')
361 			return(cp);
362 		if (*cp == '/')
363 			return(0);
364 	}
365 	return(0);
366 }
367 
368 okname(cp0)
369 	char *cp0;
370 {
371 	register char *cp = cp0;
372 	register int c;
373 
374 	do {
375 		c = *cp;
376 		if (c & 0200)
377 			goto bad;
378 		if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
379 			goto bad;
380 	} while (*++cp);
381 	return(1);
382 bad:
383 	(void)fprintf(stderr, "rcp: invalid user name %s\n", cp0);
384 	return(0);
385 }
386 
387 susystem(s)
388 	char *s;
389 {
390 	int status, pid, w;
391 	register sig_t istat, qstat;
392 
393 	if ((pid = vfork()) == 0) {
394 		(void)setuid(userid);
395 		execl(_PATH_BSHELL, "sh", "-c", s, (char *)0);
396 		_exit(127);
397 	}
398 	istat = signal(SIGINT, SIG_IGN);
399 	qstat = signal(SIGQUIT, SIG_IGN);
400 	while ((w = wait(&status)) != pid && w != -1)
401 		;
402 	if (w == -1)
403 		status = -1;
404 	(void)signal(SIGINT, istat);
405 	(void)signal(SIGQUIT, qstat);
406 	return(status);
407 }
408 
409 source(argc, argv)
410 	int argc;
411 	char **argv;
412 {
413 	struct stat stb;
414 	static BUF buffer;
415 	BUF *bp;
416 	off_t i;
417 	int x, readerr, f, amt;
418 	char *last, *name, buf[BUFSIZ];
419 	BUF *allocbuf();
420 
421 	for (x = 0; x < argc; x++) {
422 		name = argv[x];
423 		if ((f = open(name, O_RDONLY, 0)) < 0) {
424 			error("rcp: %s: %s\n", name, strerror(errno));
425 			continue;
426 		}
427 		if (fstat(f, &stb) < 0)
428 			goto notreg;
429 		switch (stb.st_mode&S_IFMT) {
430 
431 		case S_IFREG:
432 			break;
433 
434 		case S_IFDIR:
435 			if (iamrecursive) {
436 				(void)close(f);
437 				rsource(name, &stb);
438 				continue;
439 			}
440 			/* FALLTHROUGH */
441 		default:
442 notreg:			(void)close(f);
443 			error("rcp: %s: not a plain file\n", name);
444 			continue;
445 		}
446 		last = rindex(name, '/');
447 		if (last == 0)
448 			last = name;
449 		else
450 			last++;
451 		if (pflag) {
452 			/*
453 			 * Make it compatible with possible future
454 			 * versions expecting microseconds.
455 			 */
456 			(void)snprintf(buf, sizeof(buf),
457 			    "T%ld 0 %ld 0\n", stb.st_mtime, stb.st_atime);
458 			(void)write(rem, buf, (int)strlen(buf));
459 			if (response() < 0) {
460 				(void)close(f);
461 				continue;
462 			}
463 		}
464 		(void)snprintf(buf, sizeof(buf),
465 		    "C%04o %ld %s\n", stb.st_mode&07777, stb.st_size, last);
466 		(void)write(rem, buf, (int)strlen(buf));
467 		if (response() < 0) {
468 			(void)close(f);
469 			continue;
470 		}
471 		if ((bp = allocbuf(&buffer, f, BUFSIZ)) == 0) {
472 			(void)close(f);
473 			continue;
474 		}
475 		readerr = 0;
476 		for (i = 0; i < stb.st_size; i += bp->cnt) {
477 			amt = bp->cnt;
478 			if (i + amt > stb.st_size)
479 				amt = stb.st_size - i;
480 			if (readerr == 0 && read(f, bp->buf, amt) != amt)
481 				readerr = errno;
482 			(void)write(rem, bp->buf, amt);
483 		}
484 		(void)close(f);
485 		if (readerr == 0)
486 			(void)write(rem, "", 1);
487 		else
488 			error("rcp: %s: %s\n", name, strerror(readerr));
489 		(void)response();
490 	}
491 }
492 
493 rsource(name, statp)
494 	char *name;
495 	struct stat *statp;
496 {
497 	DIR *dirp;
498 	struct dirent *dp;
499 	char *last, *vect[1], path[MAXPATHLEN];
500 
501 	if (!(dirp = opendir(name))) {
502 		error("rcp: %s: %s\n", name, strerror(errno));
503 		return;
504 	}
505 	last = rindex(name, '/');
506 	if (last == 0)
507 		last = name;
508 	else
509 		last++;
510 	if (pflag) {
511 		(void)snprintf(path, sizeof(path),
512 		    "T%ld 0 %ld 0\n", statp->st_mtime, statp->st_atime);
513 		(void)write(rem, path, (int)strlen(path));
514 		if (response() < 0) {
515 			closedir(dirp);
516 			return;
517 		}
518 	}
519 	(void)snprintf(path, sizeof(path),
520 	    "D%04o %d %s\n", statp->st_mode&07777, 0, last);
521 	(void)write(rem, path, (int)strlen(path));
522 	if (response() < 0) {
523 		closedir(dirp);
524 		return;
525 	}
526 	while (dp = readdir(dirp)) {
527 		if (dp->d_ino == 0)
528 			continue;
529 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
530 			continue;
531 		if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) {
532 			error("%s/%s: name too long.\n", name, dp->d_name);
533 			continue;
534 		}
535 		(void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name);
536 		vect[0] = path;
537 		source(1, vect);
538 	}
539 	closedir(dirp);
540 	(void)write(rem, "E\n", 2);
541 	(void)response();
542 }
543 
544 response()
545 {
546 	register char *cp;
547 	char ch, resp, rbuf[BUFSIZ];
548 
549 	if (read(rem, &resp, sizeof(resp)) != sizeof(resp))
550 		lostconn();
551 
552 	cp = rbuf;
553 	switch(resp) {
554 	case 0:				/* ok */
555 		return(0);
556 	default:
557 		*cp++ = resp;
558 		/* FALLTHROUGH */
559 	case 1:				/* error, followed by err msg */
560 	case 2:				/* fatal error, "" */
561 		do {
562 			if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
563 				lostconn();
564 			*cp++ = ch;
565 		} while (cp < &rbuf[BUFSIZ] && ch != '\n');
566 
567 		if (!iamremote)
568 			(void)write(2, rbuf, cp - rbuf);
569 		++errs;
570 		if (resp == 1)
571 			return(-1);
572 		exit(1);
573 	}
574 	/*NOTREACHED*/
575 }
576 
577 void
578 lostconn()
579 {
580 	if (!iamremote)
581 		(void)fprintf(stderr, "rcp: lost connection\n");
582 	exit(1);
583 }
584 
585 sink(argc, argv)
586 	int argc;
587 	char **argv;
588 {
589 	register char *cp;
590 	static BUF buffer;
591 	struct stat stb;
592 	struct timeval tv[2];
593 	enum { YES, NO, DISPLAYED } wrerr;
594 	BUF *bp, *allocbuf();
595 	off_t i, j;
596 	char ch, *targ, *why;
597 	int amt, count, exists, first, mask, mode;
598 	int ofd, setimes, size, targisdir;
599 	char *np, *vect[1], buf[BUFSIZ];
600 
601 #define	atime	tv[0]
602 #define	mtime	tv[1]
603 #define	SCREWUP(str)	{ why = str; goto screwup; }
604 
605 	setimes = targisdir = 0;
606 	mask = umask(0);
607 	if (!pflag)
608 		(void)umask(mask);
609 	if (argc != 1) {
610 		error("rcp: ambiguous target\n");
611 		exit(1);
612 	}
613 	targ = *argv;
614 	if (targetshouldbedirectory)
615 		verifydir(targ);
616 	(void)write(rem, "", 1);
617 	if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
618 		targisdir = 1;
619 	for (first = 1;; first = 0) {
620 		cp = buf;
621 		if (read(rem, cp, 1) <= 0)
622 			return;
623 		if (*cp++ == '\n')
624 			SCREWUP("unexpected <newline>");
625 		do {
626 			if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
627 				SCREWUP("lost connection");
628 			*cp++ = ch;
629 		} while (cp < &buf[BUFSIZ - 1] && ch != '\n');
630 		*cp = 0;
631 
632 		if (buf[0] == '\01' || buf[0] == '\02') {
633 			if (iamremote == 0)
634 				(void)write(2, buf + 1, (int)strlen(buf + 1));
635 			if (buf[0] == '\02')
636 				exit(1);
637 			errs++;
638 			continue;
639 		}
640 		if (buf[0] == 'E') {
641 			(void)write(rem, "", 1);
642 			return;
643 		}
644 
645 		if (ch == '\n')
646 			*--cp = 0;
647 
648 #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
649 		cp = buf;
650 		if (*cp == 'T') {
651 			setimes++;
652 			cp++;
653 			getnum(mtime.tv_sec);
654 			if (*cp++ != ' ')
655 				SCREWUP("mtime.sec not delimited");
656 			getnum(mtime.tv_usec);
657 			if (*cp++ != ' ')
658 				SCREWUP("mtime.usec not delimited");
659 			getnum(atime.tv_sec);
660 			if (*cp++ != ' ')
661 				SCREWUP("atime.sec not delimited");
662 			getnum(atime.tv_usec);
663 			if (*cp++ != '\0')
664 				SCREWUP("atime.usec not delimited");
665 			(void)write(rem, "", 1);
666 			continue;
667 		}
668 		if (*cp != 'C' && *cp != 'D') {
669 			/*
670 			 * Check for the case "rcp remote:foo\* local:bar".
671 			 * In this case, the line "No match." can be returned
672 			 * by the shell before the rcp command on the remote is
673 			 * executed so the ^Aerror_message convention isn't
674 			 * followed.
675 			 */
676 			if (first) {
677 				error("%s\n", cp);
678 				exit(1);
679 			}
680 			SCREWUP("expected control record");
681 		}
682 		mode = 0;
683 		for (++cp; cp < buf + 5; cp++) {
684 			if (*cp < '0' || *cp > '7')
685 				SCREWUP("bad mode");
686 			mode = (mode << 3) | (*cp - '0');
687 		}
688 		if (*cp++ != ' ')
689 			SCREWUP("mode not delimited");
690 		size = 0;
691 		while (isdigit(*cp))
692 			size = size * 10 + (*cp++ - '0');
693 		if (*cp++ != ' ')
694 			SCREWUP("size not delimited");
695 		if (targisdir) {
696 			static char *namebuf;
697 			static int cursize;
698 			size_t need;
699 
700 			need = strlen(targ) + strlen(cp) + 250;
701 			if (need > cursize) {
702 				if (!(namebuf = malloc(need)))
703 					error("out of memory\n");
704 			}
705 			(void)snprintf(namebuf, need, "%s%s%s", targ,
706 			    *targ ? "/" : "", cp);
707 			np = namebuf;
708 		}
709 		else
710 			np = targ;
711 		exists = stat(np, &stb) == 0;
712 		if (buf[0] == 'D') {
713 			if (exists) {
714 				if ((stb.st_mode&S_IFMT) != S_IFDIR) {
715 					errno = ENOTDIR;
716 					goto bad;
717 				}
718 				if (pflag)
719 					(void)chmod(np, mode);
720 			} else if (mkdir(np, mode) < 0)
721 				goto bad;
722 			vect[0] = np;
723 			sink(1, vect);
724 			if (setimes) {
725 				setimes = 0;
726 				if (utimes(np, tv) < 0)
727 				    error("rcp: can't set times on %s: %s\n",
728 					np, strerror(errno));
729 			}
730 			continue;
731 		}
732 		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
733 bad:			error("rcp: %s: %s\n", np, strerror(errno));
734 			continue;
735 		}
736 		if (exists && pflag)
737 			(void)fchmod(ofd, mode);
738 		(void)write(rem, "", 1);
739 		if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == 0) {
740 			(void)close(ofd);
741 			continue;
742 		}
743 		cp = bp->buf;
744 		count = 0;
745 		wrerr = NO;
746 		for (i = 0; i < size; i += BUFSIZ) {
747 			amt = BUFSIZ;
748 			if (i + amt > size)
749 				amt = size - i;
750 			count += amt;
751 			do {
752 				j = read(rem, cp, amt);
753 				if (j <= 0) {
754 					error("rcp: %s\n",
755 					    j ? strerror(errno) :
756 					    "dropped connection");
757 					exit(1);
758 				}
759 				amt -= j;
760 				cp += j;
761 			} while (amt > 0);
762 			if (count == bp->cnt) {
763 				if (wrerr == NO &&
764 				    write(ofd, bp->buf, count) != count)
765 					wrerr = YES;
766 				count = 0;
767 				cp = bp->buf;
768 			}
769 		}
770 		if (count != 0 && wrerr == NO &&
771 		    write(ofd, bp->buf, count) != count)
772 			wrerr = YES;
773 		if (ftruncate(ofd, size)) {
774 			error("rcp: can't truncate %s: %s\n", np,
775 			    strerror(errno));
776 			wrerr = DISPLAYED;
777 		}
778 		(void)close(ofd);
779 		(void)response();
780 		if (setimes && wrerr == NO) {
781 			setimes = 0;
782 			if (utimes(np, tv) < 0) {
783 				error("rcp: can't set times on %s: %s\n",
784 				    np, strerror(errno));
785 				wrerr = DISPLAYED;
786 			}
787 		}
788 		switch(wrerr) {
789 		case YES:
790 			error("rcp: %s: %s\n", np, strerror(errno));
791 			break;
792 		case NO:
793 			(void)write(rem, "", 1);
794 			break;
795 		case DISPLAYED:
796 			break;
797 		}
798 	}
799 screwup:
800 	error("rcp: protocol screwup: %s\n", why);
801 	exit(1);
802 }
803 
804 BUF *
805 allocbuf(bp, fd, blksize)
806 	BUF *bp;
807 	int fd, blksize;
808 {
809 	struct stat stb;
810 	size_t size;
811 
812 	if (fstat(fd, &stb) < 0) {
813 		error("rcp: fstat: %s\n", strerror(errno));
814 		return(0);
815 	}
816 	size = roundup(stb.st_blksize, blksize);
817 	if (size == 0)
818 		size = blksize;
819 	if (bp->cnt < size) {
820 		if (bp->buf != 0)
821 			free(bp->buf);
822 		bp->buf = malloc(size);
823 		if (!bp->buf) {
824 			error("rcp: malloc: out of memory\n");
825 			return(0);
826 		}
827 	}
828 	bp->cnt = size;
829 	return(bp);
830 }
831 
832 /* VARARGS1 */
833 error(fmt, a1, a2, a3)
834 	char *fmt;
835 	int a1, a2, a3;
836 {
837 	static FILE *fp;
838 
839 	++errs;
840 	if (!fp && !(fp = fdopen(rem, "w")))
841 		return;
842 	(void)fprintf(fp, "%c", 0x01);
843 	(void)fprintf(fp, fmt, a1, a2, a3);
844 	(void)fflush(fp);
845 	if (!iamremote)
846 		(void)fprintf(stderr, fmt, a1, a2, a3);
847 }
848 
849 nospace()
850 {
851 	(void)fprintf(stderr, "rcp: out of memory.\n");
852 	exit(1);
853 }
854 
855 
856 usage()
857 {
858 #ifdef KERBEROS
859 	(void)fprintf(stderr, "%s\n\t%s\n",
860 	    "usage: rcp [-k realm] [-p] f1 f2",
861 	    "or: rcp [-k realm] [-rp] f1 ... fn directory");
862 #else
863 	(void)fprintf(stderr,
864 	    "usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn directory\n");
865 #endif
866 	exit(1);
867 }
868 
869 #ifdef KERBEROS
870 old_warning(str)
871 	char *str;
872 {
873 	(void)fprintf(stderr, "rcp: warning: %s, using standard rcp\n", str);
874 }
875 
876 int
877 kerberos(host, bp, locuser, user)
878 
879 	char **host, *bp, *locuser, *user;
880 {
881 	struct servent *sp;
882 
883 again:
884 	if (use_kerberos) {
885 		rem = KSUCCESS;
886 		errno = 0;
887 		if (dest_realm == NULL)
888 			dest_realm = krb_realmofhost(*host);
889 
890 			rem = krcmd(
891 				host, port,
892 				user, bp, 0, dest_realm);
893 
894 		if (rem < 0) {
895 			use_kerberos = 0;
896 			sp = getservbyname("shell", "tcp");
897 			if (sp == NULL) {
898 				(void)fprintf(stderr,
899 			    	    "rcp: unknown service shell/tcp\n");
900 				exit(1);
901 			}
902 			if (errno == ECONNREFUSED)
903 				old_warning(
904 				    "remote host doesn't support Kerberos");
905 
906 			if (errno == ENOENT)
907 				old_warning(
908 				    "Can't provide Kerberos auth data");
909 			port = sp->s_port;
910 			goto again;
911 		}
912 	} else {
913 		rem = rcmd(host, sp->s_port, locuser, user, bp, 0);
914 	}
915 	return(rem);
916 }
917 #endif /* KERBEROS */
918