0001
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
0018
0019
0020
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",
0085 0,
0086 0,
0087 (long) 0,
0088 (long) 0,
0089 1,
0090 (long) 0,
0091 0,
0092 0,
0093 0,
0094 0,
0095 0,
0096 (unsigned)strlen(name)+1,
0097 0);
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",
0117 ino++,
0118 S_IFLNK | mode,
0119 (long) uid,
0120 (long) gid,
0121 1,
0122 (long) default_mtime,
0123 (unsigned)strlen(target)+1,
0124 3,
0125 1,
0126 0,
0127 0,
0128 (unsigned)strlen(name) + 1,
0129 0);
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",
0166 ino++,
0167 mode,
0168 (long) uid,
0169 (long) gid,
0170 2,
0171 (long) default_mtime,
0172 0,
0173 3,
0174 1,
0175 0,
0176 0,
0177 (unsigned)strlen(name) + 1,
0178 0);
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",
0260 ino++,
0261 mode,
0262 (long) uid,
0263 (long) gid,
0264 1,
0265 (long) default_mtime,
0266 0,
0267 3,
0268 1,
0269 maj,
0270 min,
0271 (unsigned)strlen(name) + 1,
0272 0);
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
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
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",
0379 ino,
0380 mode,
0381 (long) uid,
0382 (long) gid,
0383 nlinks,
0384 (long) buf.st_mtime,
0385 size,
0386 3,
0387 1,
0388 0,
0389 0,
0390 namesize,
0391 size ? csum : 0);
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;
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
0604
0605
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
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
0647 continue;
0648 }
0649
0650 if (slen == strlen(type)) {
0651
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 }