cpio: implement -R/--owner

Implement -R/--owner to force ownership of files.

function                                             old     new   delta
cpio_main                                            532     586     +54
get_header_cpio                                      909     939     +30
print                                                 36      65     +29
cpio_o                                               804     832     +28
cpio_TRAILER                                           -      11     +11
packed_usage                                       30667   30662      -5
static.trailer                                        11       -     -11
------------------------------------------------------------------------------
(add/remove: 1/1 grow/shrink: 4/1 up/down: 152/-16)           Total: 136 bytes

Signed-off-by: Aaro Koskinen <aaro.koskinen@iki.fi>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/archival/Kbuild.src b/archival/Kbuild.src
index a6fd2ea..b3a7d53 100644
--- a/archival/Kbuild.src
+++ b/archival/Kbuild.src
@@ -4,7 +4,7 @@
 #
 # Licensed under GPLv2, see file LICENSE in this source tree.
 
-libs-y				+= libarchive/
+libs-y += libarchive/
 
 lib-y:=
 
diff --git a/archival/cpio.c b/archival/cpio.c
index cdc16c1..82b3fe5 100644
--- a/archival/cpio.c
+++ b/archival/cpio.c
@@ -46,7 +46,7 @@
 //kbuild:lib-$(CONFIG_CPIO) += cpio.o
 
 //usage:#define cpio_trivial_usage
-//usage:       "[-dmvu] [-F FILE]" IF_FEATURE_CPIO_O(" [-H newc]")
+//usage:       "[-dmvu] [-F FILE] [-R USER[:GRP]]" IF_FEATURE_CPIO_O(" [-H newc]")
 //usage:       " [-ti"IF_FEATURE_CPIO_O("o")"]" IF_FEATURE_CPIO_P(" [-p DIR]")
 //usage:       " [EXTR_FILE]..."
 //usage:#define cpio_full_usage "\n\n"
@@ -71,6 +71,7 @@
 //usage:     "\n	-v	Verbose"
 //usage:     "\n	-u	Overwrite"
 //usage:     "\n	-F FILE	Input (-t,-i,-p) or output (-o) file"
+//usage:     "\n	-R USER[:GRP]	Set owner of created files"
 //usage:	IF_FEATURE_CPIO_O(
 //usage:     "\n	-H newc	Archive format"
 //usage:	)
@@ -130,7 +131,7 @@
   -I FILE                    File to use instead of standard input
   -L, --dereference          Dereference symbolic links (copy the files
                              that they point to instead of copying the links)
-  -R, --owner=[USER][:.][GROUP] Set owner of created files
+  -R, --owner=[USER][:.][GRP] Set owner of created files
 
  Options valid in --extract and --pass-through modes:
   -d, --make-directories     Create leading directories where needed
@@ -150,7 +151,8 @@
 	OPT_PRESERVE_MTIME     = (1 << 6),
 	OPT_DEREF              = (1 << 7),
 	OPT_FILE               = (1 << 8),
-	OPTBIT_FILE = 8,
+	OPT_OWNER              = (1 << 9),
+	OPTBIT_OWNER = 9,
 	IF_FEATURE_CPIO_O(OPTBIT_CREATE     ,)
 	IF_FEATURE_CPIO_O(OPTBIT_FORMAT     ,)
 	IF_FEATURE_CPIO_P(OPTBIT_PASSTHROUGH,)
@@ -163,7 +165,17 @@
 	OPT_2STDOUT            = IF_LONG_OPTS(     (1 << OPTBIT_2STDOUT    )) + 0,
 };
 
-#define OPTION_STR "it0uvdmLF:"
+#define OPTION_STR "it0uvdmLF:R:"
+
+struct globals {
+	struct bb_uidgid_t owner_ugid;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+void BUG_cpio_globals_too_big(void);
+#define INIT_G() do { \
+	G.owner_ugid.uid = -1L; \
+	G.owner_ugid.gid = -1L; \
+} while (0)
 
 #if ENABLE_FEATURE_CPIO_O
 static off_t cpio_pad4(off_t size)
