mirror of
https://github.com/torvalds/linux.git
synced 2025-11-30 23:16:01 +07:00
As described in buffer-format.rst, the existing initramfs.c extraction
logic works fine if the cpio filename field is padded out with trailing
zeros, with a caveat that the padded namesize can't exceed PATH_MAX.
Add filename zero-padding logic to gen_init_cpio, which can be triggered
via the new -a <data_align> parameter. Performance and storage
utilization is improved for Btrfs and XFS workloads, as copy_file_range
can reflink the entire source file into a filesystem block-size aligned
destination offset within the cpio archive.
Btrfs benchmarks run on 6.15.8-1-default (Tumbleweed) x86_64 host:
> truncate --size=2G /tmp/backing.img
> /sbin/mkfs.btrfs /tmp/backing.img
...
Sector size: 4096 (CPU page size: 4096)
...
> sudo mount /tmp/backing.img mnt
> sudo chown $USER mnt
> cd mnt
mnt> dd if=/dev/urandom of=foo bs=1M count=20 && cat foo >/dev/null
...
mnt> echo "file /foo foo 0755 0 0" > list
mnt> perf stat -r 10 gen_init_cpio -o unaligned_btrfs list
...
0.023496 +- 0.000472 seconds time elapsed ( +- 2.01% )
mnt> perf stat -r 10 gen_init_cpio -o aligned_btrfs -a 4096 list
...
0.0010010 +- 0.0000565 seconds time elapsed ( +- 5.65% )
mnt> /sbin/xfs_io -c "fiemap -v" unaligned_btrfs
unaligned_btrfs:
EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS
0: [0..40967]: 695040..736007 40968 0x1
mnt> /sbin/xfs_io -c "fiemap -v" aligned_btrfs
aligned_btrfs:
EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS
0: [0..7]: 26768..26775 8 0x0
1: [8..40967]: 269056..310015 40960 0x2000
2: [40968..40975]: 26776..26783 8 0x1
mnt> /sbin/btrfs fi du unaligned_btrfs aligned_btrfs
Total Exclusive Set shared Filename
20.00MiB 20.00MiB 0.00B unaligned_btrfs
20.01MiB 8.00KiB 20.00MiB aligned_btrfs
XFS benchmarks run on same host:
> sudo umount mnt && rm /tmp/backing.img
> truncate --size=2G /tmp/backing.img
> /sbin/mkfs.xfs /tmp/backing.img
...
= reflink=1 ...
data = bsize=4096 blocks=524288, imaxpct=25
...
> sudo mount /tmp/backing.img mnt
> sudo chown $USER mnt
> cd mnt
mnt> dd if=/dev/urandom of=foo bs=1M count=20 && cat foo >/dev/null
...
mnt> echo "file /foo foo 0755 0 0" > list
mnt> perf stat -r 10 gen_init_cpio -o unaligned_xfs list
...
0.011069 +- 0.000469 seconds time elapsed ( +- 4.24% )
mnt> perf stat -r 10 gen_init_cpio -o aligned_xfs -a 4096 list
...
0.001273 +- 0.000288 seconds time elapsed ( +- 22.60% )
mnt> /sbin/xfs_io -c "fiemap -v" unaligned_xfs
unaligned_xfs:
EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS
0: [0..40967]: 106176..147143 40968 0x0
1: [40968..65023]: 147144..171199 24056 0x801
mnt> /sbin/xfs_io -c "fiemap -v" aligned_xfs
aligned_xfs:
EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS
0: [0..7]: 120..127 8 0x0
1: [8..40967]: 192..41151 40960 0x2000
2: [40968..40975]: 236728..236735 8 0x0
3: [40976..106495]: 236736..302255 65520 0x801
The alignment is best-effort; a stderr message is printed if alignment
can't be achieved due to PATH_MAX overrun, with fallback to non-padded
filename. This allows it to still be useful for opportunistic alignment,
e.g. on aarch64 Btrfs with 64K block-size. Alignment failure messages
provide an indicator that reordering of the cpio-manifest may be
beneficial.
Archive read performance for reflinked initramfs images may suffer due
to the effects of fragmentation, particularly on spinning disks. To
mitigate excessive fragmentation, files with lengths less than
data_align aren't padded.
Signed-off-by: David Disseldorp <ddiss@suse.de>
Reviewed-by: Nicolas Schier <nsc@kernel.org>
Link: https://lore.kernel.org/r/20250819032607.28727-8-ddiss@suse.de
Signed-off-by: Nathan Chancellor <nathan@kernel.org>
779 lines
17 KiB
C
779 lines
17 KiB
C
// 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;
|
|
|
|
return fsync(outfd);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static char *cpio_replace_env(char *new_location)
|
|
{
|
|
char expanded[PATH_MAX + 1];
|
|
char *start, *end, *var;
|
|
|
|
while ((start = strstr(new_location, "${")) &&
|
|
(end = strchr(start + 2, '}'))) {
|
|
*start = *end = 0;
|
|
var = getenv(start + 2);
|
|
snprintf(expanded, sizeof expanded, "%s%s%s",
|
|
new_location, var ? var : "", end + 1);
|
|
strcpy(new_location, expanded);
|
|
}
|
|
|
|
return new_location;
|
|
}
|
|
|
|
static int cpio_mkfile_line(const char *line)
|
|
{
|
|
char name[PATH_MAX + 1];
|
|
char *dname = NULL; /* malloc'ed buffer for hard links */
|
|
char location[PATH_MAX + 1];
|
|
unsigned int mode;
|
|
int uid;
|
|
int gid;
|
|
int nlinks = 1;
|
|
int end = 0, dname_len = 0;
|
|
int rc = -1;
|
|
|
|
if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
|
|
"s %o %d %d %n",
|
|
name, location, &mode, &uid, &gid, &end)) {
|
|
fprintf(stderr, "Unrecognized file format '%s'", line);
|
|
goto fail;
|
|
}
|
|
if (end && isgraph(line[end])) {
|
|
int len;
|
|
int nend;
|
|
|
|
dname = malloc(strlen(line));
|
|
if (!dname) {
|
|
fprintf (stderr, "out of memory (%d)\n", dname_len);
|
|
goto fail;
|
|
}
|
|
|
|
dname_len = strlen(name) + 1;
|
|
memcpy(dname, name, dname_len);
|
|
|
|
do {
|
|
nend = 0;
|
|
if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
|
|
name, &nend) < 1)
|
|
break;
|
|
len = strlen(name) + 1;
|
|
memcpy(dname + dname_len, name, len);
|
|
dname_len += len;
|
|
nlinks++;
|
|
end += nend;
|
|
} while (isgraph(line[end]));
|
|
} else {
|
|
dname = name;
|
|
}
|
|
rc = cpio_mkfile(dname, cpio_replace_env(location),
|
|
mode, uid, gid, nlinks);
|
|
fail:
|
|
if (dname_len) free(dname);
|
|
return rc;
|
|
}
|
|
|
|
static void usage(const char *prog)
|
|
{
|
|
fprintf(stderr, "Usage:\n"
|
|
"\t%s [-t <timestamp>] [-c] [-o <output_file>] [-a <data_align>] <cpio_list>\n"
|
|
"\n"
|
|
"<cpio_list> is a file containing newline separated entries that\n"
|
|
"describe the files to be included in the initramfs archive:\n"
|
|
"\n"
|
|
"# a comment\n"
|
|
"file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
|
|
"dir <name> <mode> <uid> <gid>\n"
|
|
"nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
|
|
"slink <name> <target> <mode> <uid> <gid>\n"
|
|
"pipe <name> <mode> <uid> <gid>\n"
|
|
"sock <name> <mode> <uid> <gid>\n"
|
|
"\n"
|
|
"<name> name of the file/dir/nod/etc in the archive\n"
|
|
"<location> location of the file in the current filesystem\n"
|
|
" expands shell variables quoted with ${}\n"
|
|
"<target> link target\n"
|
|
"<mode> mode/permissions of the file\n"
|
|
"<uid> user id (0=root)\n"
|
|
"<gid> group id (0=root)\n"
|
|
"<dev_type> device type (b=block, c=character)\n"
|
|
"<maj> major number of nod\n"
|
|
"<min> minor number of nod\n"
|
|
"<hard links> space separated list of other links to file\n"
|
|
"\n"
|
|
"example:\n"
|
|
"# A simple initramfs\n"
|
|
"dir /dev 0755 0 0\n"
|
|
"nod /dev/console 0600 0 0 c 5 1\n"
|
|
"dir /root 0700 0 0\n"
|
|
"dir /sbin 0755 0 0\n"
|
|
"file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n"
|
|
"\n"
|
|
"<timestamp> is time in seconds since Epoch that will be used\n"
|
|
"as mtime for symlinks, directories, regular and special files.\n"
|
|
"The default is to use the current time for all files, but\n"
|
|
"preserve modification time for regular files.\n"
|
|
"-c: calculate and store 32-bit checksums for file data.\n"
|
|
"<output_file>: write cpio to this file instead of stdout\n"
|
|
"<data_align>: attempt to align file data by zero-padding the\n"
|
|
"filename field up to data_align. Must be a multiple of 4.\n"
|
|
"Alignment is best-effort; PATH_MAX limits filename padding.\n",
|
|
prog);
|
|
}
|
|
|
|
static const struct file_handler file_handler_table[] = {
|
|
{
|
|
.type = "file",
|
|
.handler = cpio_mkfile_line,
|
|
}, {
|
|
.type = "nod",
|
|
.handler = cpio_mknod_line,
|
|
}, {
|
|
.type = "dir",
|
|
.handler = cpio_mkdir_line,
|
|
}, {
|
|
.type = "slink",
|
|
.handler = cpio_mkslink_line,
|
|
}, {
|
|
.type = "pipe",
|
|
.handler = cpio_mkpipe_line,
|
|
}, {
|
|
.type = "sock",
|
|
.handler = cpio_mksock_line,
|
|
}, {
|
|
.type = NULL,
|
|
.handler = NULL,
|
|
}
|
|
};
|
|
|
|
#define LINE_SIZE (2 * PATH_MAX + 50)
|
|
|
|
int main (int argc, char *argv[])
|
|
{
|
|
FILE *cpio_list;
|
|
char line[LINE_SIZE];
|
|
char *args, *type;
|
|
int ec = 0;
|
|
int line_nr = 0;
|
|
const char *filename;
|
|
|
|
default_mtime = time(NULL);
|
|
while (1) {
|
|
int opt = getopt(argc, argv, "t:cho:a:");
|
|
char *invalid;
|
|
|
|
if (opt == -1)
|
|
break;
|
|
switch (opt) {
|
|
case 't':
|
|
default_mtime = strtol(optarg, &invalid, 10);
|
|
if (!*optarg || *invalid) {
|
|
fprintf(stderr, "Invalid timestamp: %s\n",
|
|
optarg);
|
|
usage(argv[0]);
|
|
exit(1);
|
|
}
|
|
do_file_mtime = true;
|
|
break;
|
|
case 'c':
|
|
do_csum = true;
|
|
break;
|
|
case 'o':
|
|
outfd = open(optarg,
|
|
O_WRONLY | O_CREAT | O_LARGEFILE | O_TRUNC,
|
|
0600);
|
|
if (outfd < 0) {
|
|
fprintf(stderr, "failed to open %s\n", optarg);
|
|
usage(argv[0]);
|
|
exit(1);
|
|
}
|
|
break;
|
|
case 'a':
|
|
dalign = strtoul(optarg, &invalid, 10);
|
|
if (!*optarg || *invalid || (dalign & 3)) {
|
|
fprintf(stderr, "Invalid data_align: %s\n",
|
|
optarg);
|
|
usage(argv[0]);
|
|
exit(1);
|
|
}
|
|
break;
|
|
case 'h':
|
|
case '?':
|
|
usage(argv[0]);
|
|
exit(opt == 'h' ? 0 : 1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Timestamps after 2106-02-07 06:28:15 UTC have an ascii hex time_t
|
|
* representation that exceeds 8 chars and breaks the cpio header
|
|
* specification. Negative timestamps similarly exceed 8 chars.
|
|
*/
|
|
if (default_mtime > 0xffffffff || default_mtime < 0) {
|
|
fprintf(stderr, "ERROR: Timestamp out of range for cpio format\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (argc - optind != 1) {
|
|
usage(argv[0]);
|
|
exit(1);
|
|
}
|
|
filename = argv[optind];
|
|
if (!strcmp(filename, "-"))
|
|
cpio_list = stdin;
|
|
else if (!(cpio_list = fopen(filename, "r"))) {
|
|
fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
|
|
filename, strerror(errno));
|
|
usage(argv[0]);
|
|
exit(1);
|
|
}
|
|
|
|
while (fgets(line, LINE_SIZE, cpio_list)) {
|
|
int type_idx;
|
|
size_t slen = strlen(line);
|
|
|
|
line_nr++;
|
|
|
|
if ('#' == *line) {
|
|
/* comment - skip to next line */
|
|
continue;
|
|
}
|
|
|
|
if (! (type = strtok(line, " \t"))) {
|
|
fprintf(stderr,
|
|
"ERROR: incorrect format, could not locate file type line %d: '%s'\n",
|
|
line_nr, line);
|
|
ec = -1;
|
|
break;
|
|
}
|
|
|
|
if ('\n' == *type) {
|
|
/* a blank line */
|
|
continue;
|
|
}
|
|
|
|
if (slen == strlen(type)) {
|
|
/* must be an empty line */
|
|
continue;
|
|
}
|
|
|
|
if (! (args = strtok(NULL, "\n"))) {
|
|
fprintf(stderr,
|
|
"ERROR: incorrect format, newline required line %d: '%s'\n",
|
|
line_nr, line);
|
|
ec = -1;
|
|
}
|
|
|
|
for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
|
|
int rc;
|
|
if (! strcmp(line, file_handler_table[type_idx].type)) {
|
|
if ((rc = file_handler_table[type_idx].handler(args))) {
|
|
ec = rc;
|
|
fprintf(stderr, " line %d\n", line_nr);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (NULL == file_handler_table[type_idx].type) {
|
|
fprintf(stderr, "unknown file type line %d: '%s'\n",
|
|
line_nr, line);
|
|
}
|
|
}
|
|
if (ec == 0)
|
|
ec = cpio_trailer();
|
|
|
|
exit(ec);
|
|
}
|