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 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        filebuf = malloc(buf.st_size);
 324        if (!filebuf) {
 325                fprintf (stderr, "out of memory\n");
 326                goto error;
 327        }
 328
 329        retval = read (file, filebuf, buf.st_size);
 330        if (retval < 0) {
 331                fprintf (stderr, "Can not read %s file\n", location);
 332                goto error;
 333        }
 334
 335        size = 0;
 336        for (i = 1; i <= nlinks; i++) {
 337                /* data goes on last link */
 338                if (i == nlinks) size = buf.st_size;
 339
 340                if (name[0] == '/')
 341                        name++;
 342                namesize = strlen(name) + 1;
 343                sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
 344                       "%08lX%08X%08X%08X%08X%08X%08X",
 345                        "070701",               /* magic */
 346                        ino,                    /* ino */
 347                        mode,                   /* mode */
 348                        (long) uid,             /* uid */
 349                        (long) gid,             /* gid */
 350                        nlinks,                 /* nlink */
 351                        (long) buf.st_mtime,    /* mtime */
 352                        size,                   /* filesize */
 353                        3,                      /* major */
 354                        1,                      /* minor */
 355                        0,                      /* rmajor */
 356                        0,                      /* rminor */
 357                        namesize,               /* namesize */
 358                        0);                     /* chksum */
 359                push_hdr(s);
 360                push_string(name);
 361                push_pad();
 362
 363                if (size) {
 364                        if (fwrite(filebuf, size, 1, stdout) != 1) {
 365                                fprintf(stderr, "writing filebuf failed\n");
 366                                goto error;
 367                        }
 368                        offset += size;
 369                        push_pad();
 370                }
 371
 372                name += namesize;
 373        }
 374        ino++;
 375        rc = 0;
 376        
 377error:
 378        if (filebuf) free(filebuf);
 379        if (file >= 0) close(file);
 380        return rc;
 381}
 382
 383static char *cpio_replace_env(char *new_location)
 384{
 385        char expanded[PATH_MAX + 1];
 386        char *start, *end, *var;
 387
 388        while ((start = strstr(new_location, "${")) &&
 389               (end = strchr(start + 2, '}'))) {
 390                *start = *end = 0;
 391                var = getenv(start + 2);
 392                snprintf(expanded, sizeof expanded, "%s%s%s",
 393                         new_location, var ? var : "", end + 1);
 394                strcpy(new_location, expanded);
 395        }
 396
 397        return new_location;
 398}
 399
 400static int cpio_mkfile_line(const char *line)
 401{
 402        char name[PATH_MAX + 1];
 403        char *dname = NULL; /* malloc'ed buffer for hard links */
 404        char location[PATH_MAX + 1];
 405        unsigned int mode;
 406        int uid;
 407        int gid;
 408        int nlinks = 1;
 409        int end = 0, dname_len = 0;
 410        int rc = -1;
 411
 412        if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
 413                                "s %o %d %d %n",
 414                                name, location, &mode, &uid, &gid, &end)) {
 415                fprintf(stderr, "Unrecognized file format '%s'", line);
 416                goto fail;
 417        }
 418        if (end && isgraph(line[end])) {
 419                int len;
 420                int nend;
 421
 422                dname = malloc(strlen(line));
 423                if (!dname) {
 424                        fprintf (stderr, "out of memory (%d)\n", dname_len);
 425                        goto fail;
 426                }
 427
 428                dname_len = strlen(name) + 1;
 429                memcpy(dname, name, dname_len);
 430
 431                do {
 432                        nend = 0;
 433                        if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
 434                                        name, &nend) < 1)
 435                                break;
 436                        len = strlen(name) + 1;
 437                        memcpy(dname + dname_len, name, len);
 438                        dname_len += len;
 439                        nlinks++;
 440                        end += nend;
 441                } while (isgraph(line[end]));
 442        } else {
 443                dname = name;
 444        }
 445        rc = cpio_mkfile(dname, cpio_replace_env(location),
 446                         mode, uid, gid, nlinks);
 447 fail:
 448        if (dname_len) free(dname);
 449        return rc;
 450}
 451
 452static void usage(const char *prog)
 453{
 454        fprintf(stderr, "Usage:\n"
 455                "\t%s [-t <timestamp>] <cpio_list>\n"
 456                "\n"
 457                "<cpio_list> is a file containing newline separated entries that\n"
 458                "describe the files to be included in the initramfs archive:\n"
 459                "\n"
 460                "# a comment\n"
 461                "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
 462                "dir <name> <mode> <uid> <gid>\n"
 463                "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
 464                "slink <name> <target> <mode> <uid> <gid>\n"
 465                "pipe <name> <mode> <uid> <gid>\n"
 466                "sock <name> <mode> <uid> <gid>\n"
 467                "\n"
 468                "<name>       name of the file/dir/nod/etc in the archive\n"
 469                "<location>   location of the file in the current filesystem\n"
 470                "             expands shell variables quoted with ${}\n"
 471                "<target>     link target\n"
 472                "<mode>       mode/permissions of the file\n"
 473                "<uid>        user id (0=root)\n"
 474                "<gid>        group id (0=root)\n"
 475                "<dev_type>   device type (b=block, c=character)\n"
 476                "<maj>        major number of nod\n"
 477                "<min>        minor number of nod\n"
 478                "<hard links> space separated list of other links to file\n"
 479                "\n"
 480                "example:\n"
 481                "# A simple initramfs\n"
 482                "dir /dev 0755 0 0\n"
 483                "nod /dev/console 0600 0 0 c 5 1\n"
 484                "dir /root 0700 0 0\n"
 485                "dir /sbin 0755 0 0\n"
 486                "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n"
 487                "\n"
 488                "<timestamp> is time in seconds since Epoch that will be used\n"
 489                "as mtime for symlinks, special files and directories. The default\n"
 490                "is to use the current time for these entries.\n",
 491                prog);
 492}
 493
 494struct file_handler file_handler_table[] = {
 495        {
 496                .type    = "file",
 497                .handler = cpio_mkfile_line,
 498        }, {
 499                .type    = "nod",
 500                .handler = cpio_mknod_line,
 501        }, {
 502                .type    = "dir",
 503                .handler = cpio_mkdir_line,
 504        }, {
 505                .type    = "slink",
 506                .handler = cpio_mkslink_line,
 507        }, {
 508                .type    = "pipe",
 509                .handler = cpio_mkpipe_line,
 510        }, {
 511                .type    = "sock",
 512                .handler = cpio_mksock_line,
 513        }, {
 514                .type    = NULL,
 515                .handler = NULL,
 516        }
 517};
 518
 519#define LINE_SIZE (2 * PATH_MAX + 50)
 520
 521int main (int argc, char *argv[])
 522{
 523        FILE *cpio_list;
 524        char line[LINE_SIZE];
 525        char *args, *type;
 526        int ec = 0;
 527        int line_nr = 0;
 528        const char *filename;
 529
 530        default_mtime = time(NULL);
 531        while (1) {
 532                int opt = getopt(argc, argv, "t:h");
 533                char *invalid;
 534
 535                if (opt == -1)
 536                        break;
 537                switch (opt) {
 538                case 't':
 539                        default_mtime = strtol(optarg, &invalid, 10);
 540                        if (!*optarg || *invalid) {
 541                                fprintf(stderr, "Invalid timestamp: %s\n",
 542                                                optarg);
 543                                usage(argv[0]);
 544                                exit(1);
 545                        }
 546                        break;
 547                case 'h':
 548                case '?':
 549                        usage(argv[0]);
 550                        exit(opt == 'h' ? 0 : 1);
 551                }
 552        }
 553
 554        if (argc - optind != 1) {
 555                usage(argv[0]);
 556                exit(1);
 557        }
 558        filename = argv[optind];
 559        if (!strcmp(filename, "-"))
 560                cpio_list = stdin;
 561        else if (!(cpio_list = fopen(filename, "r"))) {
 562                fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
 563                        filename, strerror(errno));
 564                usage(argv[0]);
 565                exit(1);
 566        }
 567
 568        while (fgets(line, LINE_SIZE, cpio_list)) {
 569                int type_idx;
 570                size_t slen = strlen(line);
 571
 572                line_nr++;
 573
 574                if ('#' == *line) {
 575                        /* comment - skip to next line */
 576                        continue;
 577                }
 578
 579                if (! (type = strtok(line, " \t"))) {
 580                        fprintf(stderr,
 581                                "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
 582                                line_nr, line);
 583                        ec = -1;
 584                        break;
 585                }
 586
 587                if ('\n' == *type) {
 588                        /* a blank line */
 589                        continue;
 590                }
 591
 592                if (slen == strlen(type)) {
 593                        /* must be an empty line */
 594                        continue;
 595                }
 596
 597                if (! (args = strtok(NULL, "\n"))) {
 598                        fprintf(stderr,
 599                                "ERROR: incorrect format, newline required line %d: '%s'\n",
 600                                line_nr, line);
 601                        ec = -1;
 602                }
 603
 604                for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
 605                        int rc;
 606                        if (! strcmp(line, file_handler_table[type_idx].type)) {
 607                                if ((rc = file_handler_table[type_idx].handler(args))) {
 608                                        ec = rc;
 609                                        fprintf(stderr, " line %d\n", line_nr);
 610                                }
 611                                break;
 612                        }
 613                }
 614
 615                if (NULL == file_handler_table[type_idx].type) {
 616                        fprintf(stderr, "unknown file type line %d: '%s'\n",
 617                                line_nr, line);
 618                }
 619        }
 620        if (ec == 0)
 621                cpio_trailer();
 622
 623        exit(ec);
 624}
 625