arnold@emory.UUCP (Arnold D. Robbins {EUCC}) (11/03/86)
Subject: rcp can clobber local files, does not understand site.person:file Index: /usr/src/bin/rcp.c 4.3BSD Description: A few weeks ago, I posted a fix to rcp. Basically, it did not understand the old "host.person:file" notation, even though the man page said it did, and it could also clobber a local file accidentally, if that file was specified using both local and remote syntax. Repeat-By: Trust me. See the earlier posting for full details. Why-I-Am-Posting-This: There was a bug in my earlier fix; basically I saved copies of pointers to static data, which could in certain circumstances get overwritten. I have reworked it to make full copies of what it needs. The patch below is relative to the original rcp.c. I felt that posting a patch to a patch is less than useful. Fix: Apply the following patch to rcp.c as distributed (your line numbers may vary): *** /tmp/,RCSt1011598 Sun Nov 2 16:28:40 1986 --- rcp.c Sun Nov 2 16:23:03 1986 *************** *** 1,9 **** #ifndef lint ! static char *RCSid = "$Header: rcp.c,v 1.1 86/10/17 11:34:59 root Exp $"; #endif /* * $Log: rcp.c,v $ * Revision 1.1 86/10/17 11:34:59 root * Initial revision * --- 1,17 ---- #ifndef lint ! static char *RCSid = "$Header: rcp.c,v 1.3 86/11/02 16:22:08 root Locked $"; #endif /* * $Log: rcp.c,v $ + * Revision 1.3 86/11/02 16:22:08 root + * Fix to setmyhost(); other gethost*() routines clobber statid return + * data, so must save it to guarantee its integrity for sourceistarget(). ADR. + * + * Revision 1.2 86/10/17 11:35:27 root + * Two fixes. First, accept old 4.2 site.person:file notation. Second, check + * to make sure not trying to copy a local file onto itself. ADR. + * * Revision 1.1 86/10/17 11:34:59 root * Initial revision * *************** *** 55,60 **** --- 63,69 ---- struct passwd *getpwuid(); int userid; int port; + struct hostent me; struct buffer { int cnt; *************** *** 75,81 **** --- 84,93 ---- int i; char buf[BUFSIZ], cmd[16]; struct servent *sp; + int dot = 0; /* use old style host.user:file */ + int junk; + setmyhost (); sp = getservbyname("shell", "tcp"); if (sp == NULL) { fprintf(stderr, "rcp: shell/tcp: unknown service\n"); *************** *** 136,153 **** *targ++ = 0; if (*targ == 0) targ = "."; ! thost = index(argv[argc - 1], '@'); ! if (thost) { ! *thost++ = 0; ! tuser = argv[argc - 1]; ! if (*tuser == '\0') ! tuser = NULL; ! else if (!okname(tuser)) ! exit(1); ! } else { ! thost = argv[argc - 1]; ! tuser = NULL; ! } for (i = 0; i < argc - 1; i++) { src = colon(argv[i]); if (src) { /* remote to remote */ --- 148,154 ---- *targ++ = 0; if (*targ == 0) targ = "."; ! sethostuser (argv[argc - 1], & thost, & tuser, NULL, & dot); for (i = 0; i < argc - 1; i++) { src = colon(argv[i]); if (src) { /* remote to remote */ *************** *** 154,178 **** *src++ = 0; if (*src == 0) src = "."; ! host = index(argv[i], '@'); ! if (host) { ! *host++ = 0; ! suser = argv[i]; ! if (*suser == '\0') ! suser = pwd->pw_name; ! else if (!okname(suser)) ! continue; (void) sprintf(buf, "rsh %s -l %s -n %s %s '%s%s%s:%s'", host, suser, cmd, src, tuser, tuser ? "@" : "", thost, targ); ! } else ! (void) sprintf(buf, "rsh %s -n %s %s '%s%s%s:%s'", ! argv[i], cmd, src, tuser, ! tuser ? "@" : "", ! thost, targ); (void) susystem(buf); } else { /* local to remote */ if (rem == -1) { (void) sprintf(buf, "%s -t %s", cmd, targ); --- 155,187 ---- *src++ = 0; if (*src == 0) src = "."; ! sethostuser (argv[i], & host, ! & suser, pwd->pw_name, & junk); ! if (suser && !okname(suser)) ! continue; ! /* ! * this always passes the user name, ! * but big deal. ! */ ! if (dot) /* set only for dest */ (void) sprintf(buf, "rsh %s -l %s -n %s %s '%s%s%s:%s'", + host, suser, cmd, src, + thost, + tuser ? "." : "", + tuser, + targ); + else + (void) sprintf(buf, "rsh %s -l %s -n %s %s '%s%s%s:%s'", host, suser, cmd, src, tuser, tuser ? "@" : "", thost, targ); ! if (sourceistarget (host, src, thost, targ)) ! continue; (void) susystem(buf); } else { /* local to remote */ + if (sourceistarget (me.h_name, argv[i], + thost, targ)) + continue; if (rem == -1) { (void) sprintf(buf, "%s -t %s", cmd, targ); *************** *** 195,200 **** --- 204,213 ---- for (i = 0; i < argc - 1; i++) { src = colon(argv[i]); if (src == 0) { /* local to local */ + /* + * cp will make sure not to copy + * a file onto itself. + */ (void) sprintf(buf, "/bin/cp%s%s %s %s", iamrecursive ? " -r" : "", pflag ? " -p" : "", *************** *** 204,222 **** *src++ = 0; if (*src == 0) src = "."; ! host = index(argv[i], '@'); ! if (host) { ! *host++ = 0; ! suser = argv[i]; ! if (*suser == '\0') ! suser = pwd->pw_name; ! else if (!okname(suser)) ! continue; ! } else { ! host = argv[i]; ! suser = pwd->pw_name; ! } (void) sprintf(buf, "%s -f %s", cmd, src); rem = rcmd(&host, port, pwd->pw_name, suser, buf, 0); if (rem < 0) --- 217,230 ---- *src++ = 0; if (*src == 0) src = "."; ! sethostuser (argv[i], & host, & suser, ! pwd->pw_name, & junk); ! if (suser && !okname(suser)) ! continue; (void) sprintf(buf, "%s -f %s", cmd, src); + if (sourceistarget (host, src, me.h_name, + argv[argc-1])) + continue; rem = rcmd(&host, port, pwd->pw_name, suser, buf, 0); if (rem < 0) *************** *** 715,718 **** --- 723,911 ---- (void) write(rem, buf, strlen(buf)); if (iamremote == 0) (void) write(2, buf+1, strlen(buf+1)); + } + + /* + * sethostuser + * + * info is string containing "host", "host.user", or "user@host", + * set host to host part, user to user part, or to defuser if no + * user is available. Set dot if name was old style dotted name. + */ + + sethostuser (info, host, user, defuser, dot) + register char *info, **host, **user, *defuser; + int *dot; + { + register char *cp = info; + + while (*cp && *cp != '@' && *cp != '.') + cp++; + + if (! *cp) + { + *host = info; + *user = defuser; + } + else if (*cp == '@') + { + *cp++ = '\0'; + *host = cp; + *user = info; + } + else /* *cp == '.' */ + { + *cp++ = '\0'; + *user = cp; + *host = info; + *dot = 1; + } + return 0; + } + + setmyhost () + { + struct hostent *h; + char buf[BUFSIZ]; + char *mymalloc (); + int i; + + if (gethostname (buf, sizeof buf) < 0) + { + error ("rcp: gethostname: %s\n", sys_errlist[errno]); + exit (1); + } + + sethostent (0); + if ((h = gethostbyname (buf)) == NULL) + { + error ("rcp: gethostbyname (\"%s\") failed.\n", buf); + endhostent (); + exit (1); + } + endhostent (); + + /* + * copy data; other gethost*() routines will overwrite the + * static return data and screw it up, so struct assignment + * is out. sigh. + * + * just save what's important, the name and aliases. + */ + + me.h_name = mymalloc (strlen (h -> h_name) + 1); + (void) strcpy (me.h_name, h -> h_name); + + for (i = 0; h -> h_aliases[i]; i++) + ; /* count aliases */ + if (i) + { + me.h_aliases = (char **) mymalloc (sizeof(char *) * (i + 1)); + for (i = 0; h -> h_aliases[i]; i++) + { + me.h_aliases[i] = mymalloc (strlen (h->h_aliases[i]+1)); + (void) strcpy (me.h_aliases[i], h -> h_aliases[i]); + } + me.h_aliases[i] = NULL; + } + else + me.h_aliases = NULL; + } + + /* + * sourceistarget + * + * attempt to prevent rcp from copying a file onto itself by seeing + * if both hosts are really the current host, and if so, if the files + * are identical. + */ + + sourceistarget (shost, sfile, dhost, dfile) + char *shost, *sfile, *dhost, *dfile; + { + static char localhost[] = "localhost"; + struct stat sbuf1, sbuf2; + int i; + char buf[BUFSIZ], *cp, *rindex (); + + if (strcmp (shost, me.h_name) == 0 || strcmp (shost, localhost) == 0) + goto out1; + else + for (i = 0; me.h_aliases && me.h_aliases[i]; i++) + if (strcmp (shost, me.h_aliases[i]) == 0) + goto out1; + return 0; + + out1: + /* source host is local, check destination host */ + if (strcmp (dhost, me.h_name) == 0 || strcmp (dhost, localhost) == 0) + goto out2; + else + for (i = 0; me.h_aliases && me.h_aliases[i]; i++) + if (strcmp (dhost, me.h_aliases[i]) == 0) + goto out2; + return 0; + + out2: + /* + * At this point, the two hosts are the same, and + * they are actually the local host. + * See if the files are the same. + * + * This gets complicated because the destination could + * be a directory, so if it is, get file name part of the + * source, tack it onto the directory, and check that. + * + * If the stat call fails on the source file, play it safe + * and return 1, forcing the code which checks the status to skip + * copying this particular file. If it fails on the destination + * file, it does not exist yet, so the source and target are + * not the same file. + */ + + if (stat (sfile, & sbuf1) < 0) + { + error ("rcp: %s: %s\n", sfile, sys_errlist[errno]); + return 1; + } + + if (stat (dfile, & sbuf2) < 0) /* destination does not exist */ + return 0; + else if ((sbuf2.st_mode & S_IFMT) == S_IFDIR && + (sbuf1.st_mode & S_IFMT) != S_IFDIR) /* file to directory */ + { + cp = rindex (sfile, '/'); + if (cp) + cp++; + else + cp = sfile; + sprintf (buf, "%s/%s", dfile, cp); + if (stat (buf, & sbuf2) < 0) /* target does not exist */ + return 0; + else + dfile = buf; /* for error message, below */ + } + + if (sbuf1.st_dev == sbuf2.st_dev && sbuf1.st_ino == sbuf2.st_ino) + { + error ("rcp: %s:%s is the same file as %s:%s.\n", + shost, sfile, dhost, dfile); + return 1; + } + else + return 0; + } + + /* mymalloc -- malloc with error checking */ + + char *mymalloc (n) + int n; + { + char *cp, *malloc (); + + if ((cp = malloc (n)) != NULL) + return (cp); + + fprintf (stderr, "rcp: malloc failed\n"); + exit (1); } -- Arnold Robbins CSNET: arnold@emory BITNET: arnold@emoryu1 ARPA: arnold%emory.csnet@csnet-relay.arpa UUCP: { akgua, decvax, gatech, sb1, sb6, sunatl }!emory!arnold