kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

cpio.c (39265B)


      1 #include <kit/compress.h>
      2 #include <kit/core.h>
      3 #include <stddef.h>
      4 #include <stdint.h>
      5 #include <string.h>
      6 
      7 #include "driver.h"
      8 #include "env.h"
      9 
     10 /* `kit cpio` — create / list / extract SVR4 "newc" cpio archives, the format
     11  * the Linux kernel unpacks as its initramfs (magic 070701, or 070702 for the
     12  * CRC variant). This is an archive packager in the byte-utility family, a
     13  * sibling of `ar`; it does not boot, mount, or interpret the archive.
     14  *
     15  * The newc framing is plain container bookkeeping (110-byte ASCII-hex headers,
     16  * 4-byte-aligned name and data, a closing TRAILER!!! record, a 512-byte tail
     17  * pad), so it lives here driver-local rather than in libkit — only this tool
     18  * consumes it. Compression rides on the public kit/compress.h codecs.
     19  *
     20  * Determinism: members are sorted by archived path; metadata is normalized
     21  * (mode = type | perms with perms keyed on the source executable bit, uid/gid
     22  * 0, mtime 0, sequential inode). Identical inputs yield byte-identical output.
     23  */
     24 
     25 #define CPIO_TOOL "cpio"
     26 
     27 #define CPIO_HDR_LEN 110u
     28 #define CPIO_MAGIC_NEWC "070701"
     29 #define CPIO_MAGIC_CRC "070702"
     30 
     31 /* mode type bits (the octal S_IF* values, target-independent). */
     32 #define CPIO_S_IFMT 0170000u
     33 #define CPIO_S_IFREG 0100000u
     34 #define CPIO_S_IFDIR 0040000u
     35 #define CPIO_S_IFLNK 0120000u
     36 
     37 /* Largest symlink target / read buffer for an extracted link. */
     38 #define CPIO_LINK_MAX 4096u
     39 
     40 /* ---------------------------------------------------------------------------
     41  * Low-level field codec
     42  * ------------------------------------------------------------------------- */
     43 
     44 static void cpio_put_hex8(uint8_t* p, uint32_t v) {
     45   static const char hx[] = "0123456789ABCDEF";
     46   int i;
     47   for (i = 7; i >= 0; --i) {
     48     p[i] = (uint8_t)hx[v & 0xFu];
     49     v >>= 4;
     50   }
     51 }
     52 
     53 static int cpio_get_hex8(const uint8_t* p, uint32_t* out) {
     54   uint32_t v = 0;
     55   int i;
     56   for (i = 0; i < 8; ++i) {
     57     int d = driver_hex_nibble((char)p[i]);
     58     if (d < 0) return 1;
     59     v = (v << 4) | (uint32_t)d;
     60   }
     61   *out = v;
     62   return 0;
     63 }
     64 
     65 static size_t cpio_round4(size_t n) { return (n + 3u) & ~(size_t)3u; }
     66 
     67 static uint8_t cpio_ft_from_mode(uint32_t mode) {
     68   switch (mode & CPIO_S_IFMT) {
     69     case CPIO_S_IFDIR:
     70       return 3;
     71     case CPIO_S_IFLNK:
     72       return 7;
     73     case CPIO_S_IFREG:
     74       return 4;
     75     default:
     76       return 0;
     77   }
     78 }
     79 
     80 /* ---------------------------------------------------------------------------
     81  * Writer side: stream newc records to a KitWriter, tracking the running
     82  * archive offset so both 4-byte record alignment and the final 512-byte pad
     83  * are measured from the start of the archive.
     84  * ------------------------------------------------------------------------- */
     85 
     86 typedef struct CpioOut {
     87   KitWriter* w;
     88   uint64_t total;   /* bytes written so far */
     89   uint32_t next_ino;
     90   int crc;          /* emit 070702 with a data-byte-sum checksum */
     91   int err;
     92 } CpioOut;
     93 
     94 static int cpio_emit(CpioOut* o, const void* p, size_t n) {
     95   if (o->err) return 1;
     96   if (n && kit_writer_write(o->w, p, n) != KIT_OK) {
     97     o->err = 1;
     98     return 1;
     99   }
    100   o->total += n;
    101   return 0;
    102 }
    103 
    104 /* Pad the archive up to the next 4-byte boundary. Record starts are always
    105  * 4-aligned, so this lands data (after the name) and the next header (after
    106  * data) on 4-byte boundaries as newc requires. */
    107 static int cpio_pad4(CpioOut* o) {
    108   static const uint8_t z[4] = {0, 0, 0, 0};
    109   size_t pad = (size_t)((4u - (o->total & 3u)) & 3u);
    110   return pad ? cpio_emit(o, z, pad) : 0;
    111 }
    112 
    113 static int cpio_emit_record(CpioOut* o, const char* name, size_t namelen,
    114                             uint32_t mode, uint32_t nlink, uint32_t ino,
    115                             const uint8_t* data, uint32_t size) {
    116   uint8_t hdr[CPIO_HDR_LEN];
    117   uint32_t namesize = (uint32_t)namelen + 1u; /* includes the trailing NUL */
    118   uint32_t check = 0;
    119   static const uint8_t nul = 0;
    120 
    121   if (o->crc && data) {
    122     uint32_t i;
    123     for (i = 0; i < size; ++i) check += data[i];
    124   }
    125 
    126   memcpy(hdr, o->crc ? CPIO_MAGIC_CRC : CPIO_MAGIC_NEWC, 6);
    127   cpio_put_hex8(hdr + 6, ino);
    128   cpio_put_hex8(hdr + 14, mode);
    129   cpio_put_hex8(hdr + 22, 0u); /* uid */
    130   cpio_put_hex8(hdr + 30, 0u); /* gid */
    131   cpio_put_hex8(hdr + 38, nlink);
    132   cpio_put_hex8(hdr + 46, 0u); /* mtime */
    133   cpio_put_hex8(hdr + 54, size);
    134   cpio_put_hex8(hdr + 62, 0u);  /* devmajor */
    135   cpio_put_hex8(hdr + 70, 0u);  /* devminor */
    136   cpio_put_hex8(hdr + 78, 0u);  /* rdevmajor */
    137   cpio_put_hex8(hdr + 86, 0u);  /* rdevminor */
    138   cpio_put_hex8(hdr + 94, namesize);
    139   cpio_put_hex8(hdr + 102, check);
    140 
    141   if (cpio_emit(o, hdr, CPIO_HDR_LEN)) return 1;
    142   if (cpio_emit(o, name, namelen)) return 1;
    143   if (cpio_emit(o, &nul, 1)) return 1;
    144   if (cpio_pad4(o)) return 1;
    145   if (size && cpio_emit(o, data, size)) return 1;
    146   if (cpio_pad4(o)) return 1;
    147   return 0;
    148 }
    149 
    150 /* Append one member, assigning the next sequential inode. */
    151 static int cpio_append(CpioOut* o, const char* name, size_t namelen,
    152                        uint32_t mode, uint32_t nlink, const uint8_t* data,
    153                        uint32_t size) {
    154   if (cpio_emit_record(o, name, namelen, mode, nlink, o->next_ino, data, size))
    155     return 1;
    156   o->next_ino++;
    157   return 0;
    158 }
    159 
    160 /* Write the TRAILER!!! record (inode 0, nlink 1, no data) then pad the whole
    161  * archive to a 512-byte boundary so concatenated segments stay aligned. */
    162 static int cpio_finish(CpioOut* o) {
    163   if (cpio_emit_record(o, "TRAILER!!!", 10u, 0u, 1u, 0u, NULL, 0u)) return 1;
    164   {
    165     static const uint8_t z[64] = {0};
    166     size_t pad = (size_t)((512u - (o->total & 511u)) & 511u);
    167     while (pad) {
    168       size_t n = pad > sizeof z ? sizeof z : pad;
    169       if (cpio_emit(o, z, n)) return 1;
    170       pad -= n;
    171     }
    172   }
    173   return 0;
    174 }
    175 
    176 /* ---------------------------------------------------------------------------
    177  * Reader side: parse newc records (handling concatenated segments) and hand
    178  * each non-trailer entry to a visitor.
    179  * ------------------------------------------------------------------------- */
    180 
    181 typedef struct CpioEntry {
    182   const char* name; /* NUL-terminated, aliases the input buffer */
    183   uint32_t name_len;
    184   uint32_t mode;
    185   uint8_t filetype; /* derived from mode */
    186   const uint8_t* data;
    187   uint32_t size;
    188 } CpioEntry;
    189 
    190 /* Returns nonzero to stop the walk with an error. */
    191 typedef int (*CpioVisit)(void* user, const CpioEntry* e);
    192 
    193 static int cpio_is_magic(const uint8_t* p) {
    194   return memcmp(p, CPIO_MAGIC_NEWC, 6) == 0 || memcmp(p, CPIO_MAGIC_CRC, 6) == 0;
    195 }
    196 
    197 static int cpio_parse(const uint8_t* data, size_t len, CpioVisit fn,
    198                       void* user) {
    199   size_t off = 0;
    200   int saw_trailer = 0;
    201 
    202   while (off + CPIO_HDR_LEN <= len) {
    203     const uint8_t* h = data + off;
    204     uint32_t fields[13];
    205     uint32_t mode, filesize, namesize, check;
    206     size_t name_off, data_off;
    207     const char* name;
    208     size_t i;
    209 
    210     if (!cpio_is_magic(h)) {
    211       driver_errf(CPIO_TOOL,
    212                   off == 0 ? "not a cpio newc archive (bad magic)"
    213                            : "corrupt cpio header (bad magic mid-stream)");
    214       return 1;
    215     }
    216     for (i = 0; i < 13; ++i)
    217       if (cpio_get_hex8(h + 6u + i * 8u, &fields[i])) {
    218         driver_errf(CPIO_TOOL, "corrupt cpio header (non-hex field)");
    219         return 1;
    220       }
    221     mode = fields[1];
    222     filesize = fields[6];
    223     namesize = fields[11];
    224     check = fields[12];
    225     if (namesize == 0) {
    226       driver_errf(CPIO_TOOL, "corrupt cpio header (zero name size)");
    227       return 1;
    228     }
    229     name_off = off + CPIO_HDR_LEN;
    230     if ((size_t)namesize > len - name_off) {
    231       driver_errf(CPIO_TOOL, "truncated cpio archive (name runs past end)");
    232       return 1;
    233     }
    234     name = (const char*)(data + name_off);
    235     if (name[namesize - 1u] != '\0') {
    236       driver_errf(CPIO_TOOL, "corrupt cpio header (name not NUL-terminated)");
    237       return 1;
    238     }
    239     if (name_off + (size_t)namesize > SIZE_MAX - 3u) {
    240       driver_errf(CPIO_TOOL, "corrupt cpio header (name size overflow)");
    241       return 1;
    242     }
    243     data_off = cpio_round4(name_off + namesize);
    244     if (data_off > len) {
    245       driver_errf(CPIO_TOOL, "truncated cpio archive (name padding missing)");
    246       return 1;
    247     }
    248 
    249     if (namesize == 11u && memcmp(name, "TRAILER!!!", 11) == 0) {
    250       if (filesize != 0) {
    251         driver_errf(CPIO_TOOL, "corrupt cpio TRAILER!!! record");
    252         return 1;
    253       }
    254       saw_trailer = 1;
    255       off = data_off; /* filesize is 0 for the trailer */
    256       /* Skip inter-segment zero padding; a following non-zero run that is a
    257        * cpio magic starts a concatenated archive, otherwise it is trailing
    258        * data we do not parse (e.g. an appended compressed image). */
    259       while (off < len && data[off] == 0) ++off;
    260       if (off >= len) break;
    261       if (off + 6u > len || !cpio_is_magic(data + off)) {
    262         driver_errf(CPIO_TOOL,
    263                     "corrupt cpio archive: %lu trailing byte(s) after TRAILER",
    264                     (unsigned long)(len - off));
    265         return 1;
    266       }
    267       saw_trailer = 0; /* the concatenated segment needs its own trailer */
    268       continue;
    269     }
    270 
    271     if ((size_t)filesize > len - data_off) {
    272       driver_errf(CPIO_TOOL, "truncated cpio archive (data runs past end)");
    273       return 1;
    274     }
    275     if (memcmp(h, CPIO_MAGIC_CRC, 6) == 0) {
    276       uint32_t actual = 0;
    277       for (i = 0; i < filesize; ++i) actual += data[data_off + i];
    278       if (actual != check) {
    279         driver_errf(CPIO_TOOL, "corrupt cpio checksum for: %s", name);
    280         return 1;
    281       }
    282     }
    283     {
    284       CpioEntry e;
    285       e.name = name;
    286       e.name_len = namesize - 1u;
    287       e.mode = mode;
    288       e.filetype = cpio_ft_from_mode(mode);
    289       e.data = data + data_off;
    290       e.size = filesize;
    291       if (fn && fn(user, &e)) return 1;
    292     }
    293     if (data_off + (size_t)filesize > SIZE_MAX - 3u) {
    294       driver_errf(CPIO_TOOL, "corrupt cpio header (data size overflow)");
    295       return 1;
    296     }
    297     off = cpio_round4(data_off + filesize);
    298   }
    299 
    300   if (!saw_trailer) {
    301     if (off < len)
    302       driver_errf(CPIO_TOOL, "truncated cpio archive (incomplete header)");
    303     else
    304       driver_errf(CPIO_TOOL, "corrupt cpio archive: missing TRAILER!!! record");
    305     return 1;
    306   }
    307   return 0;
    308 }
    309 
    310 /* ---------------------------------------------------------------------------
    311  * Member collection for `-o` create.
    312  * ------------------------------------------------------------------------- */
    313 
    314 typedef struct CpioMember {
    315   char* name; /* archived path (cleaned), heap-owned */
    316   size_t name_alloc;
    317   uint32_t name_len;
    318   char* src; /* host path to read, heap-owned, or NULL */
    319   size_t src_alloc;
    320   uint8_t filetype; /* 3 dir, 4 regular, 7 symlink */
    321   int executable;
    322   uint64_t size;
    323 } CpioMember;
    324 
    325 typedef struct CpioBuild {
    326   DriverEnv* env;
    327   CpioMember* items;
    328   size_t count;
    329   size_t cap;
    330 } CpioBuild;
    331 
    332 static char* cpio_strdup(DriverEnv* env, const char* s, size_t* out_alloc) {
    333   size_t n = driver_strlen(s) + 1u;
    334   char* p = (char*)driver_alloc(env, n);
    335   if (p) memcpy(p, s, n);
    336   if (out_alloc) *out_alloc = n;
    337   return p;
    338 }
    339 
    340 static int cpio_build_grow(CpioBuild* b) {
    341   size_t nc = b->cap ? b->cap * 2u : 16u;
    342   CpioMember* nv =
    343       (CpioMember*)driver_alloc(b->env, nc * sizeof(CpioMember));
    344   if (!nv) return 1;
    345   if (b->items) {
    346     memcpy(nv, b->items, b->count * sizeof(CpioMember));
    347     driver_free(b->env, b->items, b->cap * sizeof(CpioMember));
    348   }
    349   b->items = nv;
    350   b->cap = nc;
    351   return 0;
    352 }
    353 
    354 static int cpio_build_add(CpioBuild* b, const char* arch, const char* src,
    355                           uint8_t ft, int exe, uint64_t size) {
    356   CpioMember* m;
    357   if (b->count >= b->cap && cpio_build_grow(b)) return 1;
    358   m = &b->items[b->count];
    359   memset(m, 0, sizeof *m);
    360   m->name = cpio_strdup(b->env, arch, &m->name_alloc);
    361   if (!m->name) return 1;
    362   m->name_len = (uint32_t)(m->name_alloc - 1u);
    363   if (src) {
    364     m->src = cpio_strdup(b->env, src, &m->src_alloc);
    365     if (!m->src) return 1;
    366   }
    367   m->filetype = ft;
    368   m->executable = exe;
    369   m->size = size;
    370   b->count++;
    371   return 0;
    372 }
    373 
    374 static void cpio_build_free(CpioBuild* b) {
    375   size_t i;
    376   for (i = 0; i < b->count; ++i) {
    377     if (b->items[i].name) driver_free(b->env, b->items[i].name,
    378                                       b->items[i].name_alloc);
    379     if (b->items[i].src) driver_free(b->env, b->items[i].src,
    380                                      b->items[i].src_alloc);
    381   }
    382   if (b->items) driver_free(b->env, b->items, b->cap * sizeof(CpioMember));
    383   b->items = NULL;
    384   b->count = b->cap = 0;
    385 }
    386 
    387 /* Walk one source path (`src`) recording it (and, for directories, its
    388  * contents) under the archived name `arch`. An empty `arch` means "emit the
    389  * contents only" (used for a "." operand), so no entry is written for the
    390  * root itself. Symlinks are recorded, never followed. */
    391 static int cpio_collect(CpioBuild* b, const char* src, const char* arch) {
    392   DriverEnv* env = b->env;
    393   uint64_t size = 0;
    394   uint8_t ft = 0;
    395   int exe = 0;
    396   int rc = driver_path_lstat(src, &size, &ft, &exe);
    397   if (rc != 0) {
    398     driver_errf(CPIO_TOOL, "cannot stat: %s", src);
    399     return 1;
    400   }
    401 
    402   if (ft == 4) return cpio_build_add(b, arch, src, 4, exe, size);
    403   if (ft == 7) return cpio_build_add(b, arch, src, 7, 0, 0);
    404   if (ft != 3) {
    405     driver_errf(CPIO_TOOL, "skipping unsupported file type: %s", src);
    406     return 0; /* not fatal — special/device nodes are out of scope */
    407   }
    408 
    409   /* Directory: emit its own entry (unless this is the contents-only root),
    410    * then recurse over a snapshot of its children. */
    411   if (arch[0] != '\0' && cpio_build_add(b, arch, NULL, 3, 0, 0)) return 1;
    412   {
    413     DriverDirHandle* h = driver_open_dir(env, src);
    414     uint64_t i;
    415     if (!h) {
    416       driver_errf(CPIO_TOOL, "cannot read directory: %s", src);
    417       return 1;
    418     }
    419     for (i = 0;; ++i) {
    420       const char* nm;
    421       uint32_t nl;
    422       uint64_t ino, sz, mt;
    423       uint8_t cft;
    424       char* csrc;
    425       char* carch;
    426       size_t csrc_sz = 0, carch_sz = 0;
    427       int crc;
    428       if (driver_read_dir_entry(h, i, &nm, &nl, &ino, &sz, &mt, &cft) != 0)
    429         break;
    430       csrc = driver_path_join(env, src, nm, &csrc_sz);
    431       carch = driver_path_join(env, arch, nm, &carch_sz);
    432       if (!csrc || !carch) {
    433         if (csrc) driver_free(env, csrc, csrc_sz);
    434         if (carch) driver_free(env, carch, carch_sz);
    435         driver_close_dir(env, h);
    436         return 1;
    437       }
    438       crc = cpio_collect(b, csrc, carch);
    439       driver_free(env, csrc, csrc_sz);
    440       driver_free(env, carch, carch_sz);
    441       if (crc) {
    442         driver_close_dir(env, h);
    443         return 1;
    444       }
    445     }
    446     driver_close_dir(env, h);
    447   }
    448   return 0;
    449 }
    450 
    451 static int cpio_name_cmp(const CpioMember* a, const CpioMember* b) {
    452   const unsigned char* x = (const unsigned char*)a->name;
    453   const unsigned char* y = (const unsigned char*)b->name;
    454   size_t i = 0;
    455   while (x[i] && y[i]) {
    456     if (x[i] != y[i]) return (int)x[i] - (int)y[i];
    457     ++i;
    458   }
    459   return (int)x[i] - (int)y[i];
    460 }
    461 
    462 /* Bottom-up merge sort by archived name. Stable and O(n log n); a plain
    463  * lexicographic order places every directory before its descendants (a
    464  * parent name is a strict prefix of "parent/child"), so the emitted stream
    465  * is a valid sorted DFS. */
    466 static int cpio_sort(CpioBuild* b) {
    467   size_t n = b->count, width;
    468   CpioMember* src = b->items;
    469   CpioMember* tmp;
    470   if (n < 2) return 0;
    471   tmp = (CpioMember*)driver_alloc(b->env, n * sizeof(CpioMember));
    472   if (!tmp) return 1;
    473   for (width = 1; width < n; width *= 2) {
    474     size_t i;
    475     for (i = 0; i < n; i += 2 * width) {
    476       size_t l = i;
    477       size_t mid = i + width < n ? i + width : n;
    478       size_t r = i + 2 * width < n ? i + 2 * width : n;
    479       size_t a = l, c = mid, k = l;
    480       while (a < mid && c < r)
    481         tmp[k++] = cpio_name_cmp(&src[a], &src[c]) <= 0 ? src[a++] : src[c++];
    482       while (a < mid) tmp[k++] = src[a++];
    483       while (c < r) tmp[k++] = src[c++];
    484     }
    485     {
    486       CpioMember* t = src;
    487       src = tmp;
    488       tmp = t;
    489     }
    490   }
    491   if (src != b->items) {
    492     memcpy(b->items, src, n * sizeof(CpioMember));
    493     tmp = src;
    494   }
    495   driver_free(b->env, tmp, n * sizeof(CpioMember));
    496   return 0;
    497 }
    498 
    499 /* Strip a leading "./" run and any leading '/'. Returns the cleaned pointer
    500  * into the original string and reports whether an absolute prefix was
    501  * dropped. A trailing '/' is handled separately by the caller. */
    502 static const char* cpio_clean_name(const char* raw, int* stripped_abs) {
    503   *stripped_abs = 0;
    504   while (raw[0] == '.' && raw[1] == '/') raw += 2;
    505   while (raw[0] == '/') {
    506     raw += 1;
    507     *stripped_abs = 1;
    508   }
    509   return raw;
    510 }
    511 
    512 /* Whether `n` contains a ".." path component. Used to refuse archiving an
    513  * operand whose name would escape the archive root (and which our own extract
    514  * would then reject). */
    515 static int cpio_name_has_dotdot(const char* n) {
    516   size_t i = 0, comp = 0;
    517   for (;; ++i) {
    518     char c = n[i];
    519     if (c == '/' || c == '\0') {
    520       if (i - comp == 2u && n[comp] == '.' && n[comp + 1u] == '.') return 1;
    521       comp = i + 1u;
    522       if (c == '\0') return 0;
    523     }
    524   }
    525 }
    526 
    527 /* ---------------------------------------------------------------------------
    528  * Options + dispatch
    529  * ------------------------------------------------------------------------- */
    530 
    531 typedef struct CpioOpts {
    532   int mode;          /* 'o' create, 't' list, 'i' extract */
    533   const char* file;  /* -F archive path, or NULL for stdin/stdout */
    534   int crc;           /* emit/accept 070702 */
    535   int verbose;       /* -v */
    536   int compress;      /* create: compress the output */
    537   KitCompressFormat cfmt;
    538   int decompress;    /* -d (read); auto-detect is always on regardless */
    539 } CpioOpts;
    540 
    541 static int cpio_parse_format(const char* s, int* crc) {
    542   if (driver_streq(s, "newc")) {
    543     *crc = 0;
    544     return 0;
    545   }
    546   if (driver_streq(s, "crc") || driver_streq(s, "newcrc") ||
    547       driver_streq(s, "sv4crc")) {
    548     *crc = 1;
    549     return 0;
    550   }
    551   return 1;
    552 }
    553 
    554 static int cpio_parse_compress(const char* s, KitCompressFormat* fmt) {
    555   if (driver_streq(s, "gzip") || driver_streq(s, "gz")) {
    556     *fmt = KIT_COMPRESS_GZIP;
    557     return 0;
    558   }
    559   if (driver_streq(s, "lz4")) {
    560     *fmt = KIT_COMPRESS_LZ4_FRAME;
    561     return 0;
    562   }
    563   return 1; /* zstd/xz/unknown */
    564 }
    565 
    566 void driver_help_cpio(void) {
    567   driver_printf(
    568       "%.*s",
    569       KIT_SLICE_ARG(KIT_SLICE_LIT(
    570           "kit cpio — create / list / extract SVR4 newc cpio archives\n"
    571           "\n"
    572           "USAGE\n"
    573           "  kit cpio -o [-F FILE] [-H newc|crc] [-z|--lz4] [-v] PATH...\n"
    574           "  kit cpio -t [-F FILE] [-v]\n"
    575           "  kit cpio -i [-F FILE] [-v]\n"
    576           "\n"
    577           "DESCRIPTION\n"
    578           "  Packages the SVR4 \"newc\" cpio format (magic 070701, or 070702\n"
    579           "  with -H crc) the Linux kernel unpacks as initramfs. Create reads\n"
    580           "  the given files and directories (recursed); list and extract read\n"
    581           "  the archive from -F FILE or stdin. Regular files, directories,\n"
    582           "  and symlinks are supported; special/device nodes are not.\n"
    583           "\n"
    584           "  Output is deterministic: members are sorted by path and metadata\n"
    585           "  is normalized (uid/gid 0, mtime 0, mode by type and exec bit).\n"
    586           "\n"
    587           "MODES\n"
    588           "  -o, --create     create an archive from PATH operands\n"
    589           "  -t, --list       list the members of an archive\n"
    590           "  -i, --extract    extract the members of an archive\n"
    591           "\n"
    592           "OPTIONS\n"
    593           "  -F, --file FILE  archive file (default: stdout on -o, stdin "
    594           "else)\n"
    595           "  -H, --format FMT newc (default) | crc (070702 checksum variant)\n"
    596           "  -z               gzip-compress the created archive\n"
    597           "      --lz4        LZ4-frame-compress the created archive\n"
    598           "      --compress=C  compress with C = gzip | lz4\n"
    599           "  -d               decompress on read (auto-detected regardless)\n"
    600           "  -v, --verbose    list/announce each member\n"
    601           "  -h, --help       show this help\n"
    602           "\n"
    603           "  Compression is gzip or lz4 only; zstd/xz are rejected. On read a\n"
    604           "  gzip/lz4 archive is decompressed automatically. Concatenated\n"
    605           "  archives (early-init segments) are accepted on list/extract.\n"
    606           "\n"
    607           "OPERANDS AND EXTRACTION\n"
    608           "  Create mode takes PATH operands; list/extract take no member\n"
    609           "  operands. Extraction writes below the current working directory,\n"
    610           "  so change to the intended destination first. Use -- before a\n"
    611           "  create operand beginning with `-`.\n"
    612           "\n"
    613           "EXAMPLES\n"
    614           "  kit cpio -o -F root.cpio root/\n"
    615           "  kit cpio -t -F root.cpio\n"
    616           "  mkdir unpacked\n"
    617           "  (cd unpacked && kit cpio -i -F ../root.cpio)\n"
    618           "  kit cpio -o root/ | kit cpio -t\n"
    619           "  kit cpio -o -z root/ > root.cpio.gz\n"
    620           "  kit cpio -t < root.cpio.gz       # gzip auto-detected\n"
    621           "  kit cpio -o --lz4 root/ > root.cpio.lz4\n"
    622           "  kit cpio -i < root.cpio.lz4      # extract into cwd\n"
    623           "\n"
    624           "EXIT CODES\n"
    625           "  0   success           1   I/O or format error   2   bad usage\n")));
    626 }
    627 
    628 /* ---- list ---- */
    629 
    630 typedef struct CpioListCtx {
    631   int verbose;
    632 } CpioListCtx;
    633 
    634 static int cpio_list_visit(void* user, const CpioEntry* e) {
    635   CpioListCtx* c = (CpioListCtx*)user;
    636   if (!c->verbose) {
    637     driver_printf("%.*s\n", (int)e->name_len, e->name);
    638     return 0;
    639   }
    640   if (e->filetype == 7) {
    641     driver_printf("%06o %8u %.*s -> %.*s\n", (unsigned)(e->mode & 07777u),
    642                   (unsigned)e->size, (int)e->name_len, e->name, (int)e->size,
    643                   (const char*)e->data);
    644   } else {
    645     driver_printf("%06o %8u %.*s\n", (unsigned)(e->mode & 07777u),
    646                   (unsigned)e->size, (int)e->name_len, e->name);
    647   }
    648   return 0;
    649 }
    650 
    651 /* ---- extract ---- */
    652 
    653 typedef struct CpioExtractCtx {
    654   DriverEnv* env;
    655   KitContext* ctx;
    656   int verbose;
    657 } CpioExtractCtx;
    658 
    659 /* Reject absolute paths, ".." components, embedded NUL, and empty names so a
    660  * crafted archive cannot escape the destination directory. */
    661 static int cpio_name_safe(const char* n, uint32_t len) {
    662   uint32_t i, comp = 0;
    663   if (len == 0 || n[0] == '/') return 0;
    664   for (i = 0; i <= len; ++i) {
    665     char ch = (i < len) ? n[i] : '/';
    666     if (i < len && ch == '\0') return 0;
    667     if (ch == '/') {
    668       uint32_t cl = i - comp;
    669       if (cl == 2u && n[comp] == '.' && n[comp + 1u] == '.') return 0;
    670       comp = i + 1u;
    671     }
    672   }
    673   return 1;
    674 }
    675 
    676 /* mkdir -p the parent directory of `name` (which has no trailing slash). */
    677 static int cpio_make_parents(DriverEnv* env, const char* name) {
    678   size_t len = driver_strlen(name);
    679   size_t i = len;
    680   char* p;
    681   int rc;
    682   while (i > 0 && name[i - 1u] != '/') --i;
    683   if (i <= 1u) return 0; /* no parent component */
    684   p = (char*)driver_alloc(env, i); /* i-1 chars + NUL */
    685   if (!p) return 1;
    686   memcpy(p, name, i - 1u);
    687   p[i - 1u] = '\0';
    688   rc = driver_mkdir_p(env, p);
    689   driver_free(env, p, i);
    690   return rc;
    691 }
    692 
    693 static int cpio_extract_visit(void* user, const CpioEntry* e) {
    694   CpioExtractCtx* c = (CpioExtractCtx*)user;
    695   DriverEnv* env = c->env;
    696   const char* name = e->name;
    697 
    698   if (!cpio_name_safe(name, e->name_len)) {
    699     driver_errf(CPIO_TOOL, "refusing unsafe member name: %.*s",
    700                 (int)e->name_len, name);
    701     return 1;
    702   }
    703 
    704   if (e->filetype == 3) {
    705     if (driver_mkdir_p(env, name)) {
    706       driver_errf(CPIO_TOOL, "cannot create directory: %s", name);
    707       return 1;
    708     }
    709   } else if (e->filetype == 4) {
    710     KitWriter* w = NULL;
    711     if (cpio_make_parents(env, name)) {
    712       driver_errf(CPIO_TOOL, "cannot create parent of: %s", name);
    713       return 1;
    714     }
    715     if (c->ctx->file_io->open_writer(c->ctx->file_io->user, name, &w) !=
    716         KIT_OK) {
    717       driver_errf(CPIO_TOOL, "cannot create file: %s", name);
    718       return 1;
    719     }
    720     if (e->size) (void)kit_writer_write(w, e->data, e->size);
    721     if (kit_writer_status(w) != KIT_OK) {
    722       driver_writer_abort(w);
    723       kit_writer_close(w);
    724       driver_errf(CPIO_TOOL, "write failed: %s", name);
    725       return 1;
    726     }
    727     kit_writer_close(w);
    728     if (e->mode & 0111u) (void)driver_mark_executable_output(name);
    729   } else if (e->filetype == 7) {
    730     char tgt[CPIO_LINK_MAX];
    731     if (e->size >= sizeof tgt) {
    732       driver_errf(CPIO_TOOL, "symlink target too long: %s", name);
    733       return 1;
    734     }
    735     if (cpio_make_parents(env, name)) {
    736       driver_errf(CPIO_TOOL, "cannot create parent of: %s", name);
    737       return 1;
    738     }
    739     memcpy(tgt, e->data, e->size);
    740     tgt[e->size] = '\0';
    741     (void)driver_remove_file(name); /* replace any stale entry */
    742     if (driver_create_symlink(tgt, name) != 0) {
    743       driver_errf(CPIO_TOOL, "cannot create symlink: %s", name);
    744       return 1;
    745     }
    746   } else {
    747     driver_errf(CPIO_TOOL, "unsupported member type: %.*s",
    748                 (int)e->name_len, name);
    749     return 1;
    750   }
    751 
    752   if (c->verbose) driver_printf("%.*s\n", (int)e->name_len, name);
    753   return 0;
    754 }
    755 
    756 /* Semantic validation runs over the complete archive before extraction starts,
    757  * so an invalid later member cannot leave a partially trusted tree behind. */
    758 static int cpio_extract_validate(void* user, const CpioEntry* e) {
    759   size_t i;
    760   (void)user;
    761   if (!cpio_name_safe(e->name, e->name_len)) {
    762     driver_errf(CPIO_TOOL, "refusing unsafe member name: %.*s",
    763                 (int)e->name_len, e->name);
    764     return 1;
    765   }
    766   if (e->filetype != 3 && e->filetype != 4 && e->filetype != 7) {
    767     driver_errf(CPIO_TOOL, "unsupported member type: %.*s",
    768                 (int)e->name_len, e->name);
    769     return 1;
    770   }
    771   if (e->filetype == 7) {
    772     if (e->size >= CPIO_LINK_MAX) {
    773       driver_errf(CPIO_TOOL, "symlink target too long: %s", e->name);
    774       return 1;
    775     }
    776     for (i = 0; i < e->size; ++i)
    777       if (e->data[i] == 0) {
    778         driver_errf(CPIO_TOOL, "symlink target contains NUL: %s", e->name);
    779         return 1;
    780       }
    781   }
    782   return 0;
    783 }
    784 
    785 /* ---------------------------------------------------------------------------
    786  * Create
    787  * ------------------------------------------------------------------------- */
    788 
    789 static int cpio_write_members(KitContext* ctx, CpioBuild* b, CpioOut* o) {
    790   size_t i;
    791   for (i = 0; i < b->count; ++i) {
    792     CpioMember* m = &b->items[i];
    793     if (m->filetype == 3) {
    794       if (cpio_append(o, m->name, m->name_len, CPIO_S_IFDIR | 0755u, 2u, NULL,
    795                       0u))
    796         return 1;
    797     } else if (m->filetype == 7) {
    798       char tgt[CPIO_LINK_MAX];
    799       if (driver_readlink(m->src, tgt, sizeof tgt) != 0) {
    800         driver_errf(CPIO_TOOL, "cannot read symlink target: %s", m->src);
    801         return 1;
    802       }
    803       if (cpio_append(o, m->name, m->name_len, CPIO_S_IFLNK | 0777u, 1u,
    804                       (const uint8_t*)tgt, (uint32_t)driver_strlen(tgt)))
    805         return 1;
    806     } else { /* regular file */
    807       KitFileData fd = {0};
    808       uint32_t mode = CPIO_S_IFREG | (m->executable ? 0755u : 0644u);
    809       int wrc;
    810       if (ctx->file_io->read_all(ctx->file_io->user, m->src, &fd) != KIT_OK) {
    811         driver_errf(CPIO_TOOL, "cannot read file: %s", m->src);
    812         return 1;
    813       }
    814       if (fd.size > 0xFFFFFFFFu) {
    815         ctx->file_io->release(ctx->file_io->user, &fd);
    816         driver_errf(CPIO_TOOL, "file too large for newc (>= 4 GiB): %s",
    817                     m->src);
    818         return 1;
    819       }
    820       wrc = cpio_append(o, m->name, m->name_len, mode, 1u, fd.data,
    821                         (uint32_t)fd.size);
    822       ctx->file_io->release(ctx->file_io->user, &fd);
    823       if (wrc) return 1;
    824     }
    825   }
    826   return cpio_finish(o);
    827 }
    828 
    829 static int cpio_create(DriverEnv* env, KitContext* ctx, const CpioOpts* opt,
    830                        const char** ops, size_t nops) {
    831   CpioBuild b;
    832   CpioOut co;
    833   KitWriter* target = NULL; /* where records are written */
    834   KitWriter* mem = NULL;    /* buffer when compressing */
    835   KitWriter* out = NULL;    /* final output (file/stdout) */
    836   int owned_out = 0;
    837   int rc = 1;
    838   size_t i;
    839 
    840   memset(&b, 0, sizeof b);
    841   b.env = env;
    842 
    843   for (i = 0; i < nops; ++i) {
    844     int abs_stripped = 0;
    845     const char* cleaned = cpio_clean_name(ops[i], &abs_stripped);
    846     char* owned = NULL;
    847     size_t owned_sz = 0;
    848     size_t cl;
    849     if (abs_stripped) {
    850       driver_errf(CPIO_TOOL,
    851                   "warning: storing %s as relative (leading '/' stripped)",
    852                   ops[i]);
    853     }
    854     /* Drop a trailing slash so a directory operand archives as "dir" not
    855      * "dir/". An empty cleaned name ("." / "/" / "./") means contents-only. */
    856     cl = driver_strlen(cleaned);
    857     while (cl > 0 && cleaned[cl - 1u] == '/') --cl;
    858     owned = (char*)driver_alloc(env, cl + 1u);
    859     if (!owned) goto done;
    860     owned_sz = cl + 1u;
    861     memcpy(owned, cleaned, cl);
    862     owned[cl] = '\0';
    863     if (cl > 0 && cpio_name_has_dotdot(owned)) {
    864       driver_errf(CPIO_TOOL,
    865                   "operand escapes the archive root (.. component): %s; cd into "
    866                   "the directory and pass a relative path",
    867                   ops[i]);
    868       driver_free(env, owned, owned_sz);
    869       rc = 1;
    870       goto done;
    871     }
    872     rc = cpio_collect(&b, ops[i], owned);
    873     driver_free(env, owned, owned_sz);
    874     if (rc) goto done;
    875   }
    876 
    877   if (cpio_sort(&b)) {
    878     driver_errf(CPIO_TOOL, "out of memory sorting members");
    879     rc = 1;
    880     goto done;
    881   }
    882 
    883   if (opt->compress) {
    884     if (kit_writer_mem(env->heap, &mem) != KIT_OK) {
    885       driver_errf(CPIO_TOOL, "out of memory");
    886       rc = 1;
    887       goto done;
    888     }
    889     target = mem;
    890   } else if (opt->file) {
    891     if (ctx->file_io->open_writer(ctx->file_io->user, opt->file, &out) !=
    892         KIT_OK) {
    893       driver_errf(CPIO_TOOL, "cannot open output: %s", opt->file);
    894       rc = 1;
    895       goto done;
    896     }
    897     owned_out = 1;
    898     target = out;
    899   } else {
    900     out = driver_stdout_writer(env);
    901     owned_out = 1;
    902     target = out;
    903   }
    904 
    905   memset(&co, 0, sizeof co);
    906   co.w = target;
    907   co.next_ino = 1u;
    908   co.crc = opt->crc;
    909 
    910   if (cpio_write_members(ctx, &b, &co) || co.err) {
    911     if (owned_out && out) driver_writer_abort(out);
    912     rc = 1;
    913     goto done;
    914   }
    915 
    916   if (opt->compress) {
    917     const uint8_t* bytes;
    918     size_t blen = 0;
    919     bytes = kit_writer_mem_bytes(mem, &blen);
    920     if (opt->file) {
    921       if (ctx->file_io->open_writer(ctx->file_io->user, opt->file, &out) !=
    922           KIT_OK) {
    923         driver_errf(CPIO_TOOL, "cannot open output: %s", opt->file);
    924         rc = 1;
    925         goto done;
    926       }
    927     } else {
    928       out = driver_stdout_writer(env);
    929     }
    930     owned_out = 1;
    931     if (kit_compress(ctx, opt->cfmt, bytes, blen, out) != KIT_OK) {
    932       driver_writer_abort(out);
    933       rc = 1;
    934       goto done;
    935     }
    936   }
    937 
    938   rc = 0;
    939 
    940 done:
    941   if (owned_out && out) kit_writer_close(out);
    942   if (mem) kit_writer_close(mem);
    943   cpio_build_free(&b);
    944   return rc;
    945 }
    946 
    947 /* ---------------------------------------------------------------------------
    948  * Read (list / extract) — load, optionally decompress, then parse.
    949  * ------------------------------------------------------------------------- */
    950 
    951 /* Recognize zstd / xz so we can reject them specifically instead of letting
    952  * the cpio parser report a meaningless "bad magic". */
    953 static int cpio_is_unsupported_compressed(const uint8_t* d, size_t n) {
    954   static const uint8_t zstd[4] = {0x28, 0xB5, 0x2F, 0xFD};
    955   static const uint8_t xz[6] = {0xFD, '7', 'z', 'X', 'Z', 0x00};
    956   if (n >= 4 && memcmp(d, zstd, 4) == 0) return 1;
    957   if (n >= 6 && memcmp(d, xz, 6) == 0) return 1;
    958   return 0;
    959 }
    960 
    961 static int cpio_read(DriverEnv* env, KitContext* ctx, const CpioOpts* opt) {
    962   const uint8_t* raw = NULL;
    963   size_t rawlen = 0;
    964   DriverLoad ld = {0};
    965   uint8_t* sbuf = NULL;
    966   size_t sbuf_len = 0;
    967   int loaded_file = 0, loaded_stdin = 0;
    968   const uint8_t* payload;
    969   size_t payload_len;
    970   KitWriter* dmem = NULL;
    971   KitCompressFormat cfmt;
    972   int rc = 1;
    973 
    974   if (opt->file) {
    975     KitSlice in;
    976     if (driver_load_bytes(&env->file_io, CPIO_TOOL, opt->file, &ld, &in) != 0)
    977       return 1;
    978     loaded_file = 1;
    979     raw = in.data;
    980     rawlen = in.len;
    981   } else {
    982     if (!driver_read_stdin(env, &sbuf, &sbuf_len)) {
    983       driver_errf(CPIO_TOOL, "failed to read stdin");
    984       return 1;
    985     }
    986     loaded_stdin = 1;
    987     raw = sbuf;
    988     rawlen = sbuf_len;
    989   }
    990 
    991   payload = raw;
    992   payload_len = rawlen;
    993 
    994   if (cpio_is_unsupported_compressed(raw, rawlen)) {
    995     driver_errf(CPIO_TOOL,
    996                 "archive is zstd/xz-compressed; kit cpio supports gzip and "
    997                 "lz4 only");
    998     goto done;
    999   }
   1000   if (kit_compress_detect(raw, rawlen, &cfmt) == KIT_OK) {
   1001     size_t dlen = 0;
   1002     if (kit_writer_mem(env->heap, &dmem) != KIT_OK) {
   1003       driver_errf(CPIO_TOOL, "out of memory");
   1004       goto done;
   1005     }
   1006     if (kit_decompress(ctx, cfmt, raw, rawlen, dmem) != KIT_OK) goto done;
   1007     payload = kit_writer_mem_bytes(dmem, &dlen);
   1008     payload_len = dlen;
   1009   } else if (opt->decompress) {
   1010     driver_errf(CPIO_TOOL, "-d given but input is not gzip/lz4-compressed");
   1011     goto done;
   1012   }
   1013 
   1014   /* Structural validation is intentionally separate from presentation and
   1015    * extraction. For extraction the first pass also validates every path and
   1016    * supported member type before the first filesystem mutation. */
   1017   if (cpio_parse(payload, payload_len,
   1018                  opt->mode == 'i' ? cpio_extract_validate : NULL, NULL) != 0)
   1019     goto done;
   1020 
   1021   if (opt->mode == 't') {
   1022     CpioListCtx lc;
   1023     lc.verbose = opt->verbose;
   1024     rc = cpio_parse(payload, payload_len, cpio_list_visit, &lc);
   1025   } else {
   1026     CpioExtractCtx xc;
   1027     xc.env = env;
   1028     xc.ctx = ctx;
   1029     xc.verbose = opt->verbose;
   1030     rc = cpio_parse(payload, payload_len, cpio_extract_visit, &xc);
   1031   }
   1032 
   1033 done:
   1034   if (dmem) kit_writer_close(dmem);
   1035   if (loaded_file) driver_release_bytes(&env->file_io, &ld);
   1036   if (loaded_stdin && sbuf) driver_free(env, sbuf, sbuf_len);
   1037   return rc;
   1038 }
   1039 
   1040 /* ---------------------------------------------------------------------------
   1041  * Entry point
   1042  * ------------------------------------------------------------------------- */
   1043 
   1044 static int cpio_opt_arg(int argc, char** argv, int* i, const char* tool,
   1045                         const char* flag, const char** out) {
   1046   /* Accept "--flag=value", "-Fvalue", or a separate next argument. */
   1047   const char* a = argv[*i];
   1048   size_t flen = driver_strlen(flag);
   1049   if (driver_strneq(a, flag, flen) && a[flen] == '=') {
   1050     *out = a + flen + 1u;
   1051     return 0;
   1052   }
   1053   if (*i + 1 >= argc) {
   1054     driver_errf(tool, "%s requires an argument", flag);
   1055     return 1;
   1056   }
   1057   *out = argv[++(*i)];
   1058   return 0;
   1059 }
   1060 
   1061 static int cpio_set_mode(CpioOpts* opt, char mode, const char* spelling) {
   1062   if (opt->mode && opt->mode != mode) {
   1063     driver_errf(CPIO_TOOL, "conflicting archive modes: -%c and %s", opt->mode,
   1064                 spelling);
   1065     return 1;
   1066   }
   1067   opt->mode = mode;
   1068   return 0;
   1069 }
   1070 
   1071 int driver_cpio(int argc, char** argv) {
   1072   DriverEnv env;
   1073   KitContext ctx;
   1074   CpioOpts o;
   1075   const char** ops = NULL;
   1076   size_t nops = 0;
   1077   int i, rc = 2;
   1078 
   1079   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
   1080     driver_help_cpio();
   1081     return 0;
   1082   }
   1083 
   1084   memset(&o, 0, sizeof o);
   1085   driver_env_init(&env);
   1086   ctx = driver_env_to_context(&env);
   1087 
   1088   ops = (const char**)driver_alloc(&env, sizeof(char*) * (size_t)(argc));
   1089   if (!ops) {
   1090     driver_errf(CPIO_TOOL, "out of memory");
   1091     rc = 1;
   1092     goto done;
   1093   }
   1094 
   1095   for (i = 1; i < argc; ++i) {
   1096     const char* a = argv[i];
   1097 
   1098     if (driver_streq(a, "--")) { /* end of options */
   1099       for (++i; i < argc; ++i) ops[nops++] = argv[i];
   1100       break;
   1101     }
   1102 
   1103     if (driver_strneq(a, "--", 2)) { /* long option */
   1104       if (driver_streq(a, "--create")) {
   1105         if (cpio_set_mode(&o, 'o', a)) goto done;
   1106       } else if (driver_streq(a, "--list")) {
   1107         if (cpio_set_mode(&o, 't', a)) goto done;
   1108       } else if (driver_streq(a, "--extract")) {
   1109         if (cpio_set_mode(&o, 'i', a)) goto done;
   1110       } else if (driver_streq(a, "--verbose")) {
   1111         o.verbose = 1;
   1112       } else if (driver_streq(a, "--decompress")) {
   1113         o.decompress = 1;
   1114       } else if (driver_streq(a, "--lz4")) {
   1115         o.compress = 1;
   1116         o.cfmt = KIT_COMPRESS_LZ4_FRAME;
   1117       } else if (driver_streq(a, "--file") || driver_strneq(a, "--file=", 7)) {
   1118         if (cpio_opt_arg(argc, argv, &i, CPIO_TOOL, "--file", &o.file))
   1119           goto done;
   1120       } else if (driver_streq(a, "--format") ||
   1121                  driver_strneq(a, "--format=", 9)) {
   1122         const char* v;
   1123         if (cpio_opt_arg(argc, argv, &i, CPIO_TOOL, "--format", &v)) goto done;
   1124         if (cpio_parse_format(v, &o.crc)) {
   1125           driver_errf(CPIO_TOOL, "unsupported format: %s (use newc or crc)", v);
   1126           goto done;
   1127         }
   1128       } else if (driver_streq(a, "--compress") ||
   1129                  driver_strneq(a, "--compress=", 11)) {
   1130         const char* v;
   1131         if (cpio_opt_arg(argc, argv, &i, CPIO_TOOL, "--compress", &v)) goto done;
   1132         if (cpio_parse_compress(v, &o.cfmt)) {
   1133           driver_errf(CPIO_TOOL,
   1134                       "unsupported compressor: %s (kit cpio supports gzip and "
   1135                       "lz4 only)",
   1136                       v);
   1137           goto done;
   1138         }
   1139         o.compress = 1;
   1140       } else {
   1141         driver_errf(CPIO_TOOL, "unknown option: %s", a);
   1142         goto done;
   1143       }
   1144       continue;
   1145     }
   1146 
   1147     if (a[0] == '-' && a[1] != '\0') { /* short cluster, e.g. -tv, -Fout */
   1148       int j;
   1149       for (j = 1; a[j]; ++j) {
   1150         char ch = a[j];
   1151         if (ch == 'o') {
   1152           if (cpio_set_mode(&o, 'o', "-o")) goto done;
   1153         } else if (ch == 't') {
   1154           if (cpio_set_mode(&o, 't', "-t")) goto done;
   1155         } else if (ch == 'i') {
   1156           if (cpio_set_mode(&o, 'i', "-i")) goto done;
   1157         } else if (ch == 'v') {
   1158           o.verbose = 1;
   1159         } else if (ch == 'd') {
   1160           o.decompress = 1;
   1161         } else if (ch == 'z') {
   1162           o.compress = 1;
   1163           o.cfmt = KIT_COMPRESS_GZIP;
   1164         } else if (ch == 'F' || ch == 'H') {
   1165           const char* v;
   1166           if (a[j + 1]) {
   1167             v = a + j + 1; /* -Fvalue */
   1168           } else if (i + 1 < argc) {
   1169             v = argv[++i]; /* -F value */
   1170           } else {
   1171             driver_errf(CPIO_TOOL, "-%c requires an argument", ch);
   1172             goto done;
   1173           }
   1174           if (ch == 'F') {
   1175             o.file = v;
   1176           } else if (cpio_parse_format(v, &o.crc)) {
   1177             driver_errf(CPIO_TOOL, "unsupported format: %s (use newc or crc)",
   1178                         v);
   1179             goto done;
   1180           }
   1181           break; /* value consumed the rest of the cluster */
   1182         } else {
   1183           driver_errf(CPIO_TOOL, "unknown option: -%c", ch);
   1184           goto done;
   1185         }
   1186       }
   1187       continue;
   1188     }
   1189 
   1190     ops[nops++] = a; /* operand (a file/dir, or "-") */
   1191   }
   1192 
   1193   if (o.mode == 0) {
   1194     driver_errf(CPIO_TOOL, "one of -o, -t, or -i is required");
   1195     goto done;
   1196   }
   1197   if (o.compress && o.mode != 'o') {
   1198     driver_errf(CPIO_TOOL, "compression flags apply to -o (create) only");
   1199     goto done;
   1200   }
   1201   if (o.decompress && o.mode == 'o') {
   1202     driver_errf(CPIO_TOOL, "-d applies to -t/-i (read) only");
   1203     goto done;
   1204   }
   1205   if (o.mode != 'o' && nops > 0) {
   1206     driver_errf(CPIO_TOOL,
   1207                 "member patterns are not supported: %s", ops[0]);
   1208     rc = 1;
   1209     goto done;
   1210   }
   1211 
   1212   if (o.mode == 'o') {
   1213     rc = cpio_create(&env, &ctx, &o, ops, nops);
   1214   } else {
   1215     rc = cpio_read(&env, &ctx, &o);
   1216   }
   1217 
   1218 done:
   1219   if (ops) driver_free(&env, ops, sizeof(char*) * (size_t)(argc));
   1220   driver_env_fini(&env);
   1221   return rc;
   1222 }