root/usr/gen_init_cpio.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. push_string
  2. push_pad
  3. push_rest
  4. push_hdr
  5. cpio_trailer
  6. cpio_mkslink
  7. cpio_mkslink_line
  8. cpio_mkgeneric
  9. cpio_mkgeneric_line
  10. cpio_mkdir_line
  11. cpio_mkpipe_line
  12. cpio_mksock_line
  13. cpio_mknod
  14. cpio_mknod_line
  15. cpio_mkfile
  16. cpio_replace_env
  17. cpio_mkfile_line
  18. usage
  19. main

   1 // SPDX-License-Identifier: GPL-2.0
   2 #include <stdio.h>
   3 #include <stdlib.h>
   4 #include <sys/types.h>
   5 #include <sys/stat.h>
   6 #include <string.h>
   7 #include <unistd.h>
   8 #include <time.h>
   9 #include <fcntl.h>
  10 #include <errno.h>
  11 #include <ctype.h>
  12 #include <limits.h>
  13 
  14 /*
  15  * Original work by Jeff Garzik
  16  *
  17  * External file lists, symlink, pipe and fifo support by Thayne Harbaugh
  18  * Hard link support by Luciano Rocha
  19  */
  20 
  21 #define xstr(s) #s
  22 #define str(s) xstr(s)
  23 
  24 static unsigned int offset;
  25 static unsigned int ino = 721;
  26 static time_t default_mtime;
  27 
  28 struct file_handler {
  29         const char *type;
  30         int (*handler)(const char *line);
  31 };
  32 
  33 static void push_string(const char *name)
  34 {
  35         unsigned int name_len = strlen(name) + 1;
  36 
  37         fputs(name, stdout);
  38         putchar(0);
  39         offset += name_len;
  40 }
  41 
  42 static void push_pad (void)
  43 {
  44         while (offset & 3) {
  45                 putchar(0);
  46                 offset++;
  47         }
  48 }
  49 
  50 static void push_rest(const char *name)
  51 {
  52         unsigned int name_len = strlen(name) + 1;
  53         unsigned int tmp_ofs;
  54 
  55         fputs(name, stdout);
  56         putchar(0);
  57         offset += name_len;
  58 
  59         tmp_ofs = name_len + 110;
  60         while (tmp_ofs & 3) {
  61                 putchar(0);
  62                 offset++;
  63                 tmp_ofs++;
  64         }
  65 }
  66 
  67 static void push_hdr(const char *s)
  68 {
  69         fputs(s, stdout);
  70         offset += 110;
  71 }
  72 
  73 static void cpio_trailer(void)
  74 {
  75         char s[256];
  76         const char name[] = "TRAILER!!!";
  77 
  78         sprintf(s, "%s%08X%08X%08lX%08lX%08X%08lX"
  79                "%08X%08X%08X%08X%08X%08X%08X",
  80                 "070701",               /* magic */
  81                 0,                      /* ino */
  82                 0,                      /* mode */
  83                 (long) 0,               /* uid */
  84                 (long) 0,               /* gid */
  85                 1,                      /* nlink */
  86                 (long) 0,               /* mtime */
  87                 0,                      /* filesize */
  88                 0,                      /* major */
  89                 0,                      /* minor */
  90                 0,                      /* rmajor */
  91                 0,                      /* rminor */
  92                 (unsigned)strlen(name)+1, /* namesize */
  93                 0);                     /* chksum */
  94         push_hdr(s);
  95         push_rest(name);
  96 
  97         while (offset % 512) {
  98                 putchar(0);
  99                 offset++;
 100         }
 101 }
 102 
 103 static int cpio_mkslink(const char *name, const char *target,
 104                          unsigned int mode, uid_t uid, gid_t gid)
 105 {
 106         char s[256];
 107 
 108         if (name[0] == '/')
 109                 name++;
 110         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
 111                "%08X%08X%08X%08X%08X%08X%08X",
 112                 "070701",               /* magic */
 113                 ino++,                  /* ino */
 114                 S_IFLNK | mode,         /* mode */
 115                 (long) uid,             /* uid */
 116                 (long) gid,             /* gid */
 117                 1,                      /* nlink */
 118                 (long) default_mtime,   /* mtime */
 119                 (unsigned)strlen(target)+1, /* filesize */
 120                 3,                      /* major */
 121                 1,                      /* minor */
 122                 0,                      /* rmajor */
 123                 0,                      /* rminor */
 124                 (unsigned)strlen(name) + 1,/* namesize */
 125                 0);                     /* chksum */
 126         push_hdr(s);
 127         push_string(name);
 128         push_pad();
 129         push_string(target);
 130         push_pad();
 131         return 0;
 132 }
 133 
 134 static int cpio_mkslink_line(const char *line)
 135 {
 136         char name[PATH_MAX + 1];
 137         char target[PATH_MAX + 1];
 138         unsigned int mode;
 139         int uid;
 140         int gid;
 141         int rc = -1;
 142 
 143         if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) {
 144                 fprintf(stderr, "Unrecognized dir format '%s'", line);
 145                 goto fail;
 146         }
 147         rc = cpio_mkslink(name, target, mode, uid, gid);
 148  fail:
 149         return rc;
 150 }
 151 
 152 static int cpio_mkgeneric(const char *name, unsigned int mode,
 153                        uid_t uid, gid_t gid)
 154 {
 155         char s[256];
 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) default_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 
 180 enum generic_types {
 181         GT_DIR,
 182         GT_PIPE,
 183         GT_SOCK
 184 };
 185 
 186 struct generic_type {
 187         const char *type;
 188         mode_t mode;
 189 };
 190 
 191 static 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 
 206 static 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 
 225 static int cpio_mkdir_line(const char *line)
 226 {
 227         return cpio_mkgeneric_line(line, GT_DIR);
 228 }
 229 
 230 static int cpio_mkpipe_line(const char *line)
 231 {
 232         return cpio_mkgeneric_line(line, GT_PIPE);
 233 }
 234 
 235 static int cpio_mksock_line(const char *line)
 236 {
 237         return cpio_mkgeneric_line(line, GT_SOCK);
 238 }
 239 
 240 static 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 
 246         if (dev_type == 'b')
 247                 mode |= S_IFBLK;
 248         else
 249                 mode |= S_IFCHR;
 250 
 251         if (name[0] == '/')
 252                 name++;
 253         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
 254                "%08X%08X%08X%08X%08X%08X%08X",
 255                 "070701",               /* magic */
 256                 ino++,                  /* ino */
 257                 mode,                   /* mode */
 258                 (long) uid,             /* uid */
 259                 (long) gid,             /* gid */
 260                 1,                      /* nlink */
 261                 (long) default_mtime,   /* mtime */
 262                 0,                      /* filesize */
 263                 3,                      /* major */
 264                 1,                      /* minor */
 265                 maj,                    /* rmajor */
 266                 min,                    /* rminor */
 267                 (unsigned)strlen(name) + 1,/* namesize */
 268                 0);                     /* chksum */
 269         push_hdr(s);
 270         push_rest(name);
 271         return 0;
 272 }
 273 
 274 static int cpio_mknod_line(const char *line)
 275 {
 276         char name[PATH_MAX + 1];
 277         unsigned int mode;
 278         int uid;
 279         int gid;
 280         char dev_type;
 281         unsigned int maj;
 282         unsigned int min;
 283         int rc = -1;
 284 
 285         if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",
 286                          name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
 287                 fprintf(stderr, "Unrecognized nod format '%s'", line);
 288                 goto fail;
 289         }
 290         rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
 291  fail:
 292         return rc;
 293 }
 294 
 295 static int cpio_mkfile(const char *name, const char *location,
 296                         unsigned int mode, uid_t uid, gid_t gid,
 297                         unsigned int nlinks)
 298 {
 299         char s[256];
 300         char *filebuf = NULL;
 301         struct stat buf;
 302         long size;
 303         int file = -1;
 304         int retval;
 305         int rc = -1;
 306         int namesize;
 307         unsigned int i;
 308 
 309         mode |= S_IFREG;
 310 
 311         file = open (location, O_RDONLY);
 312         if (file < 0) {
 313                 fprintf (stderr, "File %s could not be opened for reading\n", location);
 314                 goto error;
 315         }
 316 
 317         retval = fstat(file, &buf);
 318         if (retval) {
 319                 fprintf(stderr, "File %s could not be stat()'ed\n", location);
 320                 goto error;
 321         }
 322 
 323         filebuf = malloc(buf.st_size);
 324         if (!filebuf) {
 325                 fprintf (stderr, "out of memory\n");
 326                 goto error;
 327         }
 328 
 329         retval = read (file, filebuf, buf.st_size);
 330         if (retval < 0) {
 331                 fprintf (stderr, "Can not read %s file\n", location);
 332                 goto error;
 333         }
 334 
 335         size = 0;
 336         for (i = 1; i <= nlinks; i++) {
 337                 /* data goes on last link */
 338                 if (i == nlinks) size = buf.st_size;
 339 
 340                 if (name[0] == '/')
 341                         name++;
 342                 namesize = strlen(name) + 1;
 343                 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
 344                        "%08lX%08X%08X%08X%08X%08X%08X",
 345                         "070701",               /* magic */
 346                         ino,                    /* ino */
 347                         mode,                   /* mode */
 348                         (long) uid,             /* uid */
 349                         (long) gid,             /* gid */
 350                         nlinks,                 /* nlink */
 351                         (long) buf.st_mtime,    /* mtime */
 352                         size,                   /* filesize */
 353                         3,                      /* major */
 354                         1,                      /* minor */
 355                         0,                      /* rmajor */
 356                         0,                      /* rminor */
 357                         namesize,               /* namesize */
 358                         0);                     /* chksum */
 359                 push_hdr(s);
 360                 push_string(name);
 361                 push_pad();
 362 
 363                 if (size) {
 364                         if (fwrite(filebuf, size, 1, stdout) != 1) {
 365                                 fprintf(stderr, "writing filebuf failed\n");
 366                                 goto error;
 367                         }
 368                         offset += size;
 369                         push_pad();
 370                 }
 371 
 372                 name += namesize;
 373         }
 374         ino++;
 375         rc = 0;
 376         
 377 error:
 378         if (filebuf) free(filebuf);
 379         if (file >= 0) close(file);
 380         return rc;
 381 }
 382 
 383 static char *cpio_replace_env(char *new_location)
 384 {
 385         char expanded[PATH_MAX + 1];
 386         char *start, *end, *var;
 387 
 388         while ((start = strstr(new_location, "${")) &&
 389                (end = strchr(start + 2, '}'))) {
 390                 *start = *end = 0;
 391                 var = getenv(start + 2);
 392                 snprintf(expanded, sizeof expanded, "%s%s%s",
 393                          new_location, var ? var : "", end + 1);
 394                 strcpy(new_location, expanded);
 395         }
 396 
 397         return new_location;
 398 }
 399 
 400 static 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 
 452 static void usage(const char *prog)
 453 {
 454         fprintf(stderr, "Usage:\n"
 455                 "\t%s [-t <timestamp>] <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                 "\n"
 488                 "<timestamp> is time in seconds since Epoch that will be used\n"
 489                 "as mtime for symlinks, special files and directories. The default\n"
 490                 "is to use the current time for these entries.\n",
 491                 prog);
 492 }
 493 
 494 struct file_handler file_handler_table[] = {
 495         {
 496                 .type    = "file",
 497                 .handler = cpio_mkfile_line,
 498         }, {
 499                 .type    = "nod",
 500                 .handler = cpio_mknod_line,
 501         }, {
 502                 .type    = "dir",
 503                 .handler = cpio_mkdir_line,
 504         }, {
 505                 .type    = "slink",
 506                 .handler = cpio_mkslink_line,
 507         }, {
 508                 .type    = "pipe",
 509                 .handler = cpio_mkpipe_line,
 510         }, {
 511                 .type    = "sock",
 512                 .handler = cpio_mksock_line,
 513         }, {
 514                 .type    = NULL,
 515                 .handler = NULL,
 516         }
 517 };
 518 
 519 #define LINE_SIZE (2 * PATH_MAX + 50)
 520 
 521 int main (int argc, char *argv[])
 522 {
 523         FILE *cpio_list;
 524         char line[LINE_SIZE];
 525         char *args, *type;
 526         int ec = 0;
 527         int line_nr = 0;
 528         const char *filename;
 529 
 530         default_mtime = time(NULL);
 531         while (1) {
 532                 int opt = getopt(argc, argv, "t:h");
 533                 char *invalid;
 534 
 535                 if (opt == -1)
 536                         break;
 537                 switch (opt) {
 538                 case 't':
 539                         default_mtime = strtol(optarg, &invalid, 10);
 540                         if (!*optarg || *invalid) {
 541                                 fprintf(stderr, "Invalid timestamp: %s\n",
 542                                                 optarg);
 543                                 usage(argv[0]);
 544                                 exit(1);
 545                         }
 546                         break;
 547                 case 'h':
 548                 case '?':
 549                         usage(argv[0]);
 550                         exit(opt == 'h' ? 0 : 1);
 551                 }
 552         }
 553 
 554         if (argc - optind != 1) {
 555                 usage(argv[0]);
 556                 exit(1);
 557         }
 558         filename = argv[optind];
 559         if (!strcmp(filename, "-"))
 560                 cpio_list = stdin;
 561         else if (!(cpio_list = fopen(filename, "r"))) {
 562                 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
 563                         filename, strerror(errno));
 564                 usage(argv[0]);
 565                 exit(1);
 566         }
 567 
 568         while (fgets(line, LINE_SIZE, cpio_list)) {
 569                 int type_idx;
 570                 size_t slen = strlen(line);
 571 
 572                 line_nr++;
 573 
 574                 if ('#' == *line) {
 575                         /* comment - skip to next line */
 576                         continue;
 577                 }
 578 
 579                 if (! (type = strtok(line, " \t"))) {
 580                         fprintf(stderr,
 581                                 "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
 582                                 line_nr, line);
 583                         ec = -1;
 584                         break;
 585                 }
 586 
 587                 if ('\n' == *type) {
 588                         /* a blank line */
 589                         continue;
 590                 }
 591 
 592                 if (slen == strlen(type)) {
 593                         /* must be an empty line */
 594                         continue;
 595                 }
 596 
 597                 if (! (args = strtok(NULL, "\n"))) {
 598                         fprintf(stderr,
 599                                 "ERROR: incorrect format, newline required line %d: '%s'\n",
 600                                 line_nr, line);
 601                         ec = -1;
 602                 }
 603 
 604                 for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
 605                         int rc;
 606                         if (! strcmp(line, file_handler_table[type_idx].type)) {
 607                                 if ((rc = file_handler_table[type_idx].handler(args))) {
 608                                         ec = rc;
 609                                         fprintf(stderr, " line %d\n", line_nr);
 610                                 }
 611                                 break;
 612                         }
 613                 }
 614 
 615                 if (NULL == file_handler_table[type_idx].type) {
 616                         fprintf(stderr, "unknown file type line %d: '%s'\n",
 617                                 line_nr, line);
 618                 }
 619         }
 620         if (ec == 0)
 621                 cpio_trailer();
 622 
 623         exit(ec);
 624 }

/* [<][>][^][v][top][bottom][index][help] */