Browse Source

copy_file: handle "cp /dev/foo file" (almost) compatibly to coreutils.
(almost because we do not copy mode, which is probably wasn't intended).
+61 bytes.

Denis Vlasenko 16 years ago
parent
commit
b9ad75fa60

+ 5 - 1
coreutils/mv.c

@@ -71,7 +71,8 @@ int mv_main(int argc, char **argv)
 		}
 
  DO_MOVE:
-		if (dest_exists && !(flags & OPT_FILEUTILS_FORCE)
+		if (dest_exists
+		 && !(flags & OPT_FILEUTILS_FORCE)
 		 && ((access(dest, W_OK) < 0 && isatty(0))
 		    || (flags & OPT_FILEUTILS_INTERACTIVE))
 		) {
@@ -108,6 +109,9 @@ int mv_main(int argc, char **argv)
 						goto RET_1;
 					}
 				}
+				/* FILEUTILS_RECUR also prevents nasties like 
+				 * "read from device and write contents to dst"
+				 * instead of "create same device node" */
 				copy_flag = FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS;
 #if ENABLE_SELINUX
 				copy_flag |= FILEUTILS_PRESERVE_SECURITY_CONTEXT;

+ 19 - 15
include/libbb.h

@@ -223,8 +223,27 @@ extern char *skip_non_whitespace(const char *);
 //TODO: supply a pointer to char[11] buffer (avoid statics)?
 extern const char *bb_mode_string(mode_t mode);
 extern int is_directory(const char *name, int followLinks, struct stat *statBuf);
+enum {	/* DO NOT CHANGE THESE VALUES!  cp.c, mv.c, install.c depend on them. */
+	FILEUTILS_PRESERVE_STATUS = 1,
+	FILEUTILS_DEREFERENCE = 2,
+	FILEUTILS_RECUR = 4,
+	FILEUTILS_FORCE = 8,
+	FILEUTILS_INTERACTIVE = 0x10,
+	FILEUTILS_MAKE_HARDLINK = 0x20,
+	FILEUTILS_MAKE_SOFTLINK = 0x40,
+#if ENABLE_SELINUX
+	FILEUTILS_PRESERVE_SECURITY_CONTEXT = 0x80,
+	FILEUTILS_SET_SECURITY_CONTEXT = 0x100
+#endif
+};
+#define FILEUTILS_CP_OPTSTR "pdRfils" USE_SELINUX("c")
 extern int remove_file(const char *path, int flags);
+/* NB: without FILEUTILS_RECUR in flags, it will basically "cat"
+ * the source, not copy (unless "source" is a directory).
+ * This makes "cp /dev/null file" and "install /dev/null file" (!!!)
+ * work coreutils-compatibly. */
 extern int copy_file(const char *source, const char *dest, int flags);
