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