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                        if (fwrite(filebuf, size, 1, stdout) != 1) {
 358                                fprintf(stderr, "writing filebuf failed\n");
 359                                goto error;
 360                        }
 361                        offset += size;
 362                        push_pad();
 363                }
 364
 365                name += namesize;
 366        }
 367        ino++;
 368        rc = 0;
 369        
 370error:
 371        if (filebuf) free(filebuf);
 372        if (file >= 0) close(file);
 373        return rc;
 374}
 375
 376static char *cpio_replace_env(char *new_location)
 377{
 378       char expanded[PATH_MAX + 1];
 379       char env_var[PATH_MAX + 1];
 380       char *start;
 381       char *end;
 382
 383       for (start = NULL; (start = strstr(new_location, "${")); ) {
 384               end = strchr(start, '}');
 385               if (start < end) {
 386                       *env_var = *expanded = '\0';
 387                       strncat(env_var, start + 2, end - start - 2);
 388                       strncat(expanded, new_location, start - new_location);
 389                       strncat(expanded, getenv(env_var), PATH_MAX);
 390                       strncat(expanded, end + 1, PATH_MAX);
 391                       strncpy(new_location, expanded, PATH_MAX);
 392               } else
 393                       break;
 394       }
 395
 396       return new_location;
 397}
 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 <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                prog);
 488}
 489
 490struct file_handler file_handler_table[] = {
 491        {
 492                .type    = "file",
 493                .handler = cpio_mkfile_line,
 494        }, {
 495                .type    = "nod",
 496                .handler = cpio_mknod_line,
 497        }, {
 498                .type    = "dir",
 499                .handler = cpio_mkdir_line,
 500        }, {
 501                .type    = "slink",
 502                .handler = cpio_mkslink_line,
 503        }, {
 504                .type    = "pipe",
 505                .handler = cpio_mkpipe_line,
 506        }, {
 507                .type    = "sock",
 508                .handler = cpio_mksock_line,
 509        }, {
 510                .type    = NULL,
 511                .handler = NULL,
 512        }
 513};
 514
 515#define LINE_SIZE (2 * PATH_MAX + 50)
 516
 517int main (int argc, char *argv[])
 518{
 519        FILE *cpio_list;
 520        char line[LINE_SIZE];
 521        char *args, *type;
 522        int ec = 0;
 523        int line_nr = 0;
 524
 525        if (2 != argc) {
 526                usage(argv[0]);
 527                exit(1);
 528        }
 529
 530        if (!strcmp(argv[1], "-"))
 531                cpio_list = stdin;
 532        else if (! (cpio_list = fopen(argv[1], "r"))) {
 533                fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
 534                        argv[1], strerror(errno));
 535                usage(argv[0]);
 536                exit(1);
 537        }
 538
 539        while (fgets(line, LINE_SIZE, cpio_list)) {
 540                int type_idx;
 541                size_t slen = strlen(line);
 542
 543                line_nr++;
 544
 545                if ('#' == *line) {
 546                        /* comment - skip to next line */
 547                        continue;
 548                }
 549
 550                if (! (type = strtok(line, " \t"))) {
 551                        fprintf(stderr,
 552                                "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
 553                                line_nr, line);
 554                        ec = -1;
 555                        break;
 556                }
 557
 558                if ('\n' == *type) {
 559                        /* a blank line */
 560                        continue;
 561                }
 562
 563                if (slen == strlen(type)) {
 564                        /* must be an empty line */
 565                        continue;
 566                }
 567
 568                if (! (args = strtok(NULL, "\n"))) {
 569                        fprintf(stderr,
 570                                "ERROR: incorrect format, newline required line %d: '%s'\n",
 571                                line_nr, line);
 572                        ec = -1;
 573                }
 574
 575                for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
 576                        int rc;
 577                        if (! strcmp(line, file_handler_table[type_idx].type)) {
 578                                if ((rc = file_handler_table[type_idx].handler(args))) {
 579                                        ec = rc;
 580                                        fprintf(stderr, " line %d\n", line_nr);
 581                                }
 582                                break;
 583                        }
 584                }
 585
 586                if (NULL == file_handler_table[type_idx].type) {
 587                        fprintf(stderr, "unknown file type line %d: '%s'\n",
 588                                line_nr, line);
 589                }
 590        }
 591        if (ec == 0)
 592                cpio_trailer();
 593
 594        exit(ec);
 595}
 596