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        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), PATH_MAX);
 396                       strncat(expanded, end + 1, PATH_MAX);
 397                       strncpy(new_location, expanded, PATH_MAX);
 398               } else
 399                       break;
 400       }
 401
 402       return new_location;
 403}
 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
 500struct 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        if (argc - optind != 1) {
 561                usage(argv[0]);
 562                exit(1);
 563        }
 564        filename = argv[optind];
 565        if (!strcmp(filename, "-"))
 566                cpio_list = stdin;
 567        else if (!(cpio_list = fopen(filename, "r"))) {
 568                fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
 569                        filename, strerror(errno));
 570                usage(argv[0]);
 571                exit(1);
 572        }
 573
 574        while (fgets(line, LINE_SIZE, cpio_list)) {
 575                int type_idx;
 576                size_t slen = strlen(line);
 577
 578                line_nr++;
 579
 580                if ('#' == *line) {
 581                        /* comment - skip to next line */
 582                        continue;
 583                }
 584
 585                if (! (type = strtok(line, " \t"))) {
 586                        fprintf(stderr,
 587                                "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
 588                                line_nr, line);
 589                        ec = -1;
 590                        break;
 591                }
 592
 593                if ('\n' == *type) {
 594                        /* a blank line */
 595                        continue;
 596                }
 597
 598                if (slen == strlen(type)) {
 599                        /* must be an empty line */
 600                        continue;
 601                }
 602
 603                if (! (args = strtok(NULL, "\n"))) {
 604                        fprintf(stderr,
 605                                "ERROR: incorrect format, newline required line %d: '%s'\n",
 606                                line_nr, line);
 607                        ec = -1;
 608                }
 609
 610                for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
 611                        int rc;
 612                        if (! strcmp(line, file_handler_table[type_idx].type)) {
 613                                if ((rc = file_handler_table[type_idx].handler(args))) {
 614                                        ec = rc;
 615                                        fprintf(stderr, " line %d\n", line_nr);
 616                                }
 617                                break;
 618                        }
 619                }
 620
 621                if (NULL == file_handler_table[type_idx].type) {
 622                        fprintf(stderr, "unknown file type line %d: '%s'\n",
 623                                line_nr, line);
 624                }
 625        }
 626        if (ec == 0)
 627                cpio_trailer();
 628
 629        exit(ec);
 630}
 631
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.