@@ -181,7 +193,6 @@
  * It's ok to exit instead of return. */
 static NOINLINE int cpio_o(void)
 {
-	static const char trailer[] ALIGN1 = "TRAILER!!!";
 	struct name_s {
 		struct name_s *next;
 		char name[1];
@@ -223,6 +234,11 @@
 				bb_simple_perror_msg_and_die(name);
 			}
 
+			if (G.owner_ugid.uid != (uid_t)-1L)
+				st.st_uid = G.owner_ugid.uid;
+			if (G.owner_ugid.gid != (gid_t)-1L)
+				st.st_gid = G.owner_ugid.gid;
+
 			if (!(S_ISLNK(st.st_mode) || S_ISREG(st.st_mode)))
 				st.st_size = 0; /* paranoia */
 
@@ -275,7 +291,7 @@
 			} else {
 				/* If no (more) hardlinks to output,
 				 * output "trailer" entry */
-				name = trailer;
+				name = cpio_TRAILER;
 				/* st.st_size == 0 is a must, but for uniformity
 				 * in the output, we zero out everything */
 				memset(&st, 0, sizeof(st));
@@ -323,7 +339,7 @@
 		}
 
 		if (!line) {
-			if (name != trailer)
+			if (name != cpio_TRAILER)
 				goto next_link;
 			/* TODO: GNU cpio pads trailer to 512 bytes, do we want that? */
 			return EXIT_SUCCESS;
@@ -339,6 +355,7 @@
 {
 	archive_handle_t *archive_handle;
 	char *cpio_filename;
+	char *cpio_owner;
 	IF_FEATURE_CPIO_O(const char *cpio_fmt = "";)
 	unsigned opt;
 
@@ -353,12 +370,14 @@
 		"pass-through\0" No_argument       "p"
 #endif
 #endif
+		"owner\0"        Required_argument "R"
 		"verbose\0"      No_argument       "v"
 		"quiet\0"        No_argument       "\xff"
 		"to-stdout\0"    No_argument       "\xfe"
 		;
 #endif
 
+	INIT_G();
 	archive_handle = init_handle();
 	/* archive_handle->src_fd = STDIN_FILENO; - done by init_handle */
 	archive_handle->ah_flags = ARCHIVE_EXTRACT_NEWER;
@@ -369,14 +388,21 @@
 	/* -L makes sense only with -o or -p */
 
 #if !ENABLE_FEATURE_CPIO_O
-	opt = getopt32(argv, OPTION_STR, &cpio_filename);
+	opt = getopt32(argv, OPTION_STR, &cpio_filename, &cpio_owner);
+#else
+	opt = getopt32(argv, OPTION_STR "oH:" IF_FEATURE_CPIO_P("p"),
+		       &cpio_filename, &cpio_owner, &cpio_fmt);
+#endif
 	argv += optind;
+	if (opt & OPT_OWNER) { /* -R */
+		parse_chown_usergroup_or_die(&G.owner_ugid, cpio_owner);
+		archive_handle->cpio__owner = G.owner_ugid;
+	}
+#if !ENABLE_FEATURE_CPIO_O
 	if (opt & OPT_FILE) { /* -F */
 		xmove_fd(xopen(cpio_filename, O_RDONLY), STDIN_FILENO);
 	}
 #else
-	opt = getopt32(argv, OPTION_STR "oH:" IF_FEATURE_CPIO_P("p"), &cpio_filename, &cpio_fmt);
-	argv += optind;
 	if ((opt & (OPT_FILE|OPT_CREATE)) == OPT_FILE) { /* -F without -o */
 		xmove_fd(xopen(cpio_filename, O_RDONLY), STDIN_FILENO);
 	}
diff --git a/archival/libarchive/Kbuild.src b/archival/libarchive/Kbuild.src
index b7faaf7..b159a78 100644
--- a/archival/libarchive/Kbuild.src
+++ b/archival/libarchive/Kbuild.src
@@ -4,7 +4,7 @@
 #
 # Licensed under GPLv2 or later, see file LICENSE in this source tree.
 
-lib-y:=
+lib-y:= common.o
 
 COMMON_FILES:= \
 \
diff --git a/archival/libarchive/common.c b/archival/libarchive/common.c
new file mode 100644
index 0000000..dd69d22
--- /dev/null
+++ b/archival/libarchive/common.c
@@ -0,0 +1,9 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "bb_archive.h"
+
+const char cpio_TRAILER[] = "TRAILER!!!";
diff --git a/archival/libarchive/get_header_cpio.c b/archival/libarchive/get_header_cpio.c
index 7861d1f..badd4a8 100644
--- a/archival/libarchive/get_header_cpio.c
+++ b/archival/libarchive/get_header_cpio.c
@@ -52,6 +52,11 @@
 			&major, &minor, &namesize) != 10)
 		bb_error_msg_and_die("damaged cpio file");
 	file_header->mode = mode;
+	/* "cpio -R USER:GRP" support: */
+	if (archive_handle->cpio__owner.uid != (uid_t)-1L)
+		uid = archive_handle->cpio__owner.uid;
+	if (archive_handle->cpio__owner.gid != (gid_t)-1L)
+		gid = archive_handle->cpio__owner.gid;
 	file_header->uid = uid;
 	file_header->gid = gid;
 	file_header->mtime = mtime;
@@ -75,7 +80,7 @@
 	/* Update offset amount and skip padding before file contents */
 	data_align(archive_handle, 4);
 
-	if (strcmp(file_header->name, "TRAILER!!!") == 0) {
+	if (strcmp(file_header->name, cpio_TRAILER) == 0) {
 		/* Always round up. ">> 9" divides by 512 */
 		archive_handle->cpio__blocks = (uoff_t)(archive_handle->offset + 511) >> 9;
 		goto create_hardlinks;
diff --git a/coreutils/chown.c b/coreutils/chown.c
index 679c0d8..eaa1ee2 100644
--- a/coreutils/chown.c
+++ b/coreutils/chown.c
@@ -11,9 +11,9 @@
 /* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */
 
 //usage:#define chown_trivial_usage
-//usage:       "[-Rh"IF_DESKTOP("LHPcvf")"]... OWNER[<.|:>[GROUP]] FILE..."
+//usage:       "[-Rh"IF_DESKTOP("LHPcvf")"]... USER[:[GRP]] FILE..."
 //usage:#define chown_full_usage "\n\n"
-//usage:       "Change the owner and/or group of each FILE to OWNER and/or GROUP\n"
+//usage:       "Change the owner and/or group of each FILE to USER and/or GRP\n"
 //usage:     "\n	-R	Recurse"
 //usage:     "\n	-h	Affect symlinks instead of symlink targets"
 //usage:	IF_DESKTOP(
diff --git a/include/bb_archive.h b/include/bb_archive.h
index 5d9e24c..2329d02 100644
--- a/include/bb_archive.h
+++ b/include/bb_archive.h
@@ -95,6 +95,7 @@
 #endif
 #if ENABLE_CPIO || ENABLE_RPM2CPIO || ENABLE_RPM
 	uoff_t cpio__blocks;
+	struct bb_uidgid_t cpio__owner;
 	struct hardlinks_t *cpio__hardlinks_to_create;
 	struct hardlinks_t *cpio__created_hardlinks;
 #endif
@@ -159,6 +160,8 @@
 };
 
 
+extern const char cpio_TRAILER[];
+
 
 archive_handle_t *init_handle(void) FAST_FUNC;