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        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) 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        time_t mtime = time(NULL);
 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) 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        time_t mtime = time(NULL);
 246
 247        if (dev_type == 'b')
 248                mode |= S_IFBLK;
 249        else
 250                mode |= S_IFCHR;
 251
 252        if (name[0] == '/')
 253                name++;
 254        sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
 255               "%08X%08X%08X%08X%08X%08X%08X",
 256                "070701",               /* magic */
 257                ino++,                  /* ino */
 258                mode,                   /* mode */
 259                (long) uid,             /* uid */
 260                (long) gid,             /* gid */
 261                1,                      /* nlink */
 262                (long) mtime,           /* mtime */
 263                0,                      /* filesize */
 264                3,                      /* major */
 265                1,                      /* minor */
 266                maj,                    /* rmajor */
 267                min,                    /* rminor */
 268                (unsigned)strlen(name) + 1,/* namesize */
 269                0);                     /* chksum */
 270        push_hdr(s);
 271        push_rest(name);
 272        return 0;
 273}
 274
 275static int cpio_mknod_line(const char *line)
 276{
 277        char name[PATH_MAX + 1];
 278        unsigned int mode;
 279        int uid;
 280        int gid;
 281        char dev_type;
 282        unsigned int maj;
 283        unsigned int min;
 284        int rc = -1;
 285
 286        if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",
 287                         name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
 288                fprintf(stderr, "Unrecognized nod format '%s'", line);
 289                goto fail;
 290        }
 291        rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
 292 fail:
 293        return rc;
 294}
 295
 296static int cpio_mkfile(const char *name, const char *location,
 297                        unsigned int mode, uid_t uid, gid_t gid,
 298                        unsigned int nlinks)
 299{
 300        char s[256];
 301        char *filebuf = NULL;
 302        struct stat buf;
 303        long size;
 304        int file = -1;
 305        int retval;
 306        int rc = -1;
 307        int namesize;
 308        int i;
 309
 310        mode |= S_IFREG;
 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        retval = fstat(file, &buf);
 319        if (retval) {
 320                fprintf(stderr, "File %s could not be stat()'ed\n", location);
 321                goto error;
 322        }
 323
 324        filebuf = malloc(buf.st_size);
 325        if (!filebuf) {
 326                fprintf (stderr, "out of memory\n");
 327                goto error;
 328        }
 329
 330        retval = read (file, filebuf, buf.st_size);
 331        if (retval < 0) {
 332                fprintf (stderr, "Can not read %s file\n", location);
 333                goto error;
 334        }
 335
 336        size = 0;
 337        for (i = 1; i <= nlinks; i++) {
 338                /* data goes on last link */
 339                if (i == nlinks) size = buf.st_size;
 340
 341                if (name[0] == '/')
 342                        name++;
 343                namesize = strlen(name) + 1;
 344                sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
 345                       "%08lX%08X%08X%08X%08X%08X%08X",
 346                        "070701",               /* magic */
 347                        ino,                    /* ino */
 348                        mode,                   /* mode */
 349                        (long) uid,             /* uid */
 350                        (long) gid,             /* gid */
 351                        nlinks,                 /* nlink */
 352                        (long) buf.st_mtime,    /* mtime */
 353                        size,                   /* filesize */
 354                        3,                      /* major */
 355                        1,                      /* minor */
 356                        0,                      /* rmajor */
 357                        0,                      /* rminor */
 358                        namesize,               /* namesize */
 359                        0);                     /* chksum */
 360                push_hdr(s);
 361                push_string(name);
 362                push_pad();
 363
 364                if (size) {
 365                        if (fwrite(filebuf, size, 1, stdout) != 1) {
 366                                fprintf(stderr, "writing filebuf failed\n");
 367                                goto error;
 368                        }
 369                        offset += size;
 370                        push_pad();
 371                }
 372
 373                name += namesize;
 374        }
 375        ino++;
 376        rc = 0;
 377        
 378error:
 379        if (filebuf) free(filebuf);
 380        if (file >= 0) close(file);
 381        return rc;
 382}
 383
 384static char *cpio_replace_env(char *new_location)
 385{
 386       char expanded[PATH_MAX + 1];
 387       char env_var[PATH_MAX + 1];
 388       char *start;
 389       char *end;
 390
 391       for (start = NULL; (start = strstr(new_location, "${")); ) {
 392               end = strchr(start, '}');
 393               if (start < end) {
 394                       *env_var = *expanded = '\0';
 395                       strncat(env_var, start + 2, end - start - 2);
 396                       strncat(expanded, new_location, start - new_location);
 397                       strncat(expanded, getenv(env_var), PATH_MAX);
 398                       strncat(expanded, end + 1, PATH_MAX);
 399                       strncpy(new_location, expanded, PATH_MAX);
 400               } else
 401                       break;
 402       }
 403
 404       return new_location;
 405}
 406
 407
 408static int cpio_mkfile_line(const char *line)
 409{
 410        char name[PATH_MAX + 1];
 411        char *dname = NULL; /* malloc'ed buffer for hard links */
 412        char location[PATH_MAX + 1];
 413        unsigned int mode;
 414        int uid;
 415        int gid;
 416        int nlinks = 1;
 417        int end = 0, dname_len = 0;
 418        int rc = -1;
 419
 420        if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
 421                                "s %o %d %d %n",
 422                                name, location, &mode, &uid, &gid, &end)) {
 423                fprintf(stderr, "Unrecognized file format '%s'", line);
 424                goto fail;
 425        }
 426        if (end && isgraph(line[end])) {
 427                int len;
 428                int nend;
 429
 430                dname = malloc(strlen(line));
 431                if (!dname) {
 432                        fprintf (stderr, "out of memory (%d)\n", dname_len);
 433                        goto fail;
 434                }
 435
 436                dname_len = strlen(name) + 1;
 437                memcpy(dname, name, dname_len);
 438
 439                do {
 440                        nend = 0;
 441                        if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
 442                                        name, &nend) < 1)
 443                                break;
 444                        len = strlen(name) + 1;
 445                        memcpy(dname + dname_len, name, len);
 446                        dname_len += len;
 447                        nlinks++;
 448                        end += nend;
 449                } while (isgraph(line[end]));
 450        } else {
 451                dname = name;
 452        }
 453        rc = cpio_mkfile(dname, cpio_replace_env(location),
 454                         mode, uid, gid, nlinks);
 455 fail:
 456        if (dname_len) free(dname);
 457        return rc;
 458}
 459
 460static void usage(const char *prog)
 461{
 462        fprintf(stderr, "Usage:\n"
 463                "\t%s <cpio_list>\n"
 464                "\n"
 465                "<cpio_list> is a file containing newline separated entries that\n"
 466                "describe the files to be included in the initramfs archive:\n"
 467                "\n"
 468                "# a comment\n"
 469                "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
 470                "dir <name> <mode> <uid> <gid>\n"
 471                "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
 472                "slink <name> <target> <mode> <uid> <gid>\n"
 473                "pipe <name> <mode> <uid> <gid>\n"
 474                "sock <name> <mode> <uid> <gid>\n"
 475                "\n"
 476                "<name>       name of the file/dir/nod/etc in the archive\n"
 477                "<location>   location of the file in the current filesystem\n"
 478                "             expands shell variables quoted with ${}\n"
 479                "<target>     link target\n"
 480                "<mode>       mode/permissions of the file\n"
 481                "<uid>        user id (0=root)\n"
 482                "<gid>        group id (0=root)\n"
 483                "<dev_type>   device type (b=block, c=character)\n"
 484                "<maj>        major number of nod\n"
 485                "<min>        minor number of nod\n"
 486                "<hard links> space separated list of other links to file\n"
 487                "\n"
 488                "example:\n"
 489                "# A simple initramfs\n"
 490                "dir /dev 0755 0 0\n"
 491                "nod /dev/console 0600 0 0 c 5 1\n"
 492                "dir /root 0700 0 0\n"
 493                "dir /sbin 0755 0 0\n"
 494                "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n",
 495                prog);
 496}
 497
 498struct file_handler file_handler_table[] = {
 499        {
 500                .type    = "file",
 501                .handler = cpio_mkfile_line,
 502        }, {
 503                .type    = "nod",
 504                .handler = cpio_mknod_line,
 505        }, {
 506                .type    = "dir",
 507                .handler = cpio_mkdir_line,
 508        }, {
 509                .type    = "slink",
 510                .handler = cpio_mkslink_line,
 511        }, {
 512                .type    = "pipe",
 513                .handler = cpio_mkpipe_line,
 514        }, {
 515                .type    = "sock",
 516                .handler = cpio_mksock_line,
 517        }, {
 518                .type    = NULL,
 519                .handler = NULL,
 520        }
 521};
 522
 523#define LINE_SIZE (2 * PATH_MAX + 50)
 524
 525int main (int argc, char *argv[])
 526{
 527        FILE *cpio_list;
 528        char line[LINE_SIZE];
 529        char *args, *type;
 530        int ec = 0;
 531        int line_nr = 0;
 532
 533        if (2 != argc) {
 534                usage(argv[0]);
 535                exit(1);
 536        }
 537
 538        if (!strcmp(argv[1], "-"))
 539                cpio_list = stdin;
 540        else if (! (cpio_list = fopen(argv[1], "r"))) {
 541                fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
 542                        argv[1], strerror(errno));
 543                usage(argv[0]);
 544                exit(1);
 545        }
 546
 547        while (fgets(line, LINE_SIZE, cpio_list)) {
 548                int type_idx;
 549                size_t slen = strlen(line);
 550
 551                line_nr++;
 552
 553                if ('#' == *line) {
 554                        /* comment - skip to next line */
 555                        continue;
 556                }
 557
 558                if (! (type = strtok(line, " \t"))) {
 559                        fprintf(stderr,
 560                                "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
 561                                line_nr, line);
 562                        ec = -1;
 563                        break;
 564                }
 565
 566                if ('\n' == *type) {
 567                        /* a blank line */
 568                        continue;
 569                }
 570
 571                if (slen == strlen(type)) {
 572                        /* must be an empty line */
 573                        continue;
 574                }
 575
 576                if (! (args = strtok(NULL, "\n"))) {
 577                        fprintf(stderr,
 578                                "ERROR: incorrect format, newline required line %d: '%s'\n",
 579                                line_nr, line);
 580                        ec = -1;
 581                }
 582
 583                for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
 584                        int rc;
 585                        if (! strcmp(line, file_handler_table[type_idx].type)) {
 586                                if ((rc = file_handler_table[type_idx].handler(args))) {
 587                                        ec = rc;
 588                                        fprintf(stderr, " line %d\n", line_nr);
 589                                }
 590                                break;
 591                        }
 592                }
 593
 594                if (NULL == file_handler_table[type_idx].type) {
 595                        fprintf(stderr, "unknown file type line %d: '%s'\n",
 596                                line_nr, line);
 597                }
 598        }
 599        if (ec == 0)
 600                cpio_trailer();
 601
 602        exit(ec);
 603}
 604