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