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 int cpio_mkfile_line(const char *line)
 374{
 375        char name[PATH_MAX + 1];
 376        char *dname = NULL; /* malloc'ed buffer for hard links */
 377        char location[PATH_MAX + 1];
 378        unsigned int mode;
 379        int uid;
 380        int gid;
 381        int nlinks = 1;
 382        int end = 0, dname_len = 0;
 383        int rc = -1;
 384
 385        if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
 386                                "s %o %d %d %n",
 387                                name, location, &mode, &uid, &gid, &end)) {
 388                fprintf(stderr, "Unrecognized file format '%s'", line);
 389                goto fail;
 390        }
 391        if (end && isgraph(line[end])) {
 392                int len;
 393                int nend;
 394
 395                dname = malloc(strlen(line));
 396                if (!dname) {
 397                        fprintf (stderr, "out of memory (%d)\n", dname_len);
 398                        goto fail;
 399                }
 400
 401                dname_len = strlen(name) + 1;
 402                memcpy(dname, name, dname_len);
 403
 404                do {
 405                        nend = 0;
 406                        if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
 407                                        name, &nend) < 1)
 408                                break;
 409                        len = strlen(name) + 1;
 410                        memcpy(dname + dname_len, name, len);
 411                        dname_len += len;
 412                        nlinks++;
 413                        end += nend;
 414                } while (isgraph(line[end]));
 415        } else {
 416                dname = name;
 417        }
 418        rc = cpio_mkfile(dname, location, mode, uid, gid, nlinks);
 419 fail:
 420        if (dname_len) free(dname);
 421        return rc;
 422}
 423
 424void usage(const char *prog)
 425{
 426        fprintf(stderr, "Usage:\n"
 427                "\t%s <cpio_list>\n"
 428                "\n"
 429                "<cpio_list> is a file containing newline separated entries that\n"
 430                "describe the files to be included in the initramfs archive:\n"
 431                "\n"
 432                "# a comment\n"
 433                "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
 434                "dir <name> <mode> <uid> <gid>\n"
 435                "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
 436                "slink <name> <target> <mode> <uid> <gid>\n"
 437                "pipe <name> <mode> <uid> <gid>\n"
 438                "sock <name> <mode> <uid> <gid>\n"
 439                "\n"
 440                "<name>       name of the file/dir/nod/etc in the archive\n"
 441                "<location>   location of the file in the current filesystem\n"
 442                "<target>     link target\n"
 443                "<mode>       mode/permissions of the file\n"
 444                "<uid>        user id (0=root)\n"
 445                "<gid>        group id (0=root)\n"
 446                "<dev_type>   device type (b=block, c=character)\n"
 447                "<maj>        major number of nod\n"
 448                "<min>        minor number of nod\n"
 449                "<hard links> space separated list of other links to file\n"
 450                "\n"
 451                "example:\n"
 452                "# A simple initramfs\n"
 453                "dir /dev 0755 0 0\n"
 454                "nod /dev/console 0600 0 0 c 5 1\n"
 455                "dir /root 0700 0 0\n"
 456                "dir /sbin 0755 0 0\n"
 457                "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n",
 458                prog);
 459}
 460
 461struct file_handler file_handler_table[] = {
 462        {
 463                .type    = "file",
 464                .handler = cpio_mkfile_line,
 465        }, {
 466                .type    = "nod",
 467                .handler = cpio_mknod_line,
 468        }, {
 469                .type    = "dir",
 470                .handler = cpio_mkdir_line,
 471        }, {
 472                .type    = "slink",
 473                .handler = cpio_mkslink_line,
 474        }, {
 475                .type    = "pipe",
 476                .handler = cpio_mkpipe_line,
 477        }, {
 478                .type    = "sock",
 479                .handler = cpio_mksock_line,
 480        }, {
 481                .type    = NULL,
 482                .handler = NULL,
 483        }
 484};
 485
 486#define LINE_SIZE (2 * PATH_MAX + 50)
 487
 488int main (int argc, char *argv[])
 489{
 490        FILE *cpio_list;
 491        char line[LINE_SIZE];
 492        char *args, *type;
 493        int ec = 0;
 494        int line_nr = 0;
 495
 496        if (2 != argc) {
 497                usage(argv[0]);
 498                exit(1);
 499        }
 500
 501        if (!strcmp(argv[1], "-"))
 502                cpio_list = stdin;
 503        else if (! (cpio_list = fopen(argv[1], "r"))) {
 504                fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
 505                        argv[1], strerror(errno));
 506                usage(argv[0]);
 507                exit(1);
 508        }
 509
 510        while (fgets(line, LINE_SIZE, cpio_list)) {
 511                int type_idx;
 512                size_t slen = strlen(line);
 513
 514                line_nr++;
 515
 516                if ('#' == *line) {
 517                        /* comment - skip to next line */
 518                        continue;
 519                }
 520
 521                if (! (type = strtok(line, " \t"))) {
 522                        fprintf(stderr,
 523                                "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
 524                                line_nr, line);
 525                        ec = -1;
 526                        break;
 527                }
 528
 529                if ('\n' == *type) {
 530                        /* a blank line */
 531                        continue;
 532                }
 533
 534                if (slen == strlen(type)) {
 535                        /* must be an empty line */
 536                        continue;
 537                }
 538
 539                if (! (args = strtok(NULL, "\n"))) {
 540                        fprintf(stderr,
 541                                "ERROR: incorrect format, newline required line %d: '%s'\n",
 542                                line_nr, line);
 543                        ec = -1;
 544                }
 545
 546                for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
 547                        int rc;
 548                        if (! strcmp(line, file_handler_table[type_idx].type)) {
 549                                if ((rc = file_handler_table[type_idx].handler(args))) {
 550                                        ec = rc;
 551                                        fprintf(stderr, " line %d\n", line_nr);
 552                                }
 553                                break;
 554                        }
 555                }
 556
 557                if (NULL == file_handler_table[type_idx].type) {
 558                        fprintf(stderr, "unknown file type line %d: '%s'\n",
 559                                line_nr, line);
 560                }
 561        }
 562        if (ec == 0)
 563                cpio_trailer();
 564
 565        exit(ec);
 566}
 567