Back to home page

LXR

 
 

    


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