• Bug#1107203: unblock: initramfs-tools/0.148.1 (4/5)

    From Ben Hutchings@1:229/2 to All on Tue Jun 3 00:20:01 2025
    [continued from previous message]

    + return true;
    +
    + errno = orig_errno;
    + return false;
    +}
    +
    +/* write() with loop in case of partial writes */
    +static bool write_all(int fd, const void *buf, size_t len)
    +{
    + size_t pos;
    + ssize_t ret;
    +
    + pos = 0;
    + do {
    + ret = write(fd, (const char *)buf + pos, len - pos);
    + if (ret < 0)
    + return false;
    + pos += ret;
    + } while (pos < len);
    +
    + return true;
    +}
    +
    +/*
    + * Warn about failure of fread. This may be due to a file error
    + * reported in errno, or EOF which is not.
    + */
    +static void warn_after_fread_failure(FILE *file, const char *name)
    +{
    + if (ferror(file))
    + warn("%s", name);
    + else
    + warnx("%s: unexpected EOF", name);
    +}
    +
    +/*
    + * Parse one of the hexadecimal fields. Don't use strtoul() because
    + * it requires null-termination.
    + */
    +static bool cpio_parse_hex(const char *field, uint32_t *value_p)
    +{
    + const char digits[] = "0123456789ABCDEF", *p;
    + uint32_t value = 0;
    + unsigned int i;
    + bool found_digit = false;
    +
    + /* Skip leading spaces */
    + for (i = 0; i < 8 && field[i] == ' '; ++i)
    + ;
    +
    + /* Parse digits up to end of field or null */
    + for (; i < 8 && field[i] != 0; ++i) {
    + p = strchr(digits, field[i]);
    + if (!p)
    + return false;
    + value = (value << 4) | (p - digits);
    + found_digit = true;
    + }
    +
    + *value_p = value;
    + return found_digit;
    +}
    +
    +/* Align offset of file contents or header */
    +static off_t cpio_align(off_t off)
    +{
    + return (off + 3) & ~3;
    +}
    +
    +static struct cpio_handle *cpio_open(FILE *file, const char *name)
    +{
    + struct cpio_handle *cpio;
    +
    + cpio = calloc(1, sizeof(*cpio));
    + if (!cpio)
    + return NULL;
    +
    + cpio->file = file;
    + cpio->name = name;
    + cpio->next_off = ftell(file);
    + return cpio;
    +}
    +
    +/*
    + * Read next cpio header and name.
    + * Return:
    + * -1 on error
    + * 0 if entry is trailer
    + * 1 if entry is anything else
    + */
    +static int cpio_get_next(struct cpio_handle *cpio, struct cpio_entry *entry) +{
    + struct cpio_new header;
    + uint32_t file_size, name_size;
    +
    + if (fseek(cpio->file, cpio->next_off, SEEK_SET) < 0 ||
    + fread(&header, sizeof(header), 1, cpio->file) != 1) {
    + warn_after_fread_failure(cpio->file, cpio->name);
    + return -1;
    + }
    +
    + if ((memcmp(header.c_magic, CPIO_NEW_MAGIC, CPIO_MAGIC_LEN) != 0 &&
    + memcmp(header.c_magic, CPIO_NEW_CRC_MAGIC, CPIO_MAGIC_LEN) != 0) ||
    + !cpio_parse_hex(header.c_filesize, &file_size) ||
    + !cpio_parse_hex(header.c_namesize, &name_size)) {
    + warnx("%s: cpio archive has invalid header", cpio->name);
    + return -1;
    + }
    +
    + entry->name = cpio->name_buf;
    + entry->start = cpio->next_off;
    +
    + /* Calculate offset of the next header */
    + cpio->next_off = cpio_align(
    + cpio_align(cpio->next_off + sizeof(header) + name_size)
    + + file_size);
    + entry->end = cpio->next_off;
    +
    + if (name_size > sizeof(cpio->name_buf)) {
    + warnx("%s: cpio member name is too long", cpio->name);
    + return -1;
    + }
    +
    + if (fread(cpio->name_buf, name_size, 1, cpio->file) != 1) {
    + warn_after_fread_failure(cpio->file, cpio->name);
    + return -1;
    + }
    +
    + if (name_size == 0 || cpio->name_buf[name_size - 1] != 0) {
    + warnx("%s: cpio member name is invalid", cpio->name);
    + return -1;
    + }
    +
    + return strcmp(entry->name, CPIO_NAME_TRAILER) != 0;
    +}
    +
    +static void cpio_close(struct cpio_handle *cpio)
    +{
    + free(cpio);
    +}
    +
    +static bool detect_early_initramfs(FILE *in_file, const char *in_filename)
    +{
    + struct cpio_handle *cpio;
    + struct cpio_entry entry;
    + bool ret = false;
    + off_t start = ftell(in_file);
    +
    + cpio = cpio_open(in_file, in_filename);
    + if (!cpio)
    + return false;
    +
    + while (cpio_get_next(cpio, &entry) > 0) {
    + if (strncmp(entry.name, "kernel/", 7) == 0) {
    + ret = true;
    + break;
    + }
    + }
    +
    + cpio_close(cpio);
    +
    + fseek(in_file, start, SEEK_SET);
    + return ret;
    +}
    +
    +static bool copy_to_pipe(FILE *in_file, const char *in_filename,
    + off_t start, off_t end, int out_pipe)
    +{
    + char buf[0x10000];
    + off_t in_pos;
    + size_t want_len, read_len;
    +
    + /* Set input position */
    + fseek(in_file, start, SEEK_SET);
    + in_pos = start;
    +
    + while (in_pos < end) {
    + /* How much do we want to copy? */
    + want_len = sizeof(buf);
    + if ((ssize_t)want_len > end - in_pos)
    + want_len = end - in_pos;
    +
    + /* Read to buffer; update input position */
    + read_len = fread(buf, 1, want_len, in_file);
    + if (!read_len) {
    + warn_after_fread_failure(in_file, in_filename);
    + return false;
    + }
    + in_pos += read_len;
    +
    + /* Write to pipe */
    + if (!write_all(out_pipe, buf, read_len)) {
    + warn("pipe write");
    + return false;
    + }
    + }
    +
    + return true;
    +}
    +
    +static bool handle_uncompressed(FILE *in_file, const char *in_filename,
    + int out_pipe)
    +{
    + struct cpio_handle *cpio;
    + struct cpio_entry entry;
    + uint32_t pad;
    + int ret;
    +
    + cpio = cpio_open(in_file, in_filename);
    + if (!cpio)
    + return false;
    +
    + while ((ret = cpio_get_next(cpio, &entry)) > 0) {
    + if (!copy_to_pipe(in_file, in_filename, entry.start, entry.end, + out_pipe)) {
    + ret = -1;
    + break;
    + }
    + }
    +
    + cpio_close(cpio);
    +
    + if (ret < 0)
    + return false;
    +
    + /* Skip trailer and any zero padidng */
    + fseek(in_file, entry.end, SEEK_SET);
    + while (fread(&pad, sizeof(pad), 1, in_file)) {
    + if (pad != 0) {
    + fseek(in_file, -sizeof(pad), SEEK_CUR);
    + break;
    + }
    + }
    +
    + return true;
    +}
    +
    +static bool handle_compressed(FILE *in_file, enum format format, int out_pipe) +{
    + const char *const *argv = decomp_table[format];
    + int in_fd = fileno(in_file);
    + off_t in_pos = ftell(in_file);
    + int pid, wstatus;
    +
    + pid = fork();
    + if (pid < 0)
    + return false;
    +
    + /* Child */
    + if (pid == 0) {
    + /*
    + * Make in_file stdin. Reset the position of the file
    + * descriptor because stdio will have read-ahead from
    + * the position it reported.
    + */
    + dup2(in_fd, 0);
    + close(in_fd);
    + lseek(0, in_pos, SEEK_SET);
    +
    + /* Make out_pipe stdout */
    + dup2(out_pipe, 1);
    + close(out_pipe);
    +
    + execlp(argv[0], argv[0], argv[1], NULL);
    + _exit(127);
    + }
    +
    + /* Parent: wait for child */
    + if (waitpid(pid, &wstatus, 0) != pid ||
    + !WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) {
    + warnx("%s failed", argv[0]);
    + return false;
    + }
    + return true;
    +}
    +
    +static bool write_trailer(int out_pipe)
    +{
    + struct {
    + struct cpio_new header;
    + char name[sizeof(CPIO_NAME_TRAILER)];
    + char pad[-(sizeof(struct cpio_new) + sizeof(CPIO_NAME_TRAILER)) + & 3];
    + } __attribute__((packed)) trailer;
    + char name_size[8 + 1];
    +
    + static_assert((sizeof(trailer) & 3) == 0, "pad miscalculated");
    +
    + memset(&trailer.header, '0', sizeof(trailer.header));
    + memcpy(trailer.header.c_magic, CPIO_NEW_MAGIC, CPIO_MAGIC_LEN);
    + sprintf(name_size, "%08zX", sizeof(CPIO_NAME_TRAILER));
    + memcpy(trailer.header.c_namesize, name_size,
    + sizeof(trailer.header.c_namesize));
    +
    + strcpy(trailer.name, CPIO_NAME_TRAILER);
    +
    + memset(&trailer.pad, 0, sizeof(trailer.pad));
    +
    + if (!write_all(out_pipe, &trailer, sizeof(trailer))) {
    + warn("pipe write");
    + return false;
    + }
    +
    + return true;
    +}
    +
    +static bool spawn_cpio(int optc, const char **optv, struct cpio_proc *proc)
    +{
    + const char *argv[10];
    + int pipe_fds[2], pid;
    + size_t argc;
    +
    + /* Combine base cpio command with extra options */
    + argc = 0;
    + argv[argc++] = "cpio";
    + argv[argc++] = "-i";
    + argv[argc++] = "--preserve-modification-time";
    + argv[argc++] = "--no-absolute-filenames";
    + argv[argc++] = "--quiet";
    + assert(argc + optc < sizeof(argv) / sizeof(argv[0]));
    + while (optc--)
    + argv[argc++] = *optv++;

    [continued in next message]

    --- SoupGate-Win32 v1.05
    * Origin: you cannot sedate... all the things you hate (1:229/2)