Initial commit.
diff --git a/src/cpio.c b/src/cpio.c
new file mode 100644
index 0000000..c4add22
--- /dev/null
+++ b/src/cpio.c
@@ -0,0 +1,77 @@
+#include "cpio.h"
+
+#include <stdint.h>
+
+#include "std.h"
+
+#pragma pack(push, 1)
+struct cpio_header {
+	uint16_t magic;
+	uint16_t dev;
+	uint16_t ino;
+	uint16_t mode;
+	uint16_t uid;
+	uint16_t gid;
+	uint16_t nlink;
+	uint16_t rdev;
+	uint16_t mtime[2];
+	uint16_t namesize;
+	uint16_t filesize[2];
+};
+#pragma pack(pop)
+
+void cpio_init(struct cpio *c, const void *buf, size_t size)
+{
+	c->first = buf;
+	c->total_size = size;
+}
+
+void cpio_init_iter(struct cpio *c, struct cpio_iter *iter)
+{
+	iter->cur = c->first;
+	iter->size_left = c->total_size;
+}
+
+bool cpio_next(struct cpio_iter *iter, const char **name,
+	       const void **contents, size_t *size)
+{
+	const struct cpio_header *h = iter->cur;
+	size_t size_left;
+	size_t filelen;
+	size_t namelen;
+
+	size_left = iter->size_left;
+	if (size_left < sizeof(struct cpio_header))
+		return false;
+
+	/* TODO: Check magic. */
+
+	size_left -= sizeof(struct cpio_header);
+	namelen = (h->namesize + 1) & ~1;
+	if (size_left < namelen)
+		return false;
+
+	size_left -= namelen;
+	filelen = (size_t)h->filesize[0] << 16 | h->filesize[1];
+	if (size_left < filelen)
+		return false;
+
+	/* TODO: Check that string is null-terminated. */
+	/* TODO: Check that trailler is not returned. */
+
+	/* Stop enumerating files when we hit the end marker. */
+	if (!strcmp((const char *)(iter->cur + 1), "TRAILER!!!"))
+		return false;
+
+	size_left -= filelen;
+
+	*name = (const char *)(iter->cur + 1);
+	*contents = *name + namelen;
+	*size = filelen;
+
+	iter->cur = (struct cpio_header *)((char *)*contents + filelen);
+	iter->cur = (struct cpio_header *)(char *)(((size_t)iter->cur + 1) & ~1);
+	iter->size_left = size_left;
+
+	return true;
+}