Subject: bin/30624: cp(1) strips too many trailing slashes
To: None <gnats-admin@netbsd.org, netbsd-bugs@netbsd.org>
From: None <rillig@NetBSD.org>
List: netbsd-bugs
Date: 06/28/2005 16:51:00
>Number:         30624
>Category:       bin
>Synopsis:       cp(1) strips too many trailing slashes
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Jun 28 16:51:00 +0000 2005
>Originator:     rillig@NetBSD.org
>Release:        NetBSD 3.99.4
>Organization:
>Environment:
>Description:

	touch foo
	/bin/cp foo bar/

	These commands copy the foo file into the bar file, although
	according to SUSv3, the string "bar/" should have been
	interpreted like "bar/.".

>Fix:

Index: cp.c
===================================================================
RCS file: /cvsroot/src/bin/cp/cp.c,v
retrieving revision 1.36
diff -u -p -r1.36 cp.c
--- cp.c	26 Jun 2005 19:10:48 -0000	1.36
+++ cp.c	28 Jun 2005 16:45:15 -0000
@@ -76,13 +76,8 @@ __RCSID("$NetBSD: cp.c,v 1.36 2005/06/26
 
 #include "extern.h"
 
-#define	STRIP_TRAILING_SLASH(p) {					\
-        while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/')	\
-                *--(p).p_end = '\0';					\
-}
-
 static char empty[] = "";
-PATH_T to = { to.p_path, empty };
+PATH_T to = { to.p_path, empty, "" };
 
 uid_t myuid;
 int Rflag, fflag, iflag, pflag, rflag, vflag; 
@@ -93,6 +88,7 @@ enum op { FILE_TO_FILE, FILE_TO_DIR, DIR
 int 	main(int, char *[]);
 int 	copy(char *[], enum op, int);
 int 	mastercmp(const FTSENT **, const FTSENT **);
+static void	strip_trailing_slash(PATH_T *);
 
 int
 main(int argc, char *argv[])
@@ -195,7 +191,7 @@ main(int argc, char *argv[])
 		*to.p_end++ = '.';
 		*to.p_end = 0;
 	}
-        STRIP_TRAILING_SLASH(to);
+        strip_trailing_slash(&to);
 	to.target_end = to.p_end;
 
 	/* Set end of argument list for fts(3). */
@@ -347,7 +343,7 @@ copy(char *argv[], enum op type, int fts
 			(void)strncat(tmp, p, nlen);
 			to.p_end = tmp + nlen;
 			*to.p_end = 0;
-			STRIP_TRAILING_SLASH(to);
+			strip_trailing_slash(&to);
 		}
 
 		/* Not an error but need to remember it happened */
@@ -503,3 +499,16 @@ mastercmp(const FTSENT **a, const FTSENT
 		return (1);
 	return (0);
 }
+
+static void
+strip_trailing_slash(PATH_T *p)
+{
+	struct stat st;
+
+	/* only strip the path for existing directories */
+	if (stat(p->p_path, &st) == -1 || !S_ISDIR(st.st_mode))
+		return;
+
+	while (p->p_end > p->p_path + 1 && p->p_end[-1] == '/')
+		*--(p->p_end) = '\0';
+}