Here's a revised cpio.
I've reduced the use of malloc(), dropped an extra function call, and
-at least in theory- allowed proper handling of non-regular files.
(If we have a file we can't read, we still should record it when it's
of a type where file content is ignored).
Thanks,
Isaac Dunham
/* 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, struct stat st))
{
int fd;
struct stat st;
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';
if (lstat(name, &st) == -1) continue;
fd = open(name, O_RDONLY);
if (fd > 0 || !S_ISREG(st.st_mode)) {
function(fd, name, st);
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));
}
//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_member);
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");
}
}
_______________________________________________
Toybox mailing list
[email protected]
http://lists.landley.net/listinfo.cgi/toybox-landley.net