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