linux/usr/gen_init_cpio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <stdio.h>
   3#include <stdlib.h>
   4#include <sys/types.h>
   5#include <sys/stat.h>
   6#include <string.h>
   7#include <unistd.h>
   8#include <time.h>
   9#include <fcntl.h>
  10#include <errno.h>
  11#include <ctype.h>
  12#include <limits.h>
  13
  14/*
  15 * Original work by Jeff Garzik
  16 *
  17 * External file lists, symlink, pipe and fifo support by Thayne Harbaugh
  18 * Hard link support by Luciano Rocha
  19 */
  20
  21#define xstr(s) #s
  22#define str(s) xstr(s)
  23
  24static unsigned int offset;
  25static unsigned int ino = 721;
  26static time_t default_mtime;
  27
  28struct file_handler {
  29        const char *type;
  30        int (*handler)(const char *line);
  31};
  32
  33static void push_string(const char *name)
  34{
  35        unsigned int name_len = strlen(name) + 1;
  36
  37        fputs(name, stdout);
  38        putchar(0);
  39        offset += name_len;
  40}
  41
  42static void push_pad (void)
  43{
  44        while (offset & 3) {
  45                putchar(0);
  46                offset++;
  47        }
  48}
  49
  50static void push_rest(const char *name)
  51{
  52        unsigned int name_len = strlen(name) + 1;
  53        unsigned int tmp_ofs;
  54
  55        fputs(name, stdout);
  56        putchar(0);
  57        offset += name_len;
  58
  59        tmp_ofs = name_len + 110;
  60        while (tmp_ofs & 3) {
  61                putchar(0);
  62                offset++;
  63                tmp_ofs++;
  64        }
  65}
  66
  67static void push_hdr(const char *s)
  68{
  69        fputs(s, stdout);
  70        offset += 110;
  71}
  72
  73static void cpio_trailer(void)
  74{
  75        char s[256];
  76        const char name[] = "TRAILER!!!";
  77
  78        sprintf(s, "%s%08X%08X%08lX%08lX%08X%08lX"
  79               "%08X%08X%08X%08X%08X%08X%08X",
  80                "070701",               /* magic */
  81                0,                      /* ino */
  82                0,                      /* mode */
  83                (long) 0,               /* uid */
  84                (long) 0,               /* gid */
  85                1,                      /* nlink */
  86                (long) 0,               /* mtime */
  87                0,                      /* filesize */
  88                0,                      /* major */
  89                0,                      /* minor */
  90                0,                      /* rmajor */
  91                0,                      /* rminor */
  92                (unsigned)strlen(name)+1, /* namesize */
  93                0);                     /* chksum */
  94        push_hdr(s);
  95        push_rest(name);
  96
  97        while (offset % 512) {
  98                putchar(0);
  99                offset++;
 100        }
 101}
 102
 103static int cpio_mkslink(const char *name, const char *target,
 104                         unsigned int mode, uid_t uid, gid_t gid)
 105{
 106        char s[256];
 107
 108        if (name[0] == '/')
 109                name++;
 110        sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
 111               "%08X%08X%08X%08X%08X%08X%08X",
 112                "070701",               /* magic */
 113                ino++,                  /* ino */
 114                S_IFLNK | mode,         /* mode */
 115                (long) uid,             /* uid */
 116                (long) gid,             /* gid */
 117                1,                      /* nlink */
 118                (long) default_mtime,   /* mtime */
 119                (unsigned)strlen(target)+1, /* filesize */
 120                3,                      /* major */
 121                1,                      /* minor */
 122                0,                      /* rmajor */
 123                0,                      /* rminor */
 124                (unsigned)strlen(name) + 1,/* namesize */
 125                0);                     /* chksum */
 126        push_hdr(s);
 127        push_string(name);
 128        push_pad();
 129        push_string(target);
 130        push_pad();
 131        return 0;
 132}
 133
 134static int cpio_mkslink_line(const char *line)
 135{
 136        char name[PATH_MAX + 1];
 137        char target[PATH_MAX + 1];
 138        unsigned int mode;
 139        int uid;
 140        int gid;
 141        int rc = -1;
 142
 143        if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) {
 144                fprintf(stderr, "Unrecognized dir format '%s'", line);
 145                goto fail;
 146        }
 147        rc = cpio_mkslink(name, target, mode, uid, gid);
 148 fail:
 149        return rc;
 150}
 151
 152static int cpio_mkgeneric(const char *name, unsigned int mode,
 153                       uid_t uid, gid_t gid)
 154{
 155        char s[256];
 156
 157        if (name[0] == '/')
 158                name++;
 159        sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
 160               "%08X%08X%08X%08X%08X%08X%08X",
 161                "070701",               /* magic */
 162                ino++,                  /* ino */
 163                mode,                   /* mode */
 164                (long) uid,             /* uid */
 165                (long) gid,             /* gid */
 166                2,                      /* nlink */
 167                (long) default_mtime,   /* mtime */
 168                0,                      /* filesize */
 169                3,                      /* major */
 170                1,                      /* minor */
 171                0,                      /* rmajor */
 172                0,                      /* rminor */
 173                (unsigned)strlen(name) + 1,/* namesize */
 174                0);                     /* chksum */
 175        push_hdr(s);
 176        push_rest(name);
 177        return 0;
 178}
 179
 180enum generic_types {
 181        GT_DIR,
 182        GT_PIPE,
 183        GT_SOCK
 184};
 185
 186struct generic_type {
 187        const char *type;
 188        mode_t mode;
 189};
 190
 191static const struct generic_type generic_type_table[] = {
 192        [GT_DIR] = {
 193                .type = "dir",
 194                .mode = S_IFDIR
 195        },
 196        [GT_PIPE] = {
 197                .type = "pipe",
 198                .mode = S_IFIFO
 199        },
 200        [GT_SOCK] = {
 201                .type = "sock",
 202                .mode = S_IFSOCK
 203        }
 204};
 205
 206static int cpio_mkgeneric_line(const char *line, enum generic_types gt)
 207{
 208        char name[PATH_MAX + 1];
 209        unsigned int mode;
 210        int uid;
 211        int gid;
 212        int rc = -1;
 213
 214        if (4 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d", name, &mode, &uid, &gid)) {
 215                fprintf(stderr, "Unrecognized %s format '%s'",
 216                        line, generic_type_table[gt].type);
 217                goto fail;
 218        }
 219        mode |= generic_type_table[gt].mode;
 220        rc = cpio_mkgeneric(name, mode, uid, gid);
 221 fail:
 222        return rc;
 223}
 224
 225static int cpio_mkdir_line(const char *line)
 226{
 227        return cpio_mkgeneric_line(line, GT_DIR);
 228}
 229
 230static int cpio_mkpipe_line(const char *line)
 231{
 232        return cpio_mkgeneric_line(line, GT_PIPE);
 233}
 234
 235static int cpio_mksock_line(const char *line)
 236{
 237        return cpio_mkgeneric_line(line, GT_SOCK);
 238}
 239
 240static int cpio_mknod(const char *name, unsigned int mode,
 241                       uid_t uid, gid_t gid, char dev_type,
 242                       unsigned int maj, unsigned int min)
 243{
 244        char s[256];
 245
 246        if (dev_type == 'b')
 247                mode |= S_IFBLK;
 248        else
 249                mode |= S_IFCHR;
 250
 251        if (name[0] == '/')
 252                name++;
 253        sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
 254               "%08X%08X%08X%08X%08X%08X%08X",
 255                "070701",               /* magic */
 256                ino++,                  /* ino */
 257                mode,                   /* mode */
 258                (long) uid,             /* uid */
 259                (long) gid,             /* gid */
 260                1,                      /* nlink */
 261                (long) default_mtime,   /* mtime */
 262                0,                      /* filesize */
 263                3,                      /* major */
 264                1,                      /* minor */
 265                maj,                    /* rmajor */
 266                min,                    /* rminor */
 267                (unsigned)strlen(name) + 1,/* namesize */
 268                0);                     /* chksum */
 269        push_hdr(s);
 270        push_rest(name);
 271        return 0;
 272}
 273
 274static int cpio_mknod_line(const char *line)
 275{
 276        char name[PATH_MAX + 1];
 277        unsigned int mode;
 278        int uid;
 279        int gid;
 280        char dev_type;
 281        unsigned int maj;
 282        unsigned int min;
 283        int rc = -1;
 284
 285        if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",
 286                         name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
 287                fprintf(stderr, "Unrecognized nod format '%s'", line);
 288                goto fail;
 289        }
 290        rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
 291 fail:
 292        return rc;
 293}
 294
 295static int cpio_mkfile(const char *name, const char *location,
 296                        unsigned int mode, uid_t uid, gid_t gid,
 297                        unsigned int nlinks)
 298{
 299        char s[256];
 300        char *filebuf = NULL;
 301        struct stat buf;
 302        long size;
 303        int file = -1;
 304        int retval;
 305        int rc = -1;
 306        int namesize;
 307        unsigned int i;
 308
 309        mode |= S_IFREG;
 310
 311        file = open (location, O_RDONLY);
 312        if (file < 0) {
 313                fprintf (stderr, "File %s could not be opened for reading\n", location);
 314                goto error;
 315        }
 316
 317        retval = fstat(file, &buf);
 318        if (retval) {
 319                fprintf(stderr, "File %s could not be stat()'ed\n", location);
 320                goto error;
 321        }
 322
 323        if (buf.st_mtime > 0xffffffff) {
 324                fprintf(stderr, "%s: Timestamp exceeds maximum cpio timestamp, clipping.\n",
 325                        location);
 326                buf.st_mtime = 0xffffffff;
 327        }
 328
 329        filebuf = malloc(buf.st_size);
 330        if (!filebuf) {
 331                fprintf (stderr, "out of memory\n");
 332                goto error;
 333        }
 334
 335        retval = read (file, filebuf, buf.st_size);
 336        if (retval < 0) {
 337                fprintf (stderr, "Can not read %s file\n", location);
 338                goto error;
 339        }
 340
 341        size = 0;
 342        for (i = 1; i <= nlinks; i++) {
 343                /* data goes on last link */
 344                if (i == nlinks) size = buf.st_size;
 345
 346                if (name[0] == '/')
 347                        name++;
 348                namesize = strlen(name) + 1;
 349                sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
 350                       "%08lX%08X%08X%08X%08X%08X%08X",
 351                        "070701",               /* magic */
 352                        ino,                    /* ino */
 353                        mode,                   /* mode */
 354                        (long) uid,             /* uid */
 355                        (long) gid,             /* gid */
 356                        nlinks,                 /* nlink */
 357                        (long) buf.st_mtime,    /* mtime */
 358                        size,                   /* filesize */
 359                        3,                      /* major */
 360                        1,                      /* minor */
 361                        0,                      /* rmajor */
 362                        0,                      /* rminor */
 363                        namesize,               /* namesize */
 364                        0);                     /* chksum */
 365                push_hdr(s);
 366                push_string(name);
 367                push_pad();
 368
 369                if (size) {
 370                        if (fwrite(filebuf, size, 1, stdout) != 1) {
 371                                fprintf(stderr, "writing filebuf failed\n");
 372                                goto error;
 373                        }
 374                        offset += size;
 375                        push_pad();
 376                }
 377
 378                name += namesize;
 379        }
 380        ino++;
 381        rc = 0;
 382        
 383error:
 384        if (filebuf) free(filebuf);
 385        if (file >= 0) close(file);
 386        return rc;
 387}
 388
 389static char *cpio_replace_env(char *new_location)
 390{
 391        char expanded[PATH_MAX + 1];
 392        char *start, *end, *var;
 393
 394        while ((start = strstr(new_location, "${")) &&
 395               (end = strchr(start + 2, '}'))) {
 396                *start = *end = 0;
 397                var = getenv(start + 2);
 398                snprintf(expanded, sizeof expanded, "%s%s%s",
 399                         new_location, var ? var : "", end + 1);
 400                strcpy(new_location, expanded);
 401        }
 402
 403        return new_location;
 404}
 405
 406static int cpio_mkfile_line(const char *line)
 407{
 408        char name[PATH_MAX + 1];
 409        char *dname = NULL; /* malloc'ed buffer for hard links */
 410        char location[PATH_MAX + 1];
 411        unsigned int mode;
 412        int uid;
 413        int gid;
 414        int nlinks = 1;
 415        int end = 0, dname_len = 0;
 416        int rc = -1;
 417
 418        if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
 419                                "s %o %d %d %n",
 420                                name, location, &mode, &uid, &gid, &end)) {
 421                fprintf(stderr, "Unrecognized file format '%s'", line);
 422                goto fail;
 423        }
 424        if (end && isgraph(line[end])) {
 425                int len;
 426                int nend;
 427
 428                dname = malloc(strlen(line));
 429                if (!dname) {
 430                        fprintf (stderr, "out of memory (%d)\n", dname_len);
 431                        goto fail;
 432                }
 433
 434                dname_len = strlen(name) + 1;
 435                memcpy(dname, name, dname_len);
 436
 437                do {
 438                        nend = 0;
 439                        if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
 440                                        name, &nend) < 1)
 441                                break;
 442                        len = strlen(name) + 1;
 443                        memcpy(dname + dname_len, name, len);
 444                        dname_len += len;
 445                        nlinks++;
 446                        end += nend;
 447                } while (isgraph(line[end]));
 448        } else {
 449                dname = name;
 450        }
 451        rc = cpio_mkfile(dname, cpio_replace_env(location),
 452                         mode, uid, gid, nlinks);
 453 fail:
 454        if (dname_len) free(dname);
 455        return rc;
 456}
 457
 458static void usage(const char *prog)
 459{
 460        fprintf(stderr, "Usage:\n"
 461                "\t%s [-t <timestamp>] <cpio_list>\n"
 462                "\n"
 463                "<cpio_list> is a file containing newline separated entries that\n"
 464                "describe the files to be included in the initramfs archive:\n"
 465                "\n"
 466                "# a comment\n"
 467                "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
 468                "dir <name> <mode> <uid> <gid>\n"
 469                "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
 470                "slink <name> <target> <mode> <uid> <gid>\n"
 471                "pipe <name> <mode> <uid> <gid>\n"
 472                "sock <name> <mode> <uid> <gid>\n"
 473                "\n"
 474                "<name>       name of the file/dir/nod/etc in the archive\n"
 475                "<location>   location of the file in the current filesystem\n"
 476                "             expands shell variables quoted with ${}\n"
 477                "<target>     link target\n"
 478                "<mode>       mode/permissions of the file\n"
 479                "<uid>        user id (0=root)\n"
 480                "<gid>        group id (0=root)\n"
 481                "<dev_type>   device type (b=block, c=character)\n"
 482                "<maj>        major number of nod\n"
 483                "<min>        minor number of nod\n"
 484                "<hard links> space separated list of other links to file\n"
 485                "\n"
 486                "example:\n"
 487                "# A simple initramfs\n"
 488                "dir /dev 0755 0 0\n"
 489                "nod /dev/console 0600 0 0 c 5 1\n"
 490                "dir /root 0700 0 0\n"
 491                "dir /sbin 0755 0 0\n"
 492                "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n"
 493                "\n"
 494                "<timestamp> is time in seconds since Epoch that will be used\n"
 495                "as mtime for symlinks, special files and directories. The default\n"
 496                "is to use the current time for these entries.\n",
 497                prog);
 498}
 499
 500static const struct file_handler file_handler_table[] = {
 501        {
 502                .type    = "file",
 503                .handler = cpio_mkfile_line,
 504        }, {
 505                .type    = "nod",
 506                .handler = cpio_mknod_line,
 507        }, {
 508                .type    = "dir",
 509                .handler = cpio_mkdir_line,
 510        }, {
 511                .type    = "slink",
 512                .handler = cpio_mkslink_line,
 513        }, {
 514                .type    = "pipe",
 515                .handler = cpio_mkpipe_line,
 516        }, {
 517                .type    = "sock",
 518                .handler = cpio_mksock_line,
 519        }, {
 520                .type    = NULL,
 521                .handler = NULL,
 522        }
 523};
 524
 525#define LINE_SIZE (2 * PATH_MAX + 50)
 526
 527int main (int argc, char *argv[])
 528{
 529        FILE *cpio_list;
 530        char line[LINE_SIZE];
 531        char *args, *type;
 532        int ec = 0;
 533        int line_nr = 0;
 534        const char *filename;
 535
 536        default_mtime = time(NULL);
 537        while (1) {
 538                int opt = getopt(argc, argv, "t:h");
 539                char *invalid;
 540
 541                if (opt == -1)
 542                        break;
 543                switch (opt) {
 544                case 't':
 545                        default_mtime = strtol(optarg, &invalid, 10);
 546                        if (!*optarg || *invalid) {
 547                                fprintf(stderr, "Invalid timestamp: %s\n",
 548                                                optarg);
 549                                usage(argv[0]);
 550                                exit(1);
 551                        }
 552                        break;
 553                case 'h':
 554                case '?':
 555                        usage(argv[0]);
 556                        exit(opt == 'h' ? 0 : 1);
 557                }
 558        }
 559
 560        /*
 561         * Timestamps after 2106-02-07 06:28:15 UTC have an ascii hex time_t
 562         * representation that exceeds 8 chars and breaks the cpio header
 563         * specification.
 564         */
 565        if (default_mtime > 0xffffffff) {
 566                fprintf(stderr, "ERROR: Timestamp too large for cpio format\n");
 567                exit(1);
 568        }
 569
 570        if (argc - optind != 1) {
 571                usage(argv[0]);
 572                exit(1);
 573        }
 574        filename = argv[optind];
 575        if (!strcmp(filename, "-"))
 576                cpio_list = stdin;
 577        else if (!(cpio_list = fopen(filename, "r"))) {
 578                fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
 579                        filename, strerror(errno));
 580                usage(argv[0]);
 581                exit(1);
 582        }
 583
 584        while (fgets(line, LINE_SIZE, cpio_list)) {
 585                int type_idx;
 586                size_t slen = strlen(line);
 587
 588                line_nr++;
 589
 590                if ('#' == *line) {
 591                        /* comment - skip to next line */
 592                        continue;
 593                }
 594
 595                if (! (type = strtok(line, " \t"))) {
 596                        fprintf(stderr,
 597                                "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
 598                                line_nr, line);
 599                        ec = -1;
 600                        break;
 601                }
 602
 603                if ('\n' == *type) {
 604                        /* a blank line */
 605                        continue;
 606                }
 607
 608                if (slen == strlen(type)) {
 609                        /* must be an empty line */
 610                        continue;
 611                }
 612
 613                if (! (args = strtok(NULL, "\n"))) {
 614                        fprintf(stderr,
 615                                "ERROR: incorrect format, newline required line %d: '%s'\n",
 616                                line_nr, line);
 617                        ec = -1;
 618                }
 619
 620                for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
 621                        int rc;
 622                        if (! strcmp(line, file_handler_table[type_idx].type)) {
 623                                if ((rc = file_handler_table[type_idx].handler(args))) {
 624                                        ec = rc;
 625                                        fprintf(stderr, " line %d\n", line_nr);
 626                                }
 627                                break;
 628                        }
 629                }
 630
 631                if (NULL == file_handler_table[type_idx].type) {
 632                        fprintf(stderr, "unknown file type line %d: '%s'\n",
 633                                line_nr, line);
 634                }
 635        }
 636        if (ec == 0)
 637                cpio_trailer();
 638
 639        exit(ec);
 640}
 641