10f66f451Sopenharmony_ci/* cpio.c - a basic cpio 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2013 Isaac Dunham <ibid.ag@gmail.com> 40f66f451Sopenharmony_ci * Copyright 2015 Frontier Silicon Ltd. 50f66f451Sopenharmony_ci * 60f66f451Sopenharmony_ci * see https://www.kernel.org/doc/Documentation/early-userspace/buffer-format.txt 70f66f451Sopenharmony_ci * and http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/cpio.html 80f66f451Sopenharmony_ci * and http://pubs.opengroup.org/onlinepubs/7908799/xcu/cpio.html 90f66f451Sopenharmony_ci * 100f66f451Sopenharmony_ci * Yes, that's SUSv2, newer versions removed it, but RPM and initramfs use 110f66f451Sopenharmony_ci * this archive format. We implement (only) the modern "-H newc" variant which 120f66f451Sopenharmony_ci * expanded headers to 110 bytes (first field 6 bytes, rest are 8). 130f66f451Sopenharmony_ci * In order: magic ino mode uid gid nlink mtime filesize devmajor devminor 140f66f451Sopenharmony_ci * rdevmajor rdevminor namesize check 150f66f451Sopenharmony_ci * This is the equivalent of mode -H newc in other implementations. 160f66f451Sopenharmony_ci * 170f66f451Sopenharmony_ci * todo: export/import linux file list text format ala gen_initramfs_list.sh 180f66f451Sopenharmony_ci 190f66f451Sopenharmony_ciUSE_CPIO(NEWTOY(cpio, "(no-preserve-owner)(trailer)mduH:p:|i|t|F:v(verbose)o|[!pio][!pot][!pF]", TOYFLAG_BIN)) 200f66f451Sopenharmony_ci 210f66f451Sopenharmony_ciconfig CPIO 220f66f451Sopenharmony_ci bool "cpio" 230f66f451Sopenharmony_ci default y 240f66f451Sopenharmony_ci help 250f66f451Sopenharmony_ci usage: cpio -{o|t|i|p DEST} [-v] [--verbose] [-F FILE] [--no-preserve-owner] 260f66f451Sopenharmony_ci [ignored: -mdu -H newc] 270f66f451Sopenharmony_ci 280f66f451Sopenharmony_ci Copy files into and out of a "newc" format cpio archive. 290f66f451Sopenharmony_ci 300f66f451Sopenharmony_ci -F FILE Use archive FILE instead of stdin/stdout 310f66f451Sopenharmony_ci -p DEST Copy-pass mode, copy stdin file list to directory DEST 320f66f451Sopenharmony_ci -i Extract from archive into file system (stdin=archive) 330f66f451Sopenharmony_ci -o Create archive (stdin=list of files, stdout=archive) 340f66f451Sopenharmony_ci -t Test files (list only, stdin=archive, stdout=list of files) 350f66f451Sopenharmony_ci -v Verbose 360f66f451Sopenharmony_ci --no-preserve-owner (don't set ownership during extract) 370f66f451Sopenharmony_ci --trailer Add legacy trailer (prevents concatenation) 380f66f451Sopenharmony_ci*/ 390f66f451Sopenharmony_ci 400f66f451Sopenharmony_ci#define FOR_cpio 410f66f451Sopenharmony_ci#include "toys.h" 420f66f451Sopenharmony_ci 430f66f451Sopenharmony_ciGLOBALS( 440f66f451Sopenharmony_ci char *F, *p, *H; 450f66f451Sopenharmony_ci) 460f66f451Sopenharmony_ci 470f66f451Sopenharmony_ci// Read strings, tail padded to 4 byte alignment. Argument "align" is amount 480f66f451Sopenharmony_ci// by which start of string isn't aligned (usually 0, but header is 110 bytes 490f66f451Sopenharmony_ci// which is 2 bytes off because the first field wasn't expanded from 6 to 8). 500f66f451Sopenharmony_cistatic char *strpad(int fd, unsigned len, unsigned align) 510f66f451Sopenharmony_ci{ 520f66f451Sopenharmony_ci char *str; 530f66f451Sopenharmony_ci 540f66f451Sopenharmony_ci align = (align + len) & 3; 550f66f451Sopenharmony_ci if (align) len += (4-align); 560f66f451Sopenharmony_ci xreadall(fd, str = xmalloc(len+1), len); 570f66f451Sopenharmony_ci str[len]=0; // redundant, in case archive is bad 580f66f451Sopenharmony_ci 590f66f451Sopenharmony_ci return str; 600f66f451Sopenharmony_ci} 610f66f451Sopenharmony_ci 620f66f451Sopenharmony_ci//convert hex to uint; mostly to allow using bits of non-terminated strings 630f66f451Sopenharmony_cistatic unsigned x8u(char *hex) 640f66f451Sopenharmony_ci{ 650f66f451Sopenharmony_ci unsigned val, inpos = 8, outpos; 660f66f451Sopenharmony_ci char pattern[6]; 670f66f451Sopenharmony_ci 680f66f451Sopenharmony_ci while (*hex == '0') { 690f66f451Sopenharmony_ci hex++; 700f66f451Sopenharmony_ci if (!--inpos) return 0; 710f66f451Sopenharmony_ci } 720f66f451Sopenharmony_ci // Because scanf gratuitously treats %*X differently than printf does. 730f66f451Sopenharmony_ci sprintf(pattern, "%%%dX%%n", inpos); 740f66f451Sopenharmony_ci sscanf(hex, pattern, &val, &outpos); 750f66f451Sopenharmony_ci if (inpos != outpos) error_exit("bad header"); 760f66f451Sopenharmony_ci 770f66f451Sopenharmony_ci return val; 780f66f451Sopenharmony_ci} 790f66f451Sopenharmony_ci 800f66f451Sopenharmony_civoid cpio_main(void) 810f66f451Sopenharmony_ci{ 820f66f451Sopenharmony_ci // Subtle bit: FLAG_o is 1 so we can just use it to select stdin/stdout. 830f66f451Sopenharmony_ci int pipe, afd = toys.optflags & FLAG_o; 840f66f451Sopenharmony_ci pid_t pid = 0; 850f66f451Sopenharmony_ci 860f66f451Sopenharmony_ci // In passthrough mode, parent stays in original dir and generates archive 870f66f451Sopenharmony_ci // to pipe, child does chdir to new dir and reads archive from stdin (pipe). 880f66f451Sopenharmony_ci if (TT.p) { 890f66f451Sopenharmony_ci if (toys.stacktop) { 900f66f451Sopenharmony_ci // xpopen() doesn't return from child due to vfork(), instead restarts 910f66f451Sopenharmony_ci // with !toys.stacktop 920f66f451Sopenharmony_ci pid = xpopen(0, &pipe, 0); 930f66f451Sopenharmony_ci afd = pipe; 940f66f451Sopenharmony_ci } else { 950f66f451Sopenharmony_ci // child 960f66f451Sopenharmony_ci toys.optflags |= FLAG_i; 970f66f451Sopenharmony_ci xchdir(TT.p); 980f66f451Sopenharmony_ci } 990f66f451Sopenharmony_ci } 1000f66f451Sopenharmony_ci 1010f66f451Sopenharmony_ci if (TT.F) { 1020f66f451Sopenharmony_ci int perm = (toys.optflags & FLAG_o) ? O_CREAT|O_WRONLY|O_TRUNC : O_RDONLY; 1030f66f451Sopenharmony_ci 1040f66f451Sopenharmony_ci afd = xcreate(TT.F, perm, 0644); 1050f66f451Sopenharmony_ci } 1060f66f451Sopenharmony_ci 1070f66f451Sopenharmony_ci // read cpio archive 1080f66f451Sopenharmony_ci 1090f66f451Sopenharmony_ci if (toys.optflags & (FLAG_i|FLAG_t)) for (;;) { 1100f66f451Sopenharmony_ci char *name, *tofree, *data; 1110f66f451Sopenharmony_ci unsigned size, mode, uid, gid, timestamp; 1120f66f451Sopenharmony_ci int test = toys.optflags & FLAG_t, err = 0; 1130f66f451Sopenharmony_ci 1140f66f451Sopenharmony_ci // Read header and name. 1150f66f451Sopenharmony_ci if (!(size =readall(afd, toybuf, 110))) break; 1160f66f451Sopenharmony_ci if (size != 110 || memcmp(toybuf, "070701", 6)) error_exit("bad header"); 1170f66f451Sopenharmony_ci tofree = name = strpad(afd, x8u(toybuf+94), 110); 1180f66f451Sopenharmony_ci if (!strcmp("TRAILER!!!", name)) { 1190f66f451Sopenharmony_ci if (CFG_TOYBOX_FREE) free(tofree); 1200f66f451Sopenharmony_ci break; 1210f66f451Sopenharmony_ci } 1220f66f451Sopenharmony_ci 1230f66f451Sopenharmony_ci // If you want to extract absolute paths, "cd /" and run cpio. 1240f66f451Sopenharmony_ci while (*name == '/') name++; 1250f66f451Sopenharmony_ci // TODO: remove .. entries 1260f66f451Sopenharmony_ci 1270f66f451Sopenharmony_ci size = x8u(toybuf+54); 1280f66f451Sopenharmony_ci mode = x8u(toybuf+14); 1290f66f451Sopenharmony_ci uid = x8u(toybuf+22); 1300f66f451Sopenharmony_ci gid = x8u(toybuf+30); 1310f66f451Sopenharmony_ci timestamp = x8u(toybuf+46); // unsigned 32 bit, so year 2100 problem 1320f66f451Sopenharmony_ci 1330f66f451Sopenharmony_ci if (toys.optflags & (FLAG_t|FLAG_v)) puts(name); 1340f66f451Sopenharmony_ci 1350f66f451Sopenharmony_ci if (!test && strrchr(name, '/') && mkpath(name)) { 1360f66f451Sopenharmony_ci perror_msg("mkpath '%s'", name); 1370f66f451Sopenharmony_ci test++; 1380f66f451Sopenharmony_ci } 1390f66f451Sopenharmony_ci 1400f66f451Sopenharmony_ci // Consume entire record even if it couldn't create file, so we're 1410f66f451Sopenharmony_ci // properly aligned with next file. 1420f66f451Sopenharmony_ci 1430f66f451Sopenharmony_ci if (S_ISDIR(mode)) { 1440f66f451Sopenharmony_ci if (!test) err = mkdir(name, mode); 1450f66f451Sopenharmony_ci } else if (S_ISLNK(mode)) { 1460f66f451Sopenharmony_ci data = strpad(afd, size, 0); 1470f66f451Sopenharmony_ci if (!test) err = symlink(data, name); 1480f66f451Sopenharmony_ci free(data); 1490f66f451Sopenharmony_ci // Can't get a filehandle to a symlink, so do special chown 1500f66f451Sopenharmony_ci if (!err && !geteuid() && !(toys.optflags & FLAG_no_preserve_owner)) 1510f66f451Sopenharmony_ci err = lchown(name, uid, gid); 1520f66f451Sopenharmony_ci } else if (S_ISREG(mode)) { 1530f66f451Sopenharmony_ci int fd = test ? 0 : open(name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, mode); 1540f66f451Sopenharmony_ci 1550f66f451Sopenharmony_ci // If write fails, we still need to read/discard data to continue with 1560f66f451Sopenharmony_ci // archive. Since doing so overwrites errno, report error now 1570f66f451Sopenharmony_ci if (fd < 0) { 1580f66f451Sopenharmony_ci perror_msg("create %s", name); 1590f66f451Sopenharmony_ci test++; 1600f66f451Sopenharmony_ci } 1610f66f451Sopenharmony_ci 1620f66f451Sopenharmony_ci data = toybuf; 1630f66f451Sopenharmony_ci while (size) { 1640f66f451Sopenharmony_ci if (size < sizeof(toybuf)) data = strpad(afd, size, 0); 1650f66f451Sopenharmony_ci else xreadall(afd, toybuf, sizeof(toybuf)); 1660f66f451Sopenharmony_ci if (!test) xwrite(fd, data, data == toybuf ? sizeof(toybuf) : size); 1670f66f451Sopenharmony_ci if (data != toybuf) { 1680f66f451Sopenharmony_ci free(data); 1690f66f451Sopenharmony_ci break; 1700f66f451Sopenharmony_ci } 1710f66f451Sopenharmony_ci size -= sizeof(toybuf); 1720f66f451Sopenharmony_ci } 1730f66f451Sopenharmony_ci 1740f66f451Sopenharmony_ci if (!test) { 1750f66f451Sopenharmony_ci // set owner, restore dropped suid bit 1760f66f451Sopenharmony_ci if (!geteuid() && !(toys.optflags & FLAG_no_preserve_owner)) { 1770f66f451Sopenharmony_ci err = fchown(fd, uid, gid); 1780f66f451Sopenharmony_ci if (!err) err = fchmod(fd, mode); 1790f66f451Sopenharmony_ci } 1800f66f451Sopenharmony_ci close(fd); 1810f66f451Sopenharmony_ci } 1820f66f451Sopenharmony_ci } else if (!test) 1830f66f451Sopenharmony_ci err = mknod(name, mode, dev_makedev(x8u(toybuf+78), x8u(toybuf+86))); 1840f66f451Sopenharmony_ci 1850f66f451Sopenharmony_ci // Set ownership and timestamp. 1860f66f451Sopenharmony_ci if (!test && !err) { 1870f66f451Sopenharmony_ci // Creading dir/dev doesn't give us a filehandle, we have to refer to it 1880f66f451Sopenharmony_ci // by name to chown/utime, but how do we know it's the same item? 1890f66f451Sopenharmony_ci // Check that we at least have the right type of entity open, and do 1900f66f451Sopenharmony_ci // NOT restore dropped suid bit in this case. 1910f66f451Sopenharmony_ci if (!S_ISREG(mode) && !S_ISLNK(mode) && !geteuid() 1920f66f451Sopenharmony_ci && !(toys.optflags & FLAG_no_preserve_owner)) 1930f66f451Sopenharmony_ci { 1940f66f451Sopenharmony_ci int fd = open(name, O_RDONLY|O_NOFOLLOW); 1950f66f451Sopenharmony_ci struct stat st; 1960f66f451Sopenharmony_ci 1970f66f451Sopenharmony_ci if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT) == (mode&S_IFMT)) 1980f66f451Sopenharmony_ci err = fchown(fd, uid, gid); 1990f66f451Sopenharmony_ci else err = 1; 2000f66f451Sopenharmony_ci 2010f66f451Sopenharmony_ci close(fd); 2020f66f451Sopenharmony_ci } 2030f66f451Sopenharmony_ci 2040f66f451Sopenharmony_ci // set timestamp 2050f66f451Sopenharmony_ci if (!err) { 2060f66f451Sopenharmony_ci struct timespec times[2]; 2070f66f451Sopenharmony_ci 2080f66f451Sopenharmony_ci memset(times, 0, sizeof(struct timespec)*2); 2090f66f451Sopenharmony_ci times[0].tv_sec = times[1].tv_sec = timestamp; 2100f66f451Sopenharmony_ci err = utimensat(AT_FDCWD, name, times, AT_SYMLINK_NOFOLLOW); 2110f66f451Sopenharmony_ci } 2120f66f451Sopenharmony_ci } 2130f66f451Sopenharmony_ci 2140f66f451Sopenharmony_ci if (err) perror_msg_raw(name); 2150f66f451Sopenharmony_ci free(tofree); 2160f66f451Sopenharmony_ci 2170f66f451Sopenharmony_ci // Output cpio archive 2180f66f451Sopenharmony_ci 2190f66f451Sopenharmony_ci } else { 2200f66f451Sopenharmony_ci char *name = 0; 2210f66f451Sopenharmony_ci size_t size = 0; 2220f66f451Sopenharmony_ci 2230f66f451Sopenharmony_ci for (;;) { 2240f66f451Sopenharmony_ci struct stat st; 2250f66f451Sopenharmony_ci unsigned nlen, error = 0, zero = 0; 2260f66f451Sopenharmony_ci int len, fd = -1; 2270f66f451Sopenharmony_ci ssize_t llen; 2280f66f451Sopenharmony_ci 2290f66f451Sopenharmony_ci len = getline(&name, &size, stdin); 2300f66f451Sopenharmony_ci if (len<1) break; 2310f66f451Sopenharmony_ci if (name[len-1] == '\n') name[--len] = 0; 2320f66f451Sopenharmony_ci nlen = len+1; 2330f66f451Sopenharmony_ci if (lstat(name, &st) || (S_ISREG(st.st_mode) 2340f66f451Sopenharmony_ci && st.st_size && (fd = open(name, O_RDONLY))<0)) 2350f66f451Sopenharmony_ci { 2360f66f451Sopenharmony_ci perror_msg_raw(name); 2370f66f451Sopenharmony_ci continue; 2380f66f451Sopenharmony_ci } 2390f66f451Sopenharmony_ci 2400f66f451Sopenharmony_ci if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) st.st_size = 0; 2410f66f451Sopenharmony_ci if (st.st_size >> 32) perror_msg("skipping >2G file '%s'", name); 2420f66f451Sopenharmony_ci else { 2430f66f451Sopenharmony_ci llen = sprintf(toybuf, 2440f66f451Sopenharmony_ci "070701%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X", 2450f66f451Sopenharmony_ci (int)st.st_ino, st.st_mode, st.st_uid, st.st_gid, (int)st.st_nlink, 2460f66f451Sopenharmony_ci (int)st.st_mtime, (int)st.st_size, dev_major(st.st_dev), 2470f66f451Sopenharmony_ci dev_minor(st.st_dev), dev_major(st.st_rdev), dev_minor(st.st_rdev), 2480f66f451Sopenharmony_ci nlen, 0); 2490f66f451Sopenharmony_ci xwrite(afd, toybuf, llen); 2500f66f451Sopenharmony_ci xwrite(afd, name, nlen); 2510f66f451Sopenharmony_ci 2520f66f451Sopenharmony_ci // NUL Pad header up to 4 multiple bytes. 2530f66f451Sopenharmony_ci llen = (llen + nlen) & 3; 2540f66f451Sopenharmony_ci if (llen) xwrite(afd, &zero, 4-llen); 2550f66f451Sopenharmony_ci 2560f66f451Sopenharmony_ci // Write out body for symlink or regular file 2570f66f451Sopenharmony_ci llen = st.st_size; 2580f66f451Sopenharmony_ci if (S_ISLNK(st.st_mode)) { 2590f66f451Sopenharmony_ci if (readlink(name, toybuf, sizeof(toybuf)-1) == llen) 2600f66f451Sopenharmony_ci xwrite(afd, toybuf, llen); 2610f66f451Sopenharmony_ci else perror_msg("readlink '%s'", name); 2620f66f451Sopenharmony_ci } else while (llen) { 2630f66f451Sopenharmony_ci nlen = llen > sizeof(toybuf) ? sizeof(toybuf) : llen; 2640f66f451Sopenharmony_ci llen -= nlen; 2650f66f451Sopenharmony_ci // If read fails, write anyway (already wrote size in header) 2660f66f451Sopenharmony_ci if (nlen != readall(fd, toybuf, nlen)) 2670f66f451Sopenharmony_ci if (!error++) perror_msg("bad read from file '%s'", name); 2680f66f451Sopenharmony_ci xwrite(afd, toybuf, nlen); 2690f66f451Sopenharmony_ci } 2700f66f451Sopenharmony_ci llen = st.st_size & 3; 2710f66f451Sopenharmony_ci if (llen) xwrite(afd, &zero, 4-llen); 2720f66f451Sopenharmony_ci } 2730f66f451Sopenharmony_ci close(fd); 2740f66f451Sopenharmony_ci } 2750f66f451Sopenharmony_ci free(name); 2760f66f451Sopenharmony_ci 2770f66f451Sopenharmony_ci if (FLAG_trailer) { 2780f66f451Sopenharmony_ci memset(toybuf, 0, sizeof(toybuf)); 2790f66f451Sopenharmony_ci xwrite(afd, toybuf, 2800f66f451Sopenharmony_ci sprintf(toybuf, "070701%040X%056X%08XTRAILER!!!", 1, 0x0b, 0)+4); 2810f66f451Sopenharmony_ci } 2820f66f451Sopenharmony_ci } 2830f66f451Sopenharmony_ci if (TT.F) xclose(afd); 2840f66f451Sopenharmony_ci 2850f66f451Sopenharmony_ci if (TT.p) toys.exitval |= xpclose(pid, pipe); 2860f66f451Sopenharmony_ci} 287