Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <stdio.h>
0003 #include <stdlib.h>
0004 #include <stdint.h>
0005 #include <stdbool.h>
0006 #include <sys/types.h>
0007 #include <sys/stat.h>
0008 #include <string.h>
0009 #include <unistd.h>
0010 #include <time.h>
0011 #include <fcntl.h>
0012 #include <errno.h>
0013 #include <ctype.h>
0014 #include <limits.h>
0015 
0016 /*
0017  * Original work by Jeff Garzik
0018  *
0019  * External file lists, symlink, pipe and fifo support by Thayne Harbaugh
0020  * Hard link support by Luciano Rocha
0021  */
0022 
0023 #define xstr(s) #s
0024 #define str(s) xstr(s)
0025 #define MIN(a, b) ((a) < (b) ? (a) : (b))
0026 
0027 static unsigned int offset;
0028 static unsigned int ino = 721;
0029 static time_t default_mtime;
0030 static bool do_csum = false;
0031 
0032 struct file_handler {
0033     const char *type;
0034     int (*handler)(const char *line);
0035 };
0036 
0037 static void push_string(const char *name)
0038 {
0039     unsigned int name_len = strlen(name) + 1;
0040 
0041     fputs(name, stdout);
0042     putchar(0);
0043     offset += name_len;
0044 }
0045 
0046 static void push_pad (void)
0047 {
0048     while (offset & 3) {
0049         putchar(0);
0050         offset++;
0051     }
0052 }
0053 
0054 static void push_rest(const char *name)
0055 {
0056     unsigned int name_len = strlen(name) + 1;
0057     unsigned int tmp_ofs;
0058 
0059     fputs(name, stdout);
0060     putchar(0);
0061     offset += name_len;
0062 
0063     tmp_ofs = name_len + 110;
0064     while (tmp_ofs & 3) {
0065         putchar(0);
0066         offset++;
0067         tmp_ofs++;
0068     }
0069 }
0070 
0071 static void push_hdr(const char *s)
0072 {
0073     fputs(s, stdout);
0074     offset += 110;
0075 }
0076 
0077 static void cpio_trailer(void)
0078 {
0079     char s[256];
0080     const char name[] = "TRAILER!!!";
0081 
0082     sprintf(s, "%s%08X%08X%08lX%08lX%08X%08lX"
0083            "%08X%08X%08X%08X%08X%08X%08X",
0084         do_csum ? "070702" : "070701", /* magic */
0085         0,          /* ino */
0086         0,          /* mode */
0087         (long) 0,       /* uid */
0088         (long) 0,       /* gid */
0089         1,          /* nlink */
0090         (long) 0,       /* mtime */
0091         0,          /* filesize */
0092         0,          /* major */
0093         0,          /* minor */
0094         0,          /* rmajor */
0095         0,          /* rminor */
0096         (unsigned)strlen(name)+1, /* namesize */
0097         0);         /* chksum */
0098     push_hdr(s);
0099     push_rest(name);
0100 
0101     while (offset % 512) {
0102         putchar(0);
0103         offset++;
0104     }
0105 }
0106 
0107 static int cpio_mkslink(const char *name, const char *target,
0108              unsigned int mode, uid_t uid, gid_t gid)
0109 {
0110     char s[256];
0111 
0112     if (name[0] == '/')
0113         name++;
0114     sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
0115            "%08X%08X%08X%08X%08X%08X%08X",
0116         do_csum ? "070702" : "070701", /* magic */
0117         ino++,          /* ino */
0118         S_IFLNK | mode,     /* mode */
0119         (long) uid,     /* uid */
0120         (long) gid,     /* gid */
0121         1,          /* nlink */
0122         (long) default_mtime,   /* mtime */
0123         (unsigned)strlen(target)+1, /* filesize */
0124         3,          /* major */
0125         1,          /* minor */
0126         0,          /* rmajor */
0127         0,          /* rminor */
0128         (unsigned)strlen(name) + 1,/* namesize */
0129         0);         /* chksum */
0130     push_hdr(s);
0131     push_string(name);
0132     push_pad();
0133     push_string(target);
0134     push_pad();
0135     return 0;
0136 }
0137 
0138 static int cpio_mkslink_line(const char *line)
0139 {
0140     char name[PATH_MAX + 1];
0141     char target[PATH_MAX + 1];
0142     unsigned int mode;
0143     int uid;
0144     int gid;
0145     int rc = -1;
0146 
0147     if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) {
0148         fprintf(stderr, "Unrecognized dir format '%s'", line);
0149         goto fail;
0150     }
0151     rc = cpio_mkslink(name, target, mode, uid, gid);
0152  fail:
0153     return rc;
0154 }
0155 
0156 static int cpio_mkgeneric(const char *name, unsigned int mode,
0157                uid_t uid, gid_t gid)
0158 {
0159     char s[256];
0160 
0161     if (name[0] == '/')
0162         name++;
0163     sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
0164            "%08X%08X%08X%08X%08X%08X%08X",
0165         do_csum ? "070702" : "070701", /* magic */
0166         ino++,          /* ino */
0167         mode,           /* mode */
0168         (long) uid,     /* uid */
0169         (long) gid,     /* gid */
0170         2,          /* nlink */
0171         (long) default_mtime,   /* mtime */
0172         0,          /* filesize */
0173         3,          /* major */
0174         1,          /* minor */
0175         0,          /* rmajor */
0176         0,          /* rminor */
0177         (unsigned)strlen(name) + 1,/* namesize */
0178         0);         /* chksum */
0179     push_hdr(s);
0180     push_rest(name);
0181     return 0;
0182 }
0183 
0184 enum generic_types {
0185     GT_DIR,
0186     GT_PIPE,
0187     GT_SOCK
0188 };
0189 
0190 struct generic_type {
0191     const char *type;
0192     mode_t mode;
0193 };
0194 
0195 static const struct generic_type generic_type_table[] = {
0196     [GT_DIR] = {
0197         .type = "dir",
0198         .mode = S_IFDIR
0199     },
0200     [GT_PIPE] = {
0201         .type = "pipe",
0202         .mode = S_IFIFO
0203     },
0204     [GT_SOCK] = {
0205         .type = "sock",
0206         .mode = S_IFSOCK
0207     }
0208 };
0209 
0210 static int cpio_mkgeneric_line(const char *line, enum generic_types gt)
0211 {
0212     char name[PATH_MAX + 1];
0213     unsigned int mode;
0214     int uid;
0215     int gid;
0216     int rc = -1;
0217 
0218     if (4 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d", name, &mode, &uid, &gid)) {
0219         fprintf(stderr, "Unrecognized %s format '%s'",
0220             line, generic_type_table[gt].type);
0221         goto fail;
0222     }
0223     mode |= generic_type_table[gt].mode;
0224     rc = cpio_mkgeneric(name, mode, uid, gid);
0225  fail:
0226     return rc;
0227 }
0228 
0229 static int cpio_mkdir_line(const char *line)
0230 {
0231     return cpio_mkgeneric_line(line, GT_DIR);
0232 }
0233 
0234 static int cpio_mkpipe_line(const char *line)
0235 {
0236     return cpio_mkgeneric_line(line, GT_PIPE);
0237 }
0238 
0239 static int cpio_mksock_line(const char *line)
0240 {
0241     return cpio_mkgeneric_line(line, GT_SOCK);
0242 }
0243 
0244 static int cpio_mknod(const char *name, unsigned int mode,
0245                uid_t uid, gid_t gid, char dev_type,
0246                unsigned int maj, unsigned int min)
0247 {
0248     char s[256];
0249 
0250     if (dev_type == 'b')
0251         mode |= S_IFBLK;
0252     else
0253         mode |= S_IFCHR;
0254 
0255     if (name[0] == '/')
0256         name++;
0257     sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
0258            "%08X%08X%08X%08X%08X%08X%08X",
0259         do_csum ? "070702" : "070701", /* magic */
0260         ino++,          /* ino */
0261         mode,           /* mode */
0262         (long) uid,     /* uid */
0263         (long) gid,     /* gid */
0264         1,          /* nlink */
0265         (long) default_mtime,   /* mtime */
0266         0,          /* filesize */
0267         3,          /* major */
0268         1,          /* minor */
0269         maj,            /* rmajor */
0270         min,            /* rminor */
0271         (unsigned)strlen(name) + 1,/* namesize */
0272         0);         /* chksum */
0273     push_hdr(s);
0274     push_rest(name);
0275     return 0;
0276 }
0277 
0278 static int cpio_mknod_line(const char *line)
0279 {
0280     char name[PATH_MAX + 1];
0281     unsigned int mode;
0282     int uid;
0283     int gid;
0284     char dev_type;
0285     unsigned int maj;
0286     unsigned int min;
0287     int rc = -1;
0288 
0289     if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",
0290              name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
0291         fprintf(stderr, "Unrecognized nod format '%s'", line);
0292         goto fail;
0293     }
0294     rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
0295  fail:
0296     return rc;
0297 }
0298 
0299 static int cpio_mkfile_csum(int fd, unsigned long size, uint32_t *csum)
0300 {
0301     while (size) {
0302         unsigned char filebuf[65536];
0303         ssize_t this_read;
0304         size_t i, this_size = MIN(size, sizeof(filebuf));
0305 
0306         this_read = read(fd, filebuf, this_size);
0307         if (this_read <= 0 || this_read > this_size)
0308             return -1;
0309 
0310         for (i = 0; i < this_read; i++)
0311             *csum += filebuf[i];
0312 
0313         size -= this_read;
0314     }
0315     /* seek back to the start for data segment I/O */
0316     if (lseek(fd, 0, SEEK_SET) < 0)
0317         return -1;
0318 
0319     return 0;
0320 }
0321 
0322 static int cpio_mkfile(const char *name, const char *location,
0323             unsigned int mode, uid_t uid, gid_t gid,
0324             unsigned int nlinks)
0325 {
0326     char s[256];
0327     struct stat buf;
0328     unsigned long size;
0329     int file = -1;
0330     int retval;
0331     int rc = -1;
0332     int namesize;
0333     unsigned int i;
0334     uint32_t csum = 0;
0335 
0336     mode |= S_IFREG;
0337 
0338     file = open (location, O_RDONLY);
0339     if (file < 0) {
0340         fprintf (stderr, "File %s could not be opened for reading\n", location);
0341         goto error;
0342     }
0343 
0344     retval = fstat(file, &buf);
0345     if (retval) {
0346         fprintf(stderr, "File %s could not be stat()'ed\n", location);
0347         goto error;
0348     }
0349 
0350     if (buf.st_mtime > 0xffffffff) {
0351         fprintf(stderr, "%s: Timestamp exceeds maximum cpio timestamp, clipping.\n",
0352             location);
0353         buf.st_mtime = 0xffffffff;
0354     }
0355 
0356     if (buf.st_size > 0xffffffff) {
0357         fprintf(stderr, "%s: Size exceeds maximum cpio file size\n",
0358             location);
0359         goto error;
0360     }
0361 
0362     if (do_csum && cpio_mkfile_csum(file, buf.st_size, &csum) < 0) {
0363         fprintf(stderr, "Failed to checksum file %s\n", location);
0364         goto error;
0365     }
0366 
0367     size = 0;
0368     for (i = 1; i <= nlinks; i++) {
0369         /* data goes on last link */
0370         if (i == nlinks)
0371             size = buf.st_size;
0372 
0373         if (name[0] == '/')
0374             name++;
0375         namesize = strlen(name) + 1;
0376         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
0377                "%08lX%08X%08X%08X%08X%08X%08X",
0378             do_csum ? "070702" : "070701", /* magic */
0379             ino,            /* ino */
0380             mode,           /* mode */
0381             (long) uid,     /* uid */
0382             (long) gid,     /* gid */
0383             nlinks,         /* nlink */
0384             (long) buf.st_mtime,    /* mtime */
0385             size,           /* filesize */
0386             3,          /* major */
0387             1,          /* minor */
0388             0,          /* rmajor */
0389             0,          /* rminor */
0390             namesize,       /* namesize */
0391             size ? csum : 0);   /* chksum */
0392         push_hdr(s);
0393         push_string(name);
0394         push_pad();
0395 
0396         while (size) {
0397             unsigned char filebuf[65536];
0398             ssize_t this_read;
0399             size_t this_size = MIN(size, sizeof(filebuf));
0400 
0401             this_read = read(file, filebuf, this_size);
0402             if (this_read <= 0 || this_read > this_size) {
0403                 fprintf(stderr, "Can not read %s file\n", location);
0404                 goto error;
0405             }
0406 
0407             if (fwrite(filebuf, this_read, 1, stdout) != 1) {
0408                 fprintf(stderr, "writing filebuf failed\n");
0409                 goto error;
0410             }
0411             offset += this_read;
0412             size -= this_read;
0413         }
0414         push_pad();
0415 
0416         name += namesize;
0417     }
0418     ino++;
0419     rc = 0;
0420 
0421 error:
0422     if (file >= 0)
0423         close(file);
0424     return rc;
0425 }
0426 
0427 static char *cpio_replace_env(char *new_location)
0428 {
0429     char expanded[PATH_MAX + 1];
0430     char *start, *end, *var;
0431 
0432     while ((start = strstr(new_location, "${")) &&
0433            (end = strchr(start + 2, '}'))) {
0434         *start = *end = 0;
0435         var = getenv(start + 2);
0436         snprintf(expanded, sizeof expanded, "%s%s%s",
0437              new_location, var ? var : "", end + 1);
0438         strcpy(new_location, expanded);
0439     }
0440 
0441     return new_location;
0442 }
0443 
0444 static int cpio_mkfile_line(const char *line)
0445 {
0446     char name[PATH_MAX + 1];
0447     char *dname = NULL; /* malloc'ed buffer for hard links */
0448     char location[PATH_MAX + 1];
0449     unsigned int mode;
0450     int uid;
0451     int gid;
0452     int nlinks = 1;
0453     int end = 0, dname_len = 0;
0454     int rc = -1;
0455 
0456     if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
0457                 "s %o %d %d %n",
0458                 name, location, &mode, &uid, &gid, &end)) {
0459         fprintf(stderr, "Unrecognized file format '%s'", line);
0460         goto fail;
0461     }
0462     if (end && isgraph(line[end])) {
0463         int len;
0464         int nend;
0465 
0466         dname = malloc(strlen(line));
0467         if (!dname) {
0468             fprintf (stderr, "out of memory (%d)\n", dname_len);
0469             goto fail;
0470         }
0471 
0472         dname_len = strlen(name) + 1;
0473         memcpy(dname, name, dname_len);
0474 
0475         do {
0476             nend = 0;
0477             if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
0478                     name, &nend) < 1)
0479                 break;
0480             len = strlen(name) + 1;
0481             memcpy(dname + dname_len, name, len);
0482             dname_len += len;
0483             nlinks++;
0484             end += nend;
0485         } while (isgraph(line[end]));
0486     } else {
0487         dname = name;
0488     }
0489     rc = cpio_mkfile(dname, cpio_replace_env(location),
0490                      mode, uid, gid, nlinks);
0491  fail:
0492     if (dname_len) free(dname);
0493     return rc;
0494 }
0495 
0496 static void usage(const char *prog)
0497 {
0498     fprintf(stderr, "Usage:\n"
0499         "\t%s [-t <timestamp>] [-c] <cpio_list>\n"
0500         "\n"
0501         "<cpio_list> is a file containing newline separated entries that\n"
0502         "describe the files to be included in the initramfs archive:\n"
0503         "\n"
0504         "# a comment\n"
0505         "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
0506         "dir <name> <mode> <uid> <gid>\n"
0507         "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
0508         "slink <name> <target> <mode> <uid> <gid>\n"
0509         "pipe <name> <mode> <uid> <gid>\n"
0510         "sock <name> <mode> <uid> <gid>\n"
0511         "\n"
0512         "<name>       name of the file/dir/nod/etc in the archive\n"
0513         "<location>   location of the file in the current filesystem\n"
0514         "             expands shell variables quoted with ${}\n"
0515         "<target>     link target\n"
0516         "<mode>       mode/permissions of the file\n"
0517         "<uid>        user id (0=root)\n"
0518         "<gid>        group id (0=root)\n"
0519         "<dev_type>   device type (b=block, c=character)\n"
0520         "<maj>        major number of nod\n"
0521         "<min>        minor number of nod\n"
0522         "<hard links> space separated list of other links to file\n"
0523         "\n"
0524         "example:\n"
0525         "# A simple initramfs\n"
0526         "dir /dev 0755 0 0\n"
0527         "nod /dev/console 0600 0 0 c 5 1\n"
0528         "dir /root 0700 0 0\n"
0529         "dir /sbin 0755 0 0\n"
0530         "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n"
0531         "\n"
0532         "<timestamp> is time in seconds since Epoch that will be used\n"
0533         "as mtime for symlinks, special files and directories. The default\n"
0534         "is to use the current time for these entries.\n"
0535         "-c: calculate and store 32-bit checksums for file data.\n",
0536         prog);
0537 }
0538 
0539 static const struct file_handler file_handler_table[] = {
0540     {
0541         .type    = "file",
0542         .handler = cpio_mkfile_line,
0543     }, {
0544         .type    = "nod",
0545         .handler = cpio_mknod_line,
0546     }, {
0547         .type    = "dir",
0548         .handler = cpio_mkdir_line,
0549     }, {
0550         .type    = "slink",
0551         .handler = cpio_mkslink_line,
0552     }, {
0553         .type    = "pipe",
0554         .handler = cpio_mkpipe_line,
0555     }, {
0556         .type    = "sock",
0557         .handler = cpio_mksock_line,
0558     }, {
0559         .type    = NULL,
0560         .handler = NULL,
0561     }
0562 };
0563 
0564 #define LINE_SIZE (2 * PATH_MAX + 50)
0565 
0566 int main (int argc, char *argv[])
0567 {
0568     FILE *cpio_list;
0569     char line[LINE_SIZE];
0570     char *args, *type;
0571     int ec = 0;
0572     int line_nr = 0;
0573     const char *filename;
0574 
0575     default_mtime = time(NULL);
0576     while (1) {
0577         int opt = getopt(argc, argv, "t:ch");
0578         char *invalid;
0579 
0580         if (opt == -1)
0581             break;
0582         switch (opt) {
0583         case 't':
0584             default_mtime = strtol(optarg, &invalid, 10);
0585             if (!*optarg || *invalid) {
0586                 fprintf(stderr, "Invalid timestamp: %s\n",
0587                         optarg);
0588                 usage(argv[0]);
0589                 exit(1);
0590             }
0591             break;
0592         case 'c':
0593             do_csum = true;
0594             break;
0595         case 'h':
0596         case '?':
0597             usage(argv[0]);
0598             exit(opt == 'h' ? 0 : 1);
0599         }
0600     }
0601 
0602     /*
0603      * Timestamps after 2106-02-07 06:28:15 UTC have an ascii hex time_t
0604      * representation that exceeds 8 chars and breaks the cpio header
0605      * specification.
0606      */
0607     if (default_mtime > 0xffffffff) {
0608         fprintf(stderr, "ERROR: Timestamp too large for cpio format\n");
0609         exit(1);
0610     }
0611 
0612     if (argc - optind != 1) {
0613         usage(argv[0]);
0614         exit(1);
0615     }
0616     filename = argv[optind];
0617     if (!strcmp(filename, "-"))
0618         cpio_list = stdin;
0619     else if (!(cpio_list = fopen(filename, "r"))) {
0620         fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
0621             filename, strerror(errno));
0622         usage(argv[0]);
0623         exit(1);
0624     }
0625 
0626     while (fgets(line, LINE_SIZE, cpio_list)) {
0627         int type_idx;
0628         size_t slen = strlen(line);
0629 
0630         line_nr++;
0631 
0632         if ('#' == *line) {
0633             /* comment - skip to next line */
0634             continue;
0635         }
0636 
0637         if (! (type = strtok(line, " \t"))) {
0638             fprintf(stderr,
0639                 "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
0640                 line_nr, line);
0641             ec = -1;
0642             break;
0643         }
0644 
0645         if ('\n' == *type) {
0646             /* a blank line */
0647             continue;
0648         }
0649 
0650         if (slen == strlen(type)) {
0651             /* must be an empty line */
0652             continue;
0653         }
0654 
0655         if (! (args = strtok(NULL, "\n"))) {
0656             fprintf(stderr,
0657                 "ERROR: incorrect format, newline required line %d: '%s'\n",
0658                 line_nr, line);
0659             ec = -1;
0660         }
0661 
0662         for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
0663             int rc;
0664             if (! strcmp(line, file_handler_table[type_idx].type)) {
0665                 if ((rc = file_handler_table[type_idx].handler(args))) {
0666                     ec = rc;
0667                     fprintf(stderr, " line %d\n", line_nr);
0668                 }
0669                 break;
0670             }
0671         }
0672 
0673         if (NULL == file_handler_table[type_idx].type) {
0674             fprintf(stderr, "unknown file type line %d: '%s'\n",
0675                 line_nr, line);
0676         }
0677     }
0678     if (ec == 0)
0679         cpio_trailer();
0680 
0681     exit(ec);
0682 }