// 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