Slackware's Dobbs Tux with Pipe logo - not created by me
Dave's Slackware Package Blog

cpio

Ye Olde File Archiver
Created: 2020-10-06

After telling us that "GNU info rules and man drools," the man page goes on to give us this description:

GNU cpio copies files between archives and directories. It supports the following archive formats: old binary cpio, old portable cpio, SVR4 cpio with and without checksum, HP cpio, and various tar formats.

So I guess the question is: what’s the difference between tar and cpio?

Well, when I turned to "A System V Tape Archiver: cpio" in my 1994 printing of Unix Power Tools, it started with:

There was a time when people used to debate whether the BSD tar (tape archiver) or the System V cpio (copy in/out) was the better file archive and backup program. At this point, there’s no question. No one ships out cpio archives over the net. tar is widespread, and because there are free versions available, including GNU tar, there’s no reason why you should have to read a cpio archive from someone else.

That entry was written by Tim O’Reilly.

Clearly BSD’s tar won that one and is still the dominant file archiving solution on UNIX-likes.

But as the Wikipedia page for cpio points out:

The use of cpio by the RPM Package Manager, in the initramfs program of Linux kernel 2.6, and in Apple’s Installer (pax) make cpio an important archiving tool.

There is also some extremely great information in the answers to this superuser.com question.

Invoking cpio

The synopsis section of the man page looks like ASCII art. But it boils down to -o for out (create), -i for in (extract), and -p for pass-through (which is apparently one of the remaining positive traits of cpio).

The difference between just -o and -p is that -o "reads a list of file names from the standard input and create on the standard output" whereas -p "reads a list of file names from the standard input and copy them to the specified directory."

Either way, it wants the file list on STDIN.

$ echo * | cpio -o > foo.cpio
cpio: foo foo.cpio foo2 foobar: Cannot stat: No such file or directory

I'm gonna set up the simplest example I can, though:
1 block

Errr…​oh, looks like cpio wants them newline separated? Let’s see, ls will give us that:

$ ls | cpio -o > foo.cpio
1 block

$ file foo.cpio
foo.cpio: cpio archive

That looks promising. And now can we extract it? Again, this old school tool expects the input file to come from STDIN.

$ mkdir foo_out
$ cd foo_out
$ cpio -i < ../foo.cpio
1 block
$ ls
foo  foo2  foo_out  foobar

I'm gonna set up the simplest example I can, though:

There we go.

Now, let’s see what all of this "pass-through" business is about. For this, we’ve gotta use the info manual. Grrr. It’s very well written, but…​grrrr. (Okay, I’m willing to admit that I simply haven’t put the time into learning Emacs keybindings (which info shares) and that’s something I should remedy.)

Here’s the online manual on gnu.org regarding the pass-through option:

cpio copies files from one directory tree to another, combining the copy-out and copy-in steps without actually using an archive. It reads the list of files to copy from the standard input; the directory into which it will copy them is given as a non-option argument.

Oh! So this option isn’t about archiving at all. This turns cpio into a copy tool in its own right. There are actually a ton of options we can use with the pass-through option to specify how we want to copy the files.

But I’m gonna set up the simplest example I can:

$ touch foo{1..3} bar{1..3}
$ mkdir archive
$ find -name 'foo*'
./foo1
./foo2
./foo3

$ find -name 'foo*' | cpio -p archive
0 blocks
$ ls archive
foo1  foo2  foo3

Okay, not super impressive, sure. But because it’s taking a file list, we can copy all found files in subdirectories as well.

First, I’ll setup a minimal multi-directory file structure to get our files from:

$ mkdir more_stuff
$ touch more_stuff/foo{4..9} more_stuff/bar{4..9}
$ tree
.
|-- archive2
|-- bar1
|-- bar2
|-- bar3
|-- foo1
|-- foo2
|-- foo3
`-- more_stuff
    |-- bar4
    |-- bar5
    |-- bar6
    |-- bar7
    |-- bar8
    |-- bar9
    |-- foo4
    |-- foo5
    |-- foo6
    |-- foo7
    |-- foo8
    `-- foo9

2 directories, 18 files

Now we’ll do the same thing as before, but this time we’ll need to add the -d option to make directories as needed.

$ mkdir archive2
$ find -name 'foo*' | cpio -p -d archive2
0 blocks
$ tree archive2
archive2
|-- foo1
|-- foo2
|-- foo3
`-- more_stuff
    |-- foo4
    |-- foo5
    |-- foo6
    |-- foo7
    |-- foo8
    `-- foo9

1 directory, 9 files

There we go, all found "foo" files safely archived in another directory. I love that any file list on STDIN will do, so this is an extremely versitile ability.

mt

You’ll be pleased to know that the cpio package also comes with the mt tool for controlling your magnetic tape drives!

The man page for mt even points to a current GitHub-hosted repo: https://github.com/iustin/mt-st.

A bug?

I also noticed that there is an outstanding bug noted for Slackware (slackware-current circa 2001) that cpio --sparse causes data corruption! A fix was submitted, but if it’s still in the TODO list…​look out for that, I guess.

Note to self

To get tree to display only 7-bit ASCII characters (which I find much more dependable to store in portable documents like this long-term), I’m actually using this invocation:

$ tree --charset=ascii

Well, until next time, happy hacking!