4.3 rcp can clobber a source file; does not understand host.user:file
Arnold D. Robbins {EUCC}
arnold at emory.UUCP
Sat Oct 18 01:56:59 AEST 1986
Subject: rcp can clobber a file; does not understand host.user:file notation
Index: /usr/src/bin/rcp.c 4.3BSD
Description:
There are two problems with rcp. First, the man page says that rcp
understands the 4.2 "host.user:file" notation, for compatibility.
In fact, it does not; it only knows about "user at host:file".
Secondly, rcp can clobber a file by attempting to copy it onto
itself, when a "remote" file is actually a local file. See the
example below. (Admittedly, this is a hole more than a "bug", but
it can be a problem in many environments, particularly where there
are shorthand names. E.g., we have two machines, "emoryu1" and
"emoryu2", with nicknames of "u1" and "u2". I clobbered files a
number of times until I learned to be more careful.)
Repeat-By:
Script started on Fri Oct 17 11:40:47 1986
emory> echo this is a file > foo
emory> rcp foo emoryu1.osadr:
emoryu1.osadr: unknown host
emory> rcp foo emory:
rcp: foo: file changed size
emory> cat foo
emory> lf -l foo
-rw-r--r-- 1 arnold eucc 15 Oct 17 11:41 foo
emory> od -c foo
0000000 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
0000017
emory>
script done on Fri Oct 17 11:42:14 1986
Fix:
Apply the following patch:
*** rcp.c Sun Feb 9 22:36:06 1986
--- /usr4/arnold/rcp.c Fri Oct 17 10:16:28 1986
***************
*** 44,49 ****
--- 44,50 ----
struct passwd *getpwuid();
int userid;
int port;
+ struct hostent me;
struct buffer {
int cnt;
***************
*** 64,70 ****
--- 65,74 ----
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");
***************
*** 125,142 ****
*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 */
--- 129,135 ----
*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 */
***************
*** 143,167 ****
*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);
--- 136,168 ----
*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);
***************
*** 184,189 ****
--- 185,194 ----
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" : "",
***************
*** 193,211 ****
*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)
--- 198,211 ----
*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)
***************
*** 704,707 ****
--- 704,852 ----
(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 at 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];
+
+ 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);
+ }
+
+ me = *h;
+ endhostent ();
+ }
+
+ /*
+ * 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[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[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;
}
More information about the Comp.bugs.4bsd.ucb-fixes
mailing list