781 lines · 17.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#define _GNU_SOURCE#include <stdio.h>#include <stdlib.h>#include <stdint.h>#include <stdbool.h>#include <sys/types.h>#include <sys/stat.h>#include <string.h>#include <unistd.h>#include <time.h>#include <fcntl.h>#include <errno.h>#include <ctype.h>#include <limits.h> /* * Original work by Jeff Garzik * * External file lists, symlink, pipe and fifo support by Thayne Harbaugh * Hard link support by Luciano Rocha */ #define xstr(s) #s#define str(s) xstr(s)#define MIN(a, b) ((a) < (b) ? (a) : (b))#define CPIO_HDR_LEN 110#define CPIO_TRAILER "TRAILER!!!"#define padlen(_off, _align) (((_align) - ((_off) & ((_align) - 1))) % (_align)) /* zero-padding the filename field for data alignment is limited by PATH_MAX */static char padding[PATH_MAX];static unsigned int offset;static unsigned int ino = 721;static time_t default_mtime;static bool do_file_mtime;static bool do_csum = false;static int outfd = STDOUT_FILENO;static unsigned int dalign; struct file_handler {	const char *type;	int (*handler)(const char *line);}; static int push_buf(const char *name, size_t name_len){	ssize_t len; 	len = write(outfd, name, name_len);	if (len != name_len)		return -1; 	offset += name_len;	return 0;} static int push_pad(size_t padlen){	ssize_t len = 0; 	if (!padlen)		return 0; 	if (padlen < sizeof(padding))		len = write(outfd, padding, padlen);	if (len != padlen)		return -1; 	offset += padlen;	return 0;} static int push_rest(const char *name, size_t name_len){	ssize_t len; 	len = write(outfd, name, name_len);	if (len != name_len)		return -1; 	offset += name_len; 	return push_pad(padlen(name_len + CPIO_HDR_LEN, 4));} static int cpio_trailer(void){	int len;	unsigned int namesize = sizeof(CPIO_TRAILER); 	len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"	       "%08X%08X%08X%08X%08X%08X%08X",		do_csum ? "070702" : "070701", /* magic */		0,			/* ino */		0,			/* mode */		(long) 0,		/* uid */		(long) 0,		/* gid */		1,			/* nlink */		(long) 0,		/* mtime */		0,			/* filesize */		0,			/* major */		0,			/* minor */		0,			/* rmajor */		0,			/* rminor */		namesize,		/* namesize */		0);			/* chksum */	offset += len; 	if (len != CPIO_HDR_LEN ||	    push_rest(CPIO_TRAILER, namesize) < 0 ||	    push_pad(padlen(offset, 512)) < 0)		return -1; 	if (fsync(outfd) < 0 && errno != EINVAL)		return -1; 	return 0;} static int cpio_mkslink(const char *name, const char *target,			 unsigned int mode, uid_t uid, gid_t gid){	int len;	unsigned int namesize, targetsize = strlen(target) + 1; 	if (name[0] == '/')		name++;	namesize = strlen(name) + 1; 	len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"	       "%08X%08X%08X%08X%08X%08X%08X",		do_csum ? "070702" : "070701", /* magic */		ino++,			/* ino */		S_IFLNK | mode,		/* mode */		(long) uid,		/* uid */		(long) gid,		/* gid */		1,			/* nlink */		(long) default_mtime,	/* mtime */		targetsize,		/* filesize */		3,			/* major */		1,			/* minor */		0,			/* rmajor */		0,			/* rminor */		namesize,		/* namesize */		0);			/* chksum */	offset += len; 	if (len != CPIO_HDR_LEN ||	    push_buf(name, namesize) < 0 ||	    push_pad(padlen(offset, 4)) < 0 ||	    push_buf(target, targetsize) < 0 ||	    push_pad(padlen(offset, 4)) < 0)		return -1; 	return 0; } static int cpio_mkslink_line(const char *line){	char name[PATH_MAX + 1];	char target[PATH_MAX + 1];	unsigned int mode;	int uid;	int gid;	int rc = -1; 	if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) {		fprintf(stderr, "Unrecognized dir format '%s'", line);		goto fail;	}	rc = cpio_mkslink(name, target, mode, uid, gid); fail:	return rc;} static int cpio_mkgeneric(const char *name, unsigned int mode,		       uid_t uid, gid_t gid){	int len;	unsigned int namesize; 	if (name[0] == '/')		name++;	namesize = strlen(name) + 1; 	len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"	       "%08X%08X%08X%08X%08X%08X%08X",		do_csum ? "070702" : "070701", /* magic */		ino++,			/* ino */		mode,			/* mode */		(long) uid,		/* uid */		(long) gid,		/* gid */		2,			/* nlink */		(long) default_mtime,	/* mtime */		0,			/* filesize */		3,			/* major */		1,			/* minor */		0,			/* rmajor */		0,			/* rminor */		namesize,		/* namesize */		0);			/* chksum */	offset += len; 	if (len != CPIO_HDR_LEN ||	    push_rest(name, namesize) < 0)		return -1; 	return 0;} enum generic_types {	GT_DIR,	GT_PIPE,	GT_SOCK}; struct generic_type {	const char *type;	mode_t mode;}; static const struct generic_type generic_type_table[] = {	[GT_DIR] = {		.type = "dir",		.mode = S_IFDIR	},	[GT_PIPE] = {		.type = "pipe",		.mode = S_IFIFO	},	[GT_SOCK] = {		.type = "sock",		.mode = S_IFSOCK	}}; static int cpio_mkgeneric_line(const char *line, enum generic_types gt){	char name[PATH_MAX + 1];	unsigned int mode;	int uid;	int gid;	int rc = -1; 	if (4 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d", name, &mode, &uid, &gid)) {		fprintf(stderr, "Unrecognized %s format '%s'",			line, generic_type_table[gt].type);		goto fail;	}	mode |= generic_type_table[gt].mode;	rc = cpio_mkgeneric(name, mode, uid, gid); fail:	return rc;} static int cpio_mkdir_line(const char *line){	return cpio_mkgeneric_line(line, GT_DIR);} static int cpio_mkpipe_line(const char *line){	return cpio_mkgeneric_line(line, GT_PIPE);} static int cpio_mksock_line(const char *line){	return cpio_mkgeneric_line(line, GT_SOCK);} static int cpio_mknod(const char *name, unsigned int mode,		       uid_t uid, gid_t gid, char dev_type,		       unsigned int maj, unsigned int min){	int len;	unsigned int namesize; 	if (dev_type == 'b')		mode |= S_IFBLK;	else		mode |= S_IFCHR; 	if (name[0] == '/')		name++;	namesize = strlen(name) + 1; 	len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"	       "%08X%08X%08X%08X%08X%08X%08X",		do_csum ? "070702" : "070701", /* magic */		ino++,			/* ino */		mode,			/* mode */		(long) uid,		/* uid */		(long) gid,		/* gid */		1,			/* nlink */		(long) default_mtime,	/* mtime */		0,			/* filesize */		3,			/* major */		1,			/* minor */		maj,			/* rmajor */		min,			/* rminor */		namesize,		/* namesize */		0);			/* chksum */	offset += len; 	if (len != CPIO_HDR_LEN ||	    push_rest(name, namesize) < 0)		return -1; 	return 0;} static int cpio_mknod_line(const char *line){	char name[PATH_MAX + 1];	unsigned int mode;	int uid;	int gid;	char dev_type;	unsigned int maj;	unsigned int min;	int rc = -1; 	if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",			 name, &mode, &uid, &gid, &dev_type, &maj, &min)) {		fprintf(stderr, "Unrecognized nod format '%s'", line);		goto fail;	}	rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min); fail:	return rc;} static int cpio_mkfile_csum(int fd, unsigned long size, uint32_t *csum){	while (size) {		unsigned char filebuf[65536];		ssize_t this_read;		size_t i, this_size = MIN(size, sizeof(filebuf)); 		this_read = read(fd, filebuf, this_size);		if (this_read <= 0 || this_read > this_size)			return -1; 		for (i = 0; i < this_read; i++)			*csum += filebuf[i]; 		size -= this_read;	}	/* seek back to the start for data segment I/O */	if (lseek(fd, 0, SEEK_SET) < 0)		return -1; 	return 0;} static int cpio_mkfile(const char *name, const char *location,			unsigned int mode, uid_t uid, gid_t gid,			unsigned int nlinks){	struct stat buf;	unsigned long size;	int file, retval, len;	int rc = -1;	time_t mtime;	int namesize, namepadlen;	unsigned int i;	uint32_t csum = 0;	ssize_t this_read; 	mode |= S_IFREG; 	file = open (location, O_RDONLY);	if (file < 0) {		fprintf (stderr, "File %s could not be opened for reading\n", location);		goto error;	} 	retval = fstat(file, &buf);	if (retval) {		fprintf(stderr, "File %s could not be stat()'ed\n", location);		goto error;	} 	if (do_file_mtime) {		mtime = default_mtime;	} else {		mtime = buf.st_mtime;		if (mtime > 0xffffffff) {			fprintf(stderr, "%s: Timestamp exceeds maximum cpio timestamp, clipping.\n",					location);			mtime = 0xffffffff;		} 		if (mtime < 0) {			fprintf(stderr, "%s: Timestamp negative, clipping.\n",					location);			mtime = 0;		}	} 	if (buf.st_size > 0xffffffff) {		fprintf(stderr, "%s: Size exceeds maximum cpio file size\n",			location);		goto error;	} 	if (do_csum && cpio_mkfile_csum(file, buf.st_size, &csum) < 0) {		fprintf(stderr, "Failed to checksum file %s\n", location);		goto error;	} 	size = 0;	namepadlen = 0;	for (i = 1; i <= nlinks; i++) {		if (name[0] == '/')			name++;		namesize = strlen(name) + 1; 		/* data goes on last link, after any alignment padding */		if (i == nlinks)			size = buf.st_size; 		if (dalign && size > dalign) {			namepadlen = padlen(offset + CPIO_HDR_LEN + namesize,					    dalign);			if (namesize + namepadlen > PATH_MAX) {				fprintf(stderr,					"%s: best-effort alignment %u missed\n",					name, dalign);				namepadlen = 0;			}		} 		len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX"		       "%08lX%08X%08X%08X%08X%08X%08X",			do_csum ? "070702" : "070701", /* magic */			ino,			/* ino */			mode,			/* mode */			(long) uid,		/* uid */			(long) gid,		/* gid */			nlinks,			/* nlink */			(long) mtime,		/* mtime */			size,			/* filesize */			3,			/* major */			1,			/* minor */			0,			/* rmajor */			0,			/* rminor */			namesize + namepadlen,	/* namesize */			size ? csum : 0);	/* chksum */		offset += len; 		if (len != CPIO_HDR_LEN ||		    push_buf(name, namesize) < 0 ||		    push_pad(namepadlen ? namepadlen : padlen(offset, 4)) < 0)			goto error; 		if (size) {			this_read = copy_file_range(file, NULL, outfd, NULL, size, 0);			if (this_read > 0) {				if (this_read > size)					goto error;				offset += this_read;				size -= this_read;			}			/* short or failed copy falls back to read/write... */		} 		while (size) {			unsigned char filebuf[65536];			size_t this_size = MIN(size, sizeof(filebuf)); 			this_read = read(file, filebuf, this_size);			if (this_read <= 0 || this_read > this_size) {				fprintf(stderr, "Can not read %s file\n", location);				goto error;			} 			if (write(outfd, filebuf, this_read) != this_read) {				fprintf(stderr, "writing filebuf failed\n");				goto error;			}			offset += this_read;			size -= this_read;		}		if (push_pad(padlen(offset, 4)) < 0)			goto error; 		name += namesize;	}	ino++;	rc = 0; error:	if (file >= 0)		close(file);	return rc;} 

Lines 1–500 of 781.