+
 enum {
 	ACTION_RECURSE        = (1 << 0),
 	ACTION_FOLLOWLINKS    = (1 << 1),
@@ -1170,21 +1189,6 @@ void *md5_end(void *resbuf, md5_ctx_t *ctx);
 uint32_t *crc32_filltable(uint32_t *tbl256, int endian);
 
 
-enum {	/* DO NOT CHANGE THESE VALUES!  cp.c, mv.c, install.c depend on them. */
-	FILEUTILS_PRESERVE_STATUS = 1,
-	FILEUTILS_DEREFERENCE = 2,
-	FILEUTILS_RECUR = 4,
-	FILEUTILS_FORCE = 8,
-	FILEUTILS_INTERACTIVE = 0x10,
-	FILEUTILS_MAKE_HARDLINK = 0x20,
-	FILEUTILS_MAKE_SOFTLINK = 0x40,
-#if ENABLE_SELINUX
-	FILEUTILS_PRESERVE_SECURITY_CONTEXT = 0x80,
-	FILEUTILS_SET_SECURITY_CONTEXT = 0x100
-#endif
-};
-
-#define FILEUTILS_CP_OPTSTR "pdRfils" USE_SELINUX("c")
 extern const char *applet_name;
 /* "BusyBox vN.N.N (timestamp or extra_version)" */
 extern const char bb_banner[];

+ 29 - 8
libbb/copy_file.c

@@ -81,6 +81,7 @@ int copy_file(const char *source, const char *dest, int flags)
 	signed char dest_exists = 0;
 	signed char ovr;
 
+/* Inverse of cp -d ("cp without -d") */
 #define FLAGS_DEREF (flags & FILEUTILS_DEREFERENCE)
 
 	if ((FLAGS_DEREF ? stat : lstat)(source, &source_stat) < 0) {
@@ -229,12 +230,22 @@ int copy_file(const char *source, const char *dest, int flags)
 		return 0;
 	}
 
-	if (S_ISREG(source_stat.st_mode)
-	 /* DEREF uses stat, which never returns S_ISLNK() == true. */
+	if (/* "cp thing1 thing2" without -R: just open and read() from thing1 */
+	    !(flags & FILEUTILS_RECUR)
+	    /* "cp [-opts] regular_file thing2" */
+	 || S_ISREG(source_stat.st_mode)
+	 /* DEREF uses stat, which never returns S_ISLNK() == true.
+	  * So the below is never true: */
 	 /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */
 	) {
 		int src_fd;
 		int dst_fd;
+		mode_t new_mode;
+
+		if (!FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) {
+			/* "cp -d symlink dst": create a link */
+			goto dont_cat;
+		}
 
 		if (ENABLE_FEATURE_PRESERVE_HARDLINKS && !FLAGS_DEREF) {
 			const char *link_target;
@@ -258,18 +269,24 @@ int copy_file(const char *source, const char *dest, int flags)
 		if (src_fd < 0)
 			return -1;
 
+		/* Do not try to open with weird mode fields */
+		new_mode = source_stat.st_mode;
+		if (!S_ISREG(source_stat.st_mode))
+			new_mode = 0666;
+
 		/* POSIX way is a security problem versus symlink attacks,
 		 * we do it only for non-symlinks, and only for non-recursive,
 		 * non-interactive cp. NB: it is still racy
 		 * for "cp file /home/bad_user/file" case
 		 * (user can rm file and create a link to /etc/passwd) */
 		if (DO_POSIX_CP
-		 || (dest_exists && !(flags & (FILEUTILS_RECUR|FILEUTILS_INTERACTIVE))
+		 || (dest_exists
+		     && !(flags & (FILEUTILS_RECUR|FILEUTILS_INTERACTIVE))
 		     && !S_ISLNK(dest_stat.st_mode))
 		) {
-			dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, source_stat.st_mode);
+			dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode);
 		} else  /* safe way: */
-			dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, source_stat.st_mode);
+			dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
 		if (dst_fd == -1) {
 			ovr = ask_and_unlink(dest, flags);
 			if (ovr <= 0) {
@@ -277,7 +294,7 @@ int copy_file(const char *source, const char *dest, int flags)
 				return ovr;
 			}
 			/* It shouldn't exist. If it exists, do not open (symlink attack?) */
-			dst_fd = open3_or_warn(dest, O_WRONLY|O_CREAT|O_EXCL, source_stat.st_mode);
+			dst_fd = open3_or_warn(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
 			if (dst_fd < 0) {
 				close(src_fd);
 				return -1;
@@ -285,8 +302,7 @@ int copy_file(const char *source, const char *dest, int flags)
 		}
 
 #if ENABLE_SELINUX
-		if (((flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT)
-		    || (flags & FILEUTILS_SET_SECURITY_CONTEXT))
+		if ((flags & (FILEUTILS_PRESERVE_SECURITY_CONTEXT|FILEUTILS_SET_SECURITY_CONTEXT))
 		 && is_selinux_enabled() > 0
 		) {
 			security_context_t con;
@@ -313,8 +329,13 @@ int copy_file(const char *source, const char *dest, int flags)
 		}
 		/* ...but read size is already checked by bb_copyfd_eof */
 		close(src_fd);
+		/* "cp /dev/something new_file" should not
+		 * copy mode of /dev/something */
+		if (!S_ISREG(source_stat.st_mode))
+			return retval;
 		goto preserve_mode_ugid_time;
 	}
+ dont_cat:
 
 	/* Source is a symlink or a special file */
 	/* We are lazy here, a bit lax with races... */

+ 2 - 1
testsuite/cp/cp-a-files-to-dir

@@ -2,7 +2,8 @@ echo file number one > file1
 echo file number two > file2
 ln -s file2 link1
 mkdir dir1
-touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3
+# why???
+#touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3
 mkdir there
 busybox cp -a file1 file2 link1 dir1 there
 test -f there/file1

+ 2 - 0
testsuite/cp/cp-dev-file

@@ -0,0 +1,2 @@
+busybox cp /dev/null foo
+test -f foo

+ 6 - 1
testsuite/cp/cp-does-not-copy-unreadable-file

@@ -1,6 +1,11 @@
 touch foo
 chmod a-r foo
 set +e
-busybox cp foo bar
+if test `id -u` = 0; then
+    # run as user with nonzero uid
+    setuidgid 1 busybox cp foo bar
+else
+    busybox cp foo bar
+fi
 set -e
 test ! -f bar