aboutsummaryrefslogtreecommitdiff
path: root/toys/posix/cpio.c
diff options
context:
space:
mode:
Diffstat (limited to 'toys/posix/cpio.c')
-rw-r--r--toys/posix/cpio.c131
1 files changed, 71 insertions, 60 deletions
diff --git a/toys/posix/cpio.c b/toys/posix/cpio.c
index 5ecd56df..bbcde4b5 100644
--- a/toys/posix/cpio.c
+++ b/toys/posix/cpio.c
@@ -13,36 +13,39 @@
* In order: magic ino mode uid gid nlink mtime filesize devmajor devminor
* rdevmajor rdevminor namesize check
* This is the equivalent of mode -H newc in other implementations.
+ * We always do --quiet, but accept it as a compatibility NOP.
*
- * todo: export/import linux file list text format ala gen_initramfs_list.sh
+ * TODO: export/import linux file list text format ala gen_initramfs_list.sh
+ * TODO: hardlink support, -A, -0, -a, -L, --sparse
+ * TODO: --renumber-archives (probably always?) --ignore-devno --reproducible
-USE_CPIO(NEWTOY(cpio, "(ignore-devno)(renumber-inodes)(quiet)(no-preserve-owner)md(make-directories)uH:p|i|t|F:v(verbose)o|[!pio][!pot][!pF]", TOYFLAG_BIN))
+USE_CPIO(NEWTOY(cpio, "(ignore-devno)(renumber-inodes)(quiet)(no-preserve-owner)R(owner):md(make-directories)uH:p|i|t|F:v(verbose)o|[!pio][!pot][!pF]", TOYFLAG_BIN))
config CPIO
bool "cpio"
default y
help
- usage: cpio -{o|t|i|p DEST} [-v] [--verbose] [-F FILE] [--no-preserve-owner]
- [ignored: -m -H newc]
+ usage: cpio -{o|t|i|p DEST} [-v] [--verbose] [-F FILE] [-R [USER][:GROUP] [--no-preserve-owner]
Copy files into and out of a "newc" format cpio archive.
+ -d Create directories if needed
-F FILE Use archive FILE instead of stdin/stdout
- -p DEST Copy-pass mode, copy stdin file list to directory DEST
-i Extract from archive into file system (stdin=archive)
-o Create archive (stdin=list of files, stdout=archive)
+ -p DEST Copy-pass mode, copy stdin file list to directory DEST
+ -R USER Replace owner with USER[:GROUP]
-t Test files (list only, stdin=archive, stdout=list of files)
- -d Create directories if needed
- -u unlink existing files when extracting
+ -u Unlink existing files when extracting
-v Verbose
- --no-preserve-owner (don't set ownership during extract)
+ --no-preserve-owner Don't set ownership during extract
*/
#define FOR_cpio
#include "toys.h"
GLOBALS(
- char *F, *H;
+ char *F, *H, *R;
)
// Read strings, tail padded to 4 byte alignment. Argument "align" is amount
@@ -80,9 +83,21 @@ static unsigned x8u(char *hex)
void cpio_main(void)
{
- // Subtle bit: FLAG_o is 1 so we can just use it to select stdin/stdout.
- int pipe, afd = FLAG(o), empty = 1;
+ int pipe, afd = FLAG(o), reown = !geteuid() && !FLAG(no_preserve_owner),
+ empty = 1;
pid_t pid = 0;
+ long Ruid = -1, Rgid = -1;
+ char *tofree = 0;
+
+ if (TT.R) {
+ char *group = TT.R+strcspn(TT.R, ":.");
+
+ if (*group) {
+ Rgid = xgetgid(group+1);
+ *group = 0;
+ }
+ if (group != TT.R) Ruid = xgetuid(TT.R);
+ }
// In passthrough mode, parent stays in original dir and generates archive
// to pipe, child does chdir to new dir and reads archive from stdin (pipe).
@@ -90,7 +105,7 @@ void cpio_main(void)
if (FLAG(d)) {
if (!*toys.optargs) error_exit("need directory for -p");
if (mkdir(*toys.optargs, 0700) == -1 && errno != EEXIST)
- perror_exit("mkdir %s", *toys.optargs);
+ perror_msg("mkdir %s", *toys.optargs);
}
if (toys.stacktop) {
// xpopen() doesn't return from child due to vfork(), instead restarts
@@ -113,10 +128,12 @@ void cpio_main(void)
// read cpio archive
if (FLAG(i) || FLAG(t)) for (;; empty = 0) {
- char *name, *tofree, *data;
+ char *name, *data;
unsigned mode, uid, gid, timestamp;
int test = FLAG(t), err = 0, size = 0, len;
+ free(tofree);
+ tofree = 0;
// read header, skipping arbitrary leading NUL bytes (concatenated archives)
for (;;) {
if (1>(len = readall(afd, toybuf+size, 110-size))) break;
@@ -132,12 +149,10 @@ void cpio_main(void)
if (empty) error_exit("empty archive");
else break;
}
- if (size != 110 || memcmp(toybuf, "070701", 6)) error_exit("bad header");
+ if (size != 110 || smemcmp(toybuf, "070701", 6)) error_exit("bad header");
tofree = name = strpad(afd, x8u(toybuf+94), 110);
- if (!strcmp("TRAILER!!!", name)) {
- free(tofree);
- continue;
- }
+ // TODO: this flushes hardlink detection via major/minor/ino match
+ if (!strcmp("TRAILER!!!", name)) continue;
// If you want to extract absolute paths, "cd /" and run cpio.
while (*name == '/') name++;
@@ -145,8 +160,8 @@ void cpio_main(void)
size = x8u(toybuf+54);
mode = x8u(toybuf+14);
- uid = x8u(toybuf+22);
- gid = x8u(toybuf+30);
+ uid = (Ruid>=0) ? Ruid : x8u(toybuf+22);
+ gid = (Rgid>=0) ? Rgid : x8u(toybuf+30);
timestamp = x8u(toybuf+46); // unsigned 32 bit, so year 2100 problem
// (This output is unaffected by --quiet.)
@@ -163,16 +178,23 @@ void cpio_main(void)
// properly aligned with next file.
if (S_ISDIR(mode)) {
- if (!test) err = mkdir(name, mode) && !FLAG(u);
- } else if (S_ISLNK(mode)) {
- data = strpad(afd, size, 0);
- if (!test) {
- err = symlink(data, name);
- // Can't get a filehandle to a symlink, so do special chown
- if (!err && !geteuid() && !FLAG(no_preserve_owner))
- err = lchown(name, uid, gid);
+ if (test) continue;
+ err = mkdir(name, mode) && (errno != EEXIST && !FLAG(u));
+
+ // Creading dir/dev doesn't give us a filehandle, we have to refer to it
+ // by name to chown/utime, but how do we know it's the same item?
+ // Check that we at least have the right type of entity open, and do
+ // NOT restore dropped suid bit in this case.
+ if (S_ISDIR(mode) && reown) {
+ int fd = open(name, O_RDONLY|O_NOFOLLOW);
+ struct stat st;
+
+ if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT) == (mode&S_IFMT))
+ err = fchown(fd, uid, gid);
+ else err = 1;
+
+ close(fd);
}
- free(data);
} else if (S_ISREG(mode)) {
int fd = test ? 0 : open(name, O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, mode);
@@ -197,46 +219,33 @@ void cpio_main(void)
if (!test) {
// set owner, restore dropped suid bit
- if (!geteuid() && !FLAG(no_preserve_owner)) {
- err = fchown(fd, uid, gid);
- if (!err) err = fchmod(fd, mode);
- }
+ if (reown) err = fchown(fd, uid, gid) && fchmod(fd, mode);
close(fd);
}
- } else if (!test)
- err = mknod(name, mode, dev_makedev(x8u(toybuf+78), x8u(toybuf+86)));
-
- // Set ownership and timestamp.
- if (!test && !err) {
- // Creading dir/dev doesn't give us a filehandle, we have to refer to it
- // by name to chown/utime, but how do we know it's the same item?
- // Check that we at least have the right type of entity open, and do
- // NOT restore dropped suid bit in this case.
- if (!S_ISREG(mode) && !S_ISLNK(mode) && !geteuid()
- && !FLAG(no_preserve_owner))
- {
- int fd = open(name, O_RDONLY|O_NOFOLLOW);
- struct stat st;
-
- if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT) == (mode&S_IFMT))
- err = fchown(fd, uid, gid);
- else err = 1;
+ } else {
+ data = S_ISLNK(mode) ? strpad(afd, size, 0) : 0;
+ if (!test) {
+ err = data ? symlink(data, name)
+ : mknod(name, mode, dev_makedev(x8u(toybuf+78), x8u(toybuf+86)));
- close(fd);
+ // Can't get a filehandle to a symlink or a node on nodev mount,
+ // so do special chown that at least doesn't follow symlinks.
+ // We also don't chmod after, so dropped suid bit isn't restored
+ if (!err && reown) err = lchown(name, uid, gid);
}
+ free(data);
+ }
- // set timestamp
- if (!err) {
- struct timespec times[2];
+ // Set timestamp.
+ if (!test && !err) {
+ struct timespec times[2];
- memset(times, 0, sizeof(struct timespec)*2);
- times[0].tv_sec = times[1].tv_sec = timestamp;
- err = utimensat(AT_FDCWD, name, times, AT_SYMLINK_NOFOLLOW);
- }
+ memset(times, 0, sizeof(struct timespec)*2);
+ times[0].tv_sec = times[1].tv_sec = timestamp;
+ err = utimensat(AT_FDCWD, name, times, AT_SYMLINK_NOFOLLOW);
}
if (err) perror_msg_raw(name);
- free(tofree);
// Output cpio archive
@@ -266,6 +275,8 @@ void cpio_main(void)
// encrypted filesystems can stat the wrong link size
if (link) st.st_size = strlen(link);
+ if (Ruid>=0) st.st_uid = Ruid;
+ if (Rgid>=0) st.st_gid = Rgid;
if (FLAG(no_preserve_owner)) st.st_uid = st.st_gid = 0;
if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) st.st_size = 0;
if (st.st_size >> 32) perror_msg("skipping >2G file '%s'", name);