aboutsummaryrefslogtreecommitdiff
path: root/toys/pending/cpio.c
diff options
context:
space:
mode:
authorIsaac Dunham <ibid.ag@gmail.com>2013-10-14 11:15:22 -0500
committerIsaac Dunham <ibid.ag@gmail.com>2013-10-14 11:15:22 -0500
commitc50057e5be75b65c2359803ecc776378b44f2e3f (patch)
tree875f5207a04683523eee95055337a8c526f5469f /toys/pending/cpio.c
parent03c1b0b202dd4179d472497b08667fad8d4c4530 (diff)
downloadtoybox-c50057e5be75b65c2359803ecc776378b44f2e3f.tar.gz
I've finally gotten 'cpio' into a shape where it could be useable.
This version can archive and extract directories, sockets, FIFOs, devices, symlinks, and regular files. Supported options are -iot, -H FMT (which is a dummy right now). It only writes newc, and could read newc or newcrc. This does NOT implement -d, which essentially is equivalent to mkdir -p $(dirname $FILE) for every file that needs it. Hard links are not supported, though it would be easy to add them given a hash table or something like that. I also have not implemented the "<n> blocks" output on stderr. If desired, I can add it pretty simply. There is one assumption this makes: that the mode of a file, as mode_t, is bitwise equivalent to the mode as defined for the cpio format. This is true of Linux, but is not mandated by POSIX. If it is compiled for a system where that is false, the archives will not be portable.
Diffstat (limited to 'toys/pending/cpio.c')
-rw-r--r--toys/pending/cpio.c240
1 files changed, 240 insertions, 0 deletions
diff --git a/toys/pending/cpio.c b/toys/pending/cpio.c
new file mode 100644
index 00000000..6830bc72
--- /dev/null
+++ b/toys/pending/cpio.c
@@ -0,0 +1,240 @@
+/* cpio.c - a basic cpio
+ *
+ * Written 2013 AD by Isaac Dunham; this code is placed under the
+ * same license as toybox or as CC0, at your option.
+USE_CPIO(NEWTOY(cpio, "H:iot", TOYFLAG_BIN))
+
+config CPIO
+ bool "cpio"
+ default n
+ help
+ usage: cpio { -i | -o | -t } [-H fmt]
+
+ copy files into and out of an archive
+ -i extract from archive into file system (stdin is an archive)
+ -o create archive (stdin is a list of files, stdout is an archive)
+ -t list files (stdin is an archive, stdout is a list of files)
+ -H fmt Write archive in specified format:
+ newc SVR4 new character format (default)
+*/
+#define FOR_cpio
+#include "toys.h"
+
+GLOBALS(
+char * fmt;
+)
+
+/* Iterate through a list of files, read from stdin.
+ * No users need rw.
+ */
+void loopfiles_stdin(void (*function)(int fd, char *name))
+{
+ int fd;
+ char *name = toybuf;
+
+ while (name != NULL){
+ memset(toybuf, 0, sizeof(toybuf));
+ name = fgets(toybuf, sizeof(toybuf) - 1, stdin);
+
+ if (name != NULL) {
+ if (toybuf[strlen(name) - 1] == '\n' ) {
+ toybuf[strlen(name) - 1 ] = '\0';
+ fd = open(name, O_RDONLY);
+ if (fd > 0) {
+ function(fd, name);
+ close(fd);
+ }
+ errno = 0;
+ }
+ }
+ }
+}
+
+struct newc_header {
+ char c_magic[6];
+ char c_ino[8];
+ char c_mode[8];
+ char c_uid[8];
+ char c_gid[8];
+ char c_nlink[8];
+ char c_mtime[8];
+ char c_filesize[8];
+ char c_devmajor[8];
+ char c_devminor[8];
+ char c_rdevmajor[8];
+ char c_rdevminor[8];
+ char c_namesize[8];
+ char c_check[8];
+};
+
+void write_cpio_member(int fd, char *name, struct stat buf)
+{
+ char ahdr[sizeof(struct newc_header) + 1];
+ struct newc_header *hdr = (struct newc_header *)ahdr;
+ size_t out = 0;
+ unsigned int n = 0x00000000, nlen = strlen(name) + 1;
+
+ memset(hdr, '0', sizeof(struct newc_header));
+ if (S_ISDIR(buf.st_mode) || S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode)
+ || S_ISFIFO(buf.st_mode) || S_ISSOCK(buf.st_mode))
+ buf.st_size = 0;
+ snprintf((char *)(hdr), sizeof(struct newc_header)+1,
+ "070701%08X%08X" "%08X%08X"
+ "%08X%08X%08X"
+ "%08X%08X" "%08X%08X%08X00000000",
+ (unsigned int)(buf.st_ino), buf.st_mode, buf.st_uid, buf.st_gid,
+ buf.st_nlink, (uint32_t)(buf.st_mtime), (uint32_t)(buf.st_size),
+ major(buf.st_dev), minor(buf.st_dev),
+ major(buf.st_rdev), minor(buf.st_rdev), nlen);
+ write(1, hdr, sizeof(struct newc_header));
+ write(1, name, nlen);
+ if ((nlen + 2) % 4) write(1, &n, 4 - ((nlen + 2) % 4));
+ if (S_ISLNK(buf.st_mode)) {
+ ssize_t llen = readlink(name, toybuf, sizeof(toybuf) - 1);
+ if (llen > 0) {
+ toybuf[llen] = '\0';
+ write(1, toybuf, buf.st_size);
+ }
+ } else if (buf.st_size) {
+ for (; (lseek(fd, 0, SEEK_CUR) < (uint32_t)(buf.st_size));) {
+ out = read(fd, toybuf, sizeof(toybuf));
+ if (out > 0) write(1, toybuf, out);
+ if (errno || out < sizeof(toybuf)) break;
+ }
+ }
+ if (buf.st_size % 4) write(1, &n, 4 - (buf.st_size % 4));
+}
+
+void write_cpio_call(int fd, char *name)
+{
+ struct stat buf;
+ if (lstat(name, &buf) == -1) return;
+ write_cpio_member(fd, name, buf);
+}
+
+//convert hex to uint; mostly to allow using bits of non-terminated strings
+unsigned int htou(char * hex)
+{
+ unsigned int ret = 0, i = 0;
+
+ for (;(i < 8 && hex[i]);) {
+ ret *= 16;
+ switch(hex[i]) {
+ case '0':
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ ret += hex[i] - '1' + 1;
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ ret += hex[i] - 'A' + 10;
+ break;
+ }
+ i++;
+ }
+ return ret;
+}
+
+/* Read one cpio record.
+ * Returns 0 for last record,
+ * 1 for "continue".
+ */
+int read_cpio_member(int fd, int how)
+{
+ uint32_t nsize, fsize;
+ mode_t mode = 0;
+ int pad, ofd = 0;
+ struct newc_header hdr;
+ char *name;
+ dev_t dev = 0;
+
+ xreadall(fd, &hdr, sizeof(struct newc_header));
+ nsize = htou(hdr.c_namesize);
+ name = xmalloc(nsize);
+ xreadall(fd, name, nsize);
+ if (!strcmp("TRAILER!!!", name)) return 0;
+ fsize = htou(hdr.c_filesize);
+ mode += htou(hdr.c_mode);
+ pad = 4 - ((nsize + 2) % 4); // 2 == sizeof(struct newc_header) % 4
+ if (pad < 4) xreadall(fd, toybuf, pad);
+ pad = 4 - (fsize % 4);
+ if (how & 1) {
+ if (S_ISDIR(mode)) {
+ ofd = mkdir(name, mode);
+ } else if (S_ISLNK(mode)) {
+ memset(toybuf, 0, sizeof(toybuf));
+ if (fsize < sizeof(toybuf)) {
+ pad = readall(fd, toybuf, fsize);
+ if (pad < fsize) error_exit("short archive");
+ pad = 4 - (fsize % 4);
+ fsize = 0;
+ if (symlink(toybuf, name)) {
+ perror_msg("could not write link %s", name);
+ toys.exitval |= 1;
+ }
+ } else {
+ perror_msg("link too long: %s", name);
+ toys.exitval |= 1;
+ }
+ } else if (S_ISBLK(mode)||S_ISCHR(mode)||S_ISFIFO(mode)||S_ISSOCK(mode)) {
+ dev = makedev(htou(hdr.c_rdevmajor),htou(hdr.c_rdevminor));
+ ofd = mknod(name, mode, dev);
+ } else {
+ ofd = creat(name, mode);
+ }
+ if (ofd == -1) {
+ error_msg("could not create %s", name);
+ toys.exitval |= 1;
+ }
+ }
+ errno = 0;
+ if (how & 2) puts(name);
+ while (fsize) {
+ int i;
+ memset(toybuf, 0, sizeof(toybuf));
+ i = readall(fd, toybuf, (fsize>sizeof(toybuf)) ? sizeof(toybuf) : fsize);
+ if (i < 1) error_exit("archive too short");
+ if (ofd > 0) writeall(ofd, toybuf, i);
+ fsize -= i;
+ }
+ if (pad < 4) xreadall(fd, toybuf, pad);
+ return 1;
+}
+
+void read_cpio_archive(int fd, int how)
+{
+ for(;;) {
+ if (!read_cpio_member(fd, how)) return;
+ }
+}
+
+void cpio_main(void)
+{
+ switch (toys.optflags & (FLAG_i | FLAG_o | FLAG_t)) {
+ case FLAG_o:
+ loopfiles_stdin(write_cpio_call);
+ write(1, "07070100000000000000000000000000000000000000010000000000000000"
+ "000000000000000000000000000000000000000B00000000TRAILER!!!\0\0\0", 124);
+ break;
+ case FLAG_i:
+ read_cpio_archive(0, 1);
+ case FLAG_t:
+ case (FLAG_t | FLAG_i):
+ read_cpio_archive(0, 2);
+ break;
+ default:
+ error_exit("must use one of -iot");
+ }
+}