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