Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gen_init_cpio.c
Go to the documentation of this file.
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 
23 static unsigned int offset;
24 static unsigned int ino = 721;
25 static time_t default_mtime;
26 
27 struct file_handler {
28  const char *type;
29  int (*handler)(const char *line);
30 };
31 
32 static void push_string(const char *name)
33 {
34  unsigned int name_len = strlen(name) + 1;
35 
36  fputs(name, stdout);
37  putchar(0);
38  offset += name_len;
39 }
40 
41 static void push_pad (void)
42 {
43  while (offset & 3) {
44  putchar(0);
45  offset++;
46  }
47 }
48 
49 static void push_rest(const char *name)
50 {
51  unsigned int name_len = strlen(name) + 1;
52  unsigned int tmp_ofs;
53 
54  fputs(name, stdout);
55  putchar(0);
56  offset += name_len;
57 
58  tmp_ofs = name_len + 110;
59  while (tmp_ofs & 3) {
60  putchar(0);
61  offset++;
62  tmp_ofs++;
63  }
64 }
65 
66 static void push_hdr(const char *s)
67 {
68  fputs(s, stdout);
69  offset += 110;
70 }
71 
72 static void cpio_trailer(void)
73 {
74  char s[256];
75  const char name[] = "TRAILER!!!";
76 
77  sprintf(s, "%s%08X%08X%08lX%08lX%08X%08lX"
78  "%08X%08X%08X%08X%08X%08X%08X",
79  "070701", /* magic */
80  0, /* ino */
81  0, /* mode */
82  (long) 0, /* uid */
83  (long) 0, /* gid */
84  1, /* nlink */
85  (long) 0, /* mtime */
86  0, /* filesize */
87  0, /* major */
88  0, /* minor */
89  0, /* rmajor */
90  0, /* rminor */
91  (unsigned)strlen(name)+1, /* namesize */
92  0); /* chksum */
93  push_hdr(s);
94  push_rest(name);
95 
96  while (offset % 512) {
97  putchar(0);
98  offset++;
99  }
100 }
101 
102 static int cpio_mkslink(const char *name, const char *target,
103  unsigned int mode, uid_t uid, gid_t gid)
104 {
105  char s[256];
106 
107  if (name[0] == '/')
108  name++;
109  sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
110  "%08X%08X%08X%08X%08X%08X%08X",
111  "070701", /* magic */
112  ino++, /* ino */
113  S_IFLNK | mode, /* mode */
114  (long) uid, /* uid */
115  (long) gid, /* gid */
116  1, /* nlink */
117  (long) default_mtime, /* mtime */
118  (unsigned)strlen(target)+1, /* filesize */
119  3, /* major */
120  1, /* minor */
121  0, /* rmajor */
122  0, /* rminor */
123  (unsigned)strlen(name) + 1,/* namesize */
124  0); /* chksum */
125  push_hdr(s);
126  push_string(name);
127  push_pad();
128  push_string(target);
129  push_pad();
130  return 0;
131 }
132 
133 static int cpio_mkslink_line(const char *line)
134 {
135  char name[PATH_MAX + 1];
136  char target[PATH_MAX + 1];
137  unsigned int mode;
138  int uid;
139  int gid;
140  int rc = -1;
141 
142  if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) {
143  fprintf(stderr, "Unrecognized dir format '%s'", line);
144  goto fail;
145  }
146  rc = cpio_mkslink(name, target, mode, uid, gid);
147  fail:
148  return rc;
149 }
150 
151 static int cpio_mkgeneric(const char *name, unsigned int mode,
152  uid_t uid, gid_t gid)
153 {
154  char s[256];
155 
156  if (name[0] == '/')
157  name++;
158  sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
159  "%08X%08X%08X%08X%08X%08X%08X",
160  "070701", /* magic */
161  ino++, /* ino */
162  mode, /* mode */
163  (long) uid, /* uid */
164  (long) gid, /* gid */
165  2, /* nlink */
166  (long) default_mtime, /* mtime */
167  0, /* filesize */
168  3, /* major */
169  1, /* minor */
170  0, /* rmajor */
171  0, /* rminor */
172  (unsigned)strlen(name) + 1,/* namesize */
173  0); /* chksum */
174  push_hdr(s);
175  push_rest(name);
176  return 0;
177 }
178 
183 };
184 
185 struct generic_type {
186  const char *type;
188 };
189 
190 static struct generic_type generic_type_table[] = {
191  [GT_DIR] = {
192  .type = "dir",
193  .mode = S_IFDIR
194  },
195  [GT_PIPE] = {
196  .type = "pipe",
197  .mode = S_IFIFO
198  },
199  [GT_SOCK] = {
200  .type = "sock",
201  .mode = S_IFSOCK
202  }
203 };
204 
205 static int cpio_mkgeneric_line(const char *line, enum generic_types gt)
206 {
207  char name[PATH_MAX + 1];
208  unsigned int mode;
209  int uid;
210  int gid;
211  int rc = -1;
212 
213  if (4 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d", name, &mode, &uid, &gid)) {
214  fprintf(stderr, "Unrecognized %s format '%s'",
215  line, generic_type_table[gt].type);
216  goto fail;
217  }
218  mode |= generic_type_table[gt].mode;
219  rc = cpio_mkgeneric(name, mode, uid, gid);
220  fail:
221  return rc;
222 }
223 
224 static int cpio_mkdir_line(const char *line)
225 {
226  return cpio_mkgeneric_line(line, GT_DIR);
227 }
228 
229 static int cpio_mkpipe_line(const char *line)
230 {
231  return cpio_mkgeneric_line(line, GT_PIPE);
232 }
233 
234 static int cpio_mksock_line(const char *line)
235 {
236  return cpio_mkgeneric_line(line, GT_SOCK);
237 }
238 
239 static int cpio_mknod(const char *name, unsigned int mode,
240  uid_t uid, gid_t gid, char dev_type,
241  unsigned int maj, unsigned int min)
242 {
243  char s[256];
244 
245  if (dev_type == 'b')
246  mode |= S_IFBLK;
247  else
248  mode |= S_IFCHR;
249 
250  if (name[0] == '/')
251  name++;
252  sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
253  "%08X%08X%08X%08X%08X%08X%08X",
254  "070701", /* magic */
255  ino++, /* ino */
256  mode, /* mode */
257  (long) uid, /* uid */
258  (long) gid, /* gid */
259  1, /* nlink */
260  (long) default_mtime, /* mtime */
261  0, /* filesize */
262  3, /* major */
263  1, /* minor */
264  maj, /* rmajor */
265  min, /* rminor */
266  (unsigned)strlen(name) + 1,/* namesize */
267  0); /* chksum */
268  push_hdr(s);
269  push_rest(name);
270  return 0;
271 }
272 
273 static int cpio_mknod_line(const char *line)
274 {
275  char name[PATH_MAX + 1];
276  unsigned int mode;
277  int uid;
278  int gid;
279  char dev_type;
280  unsigned int maj;
281  unsigned int min;
282  int rc = -1;
283 
284  if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",
285  name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
286  fprintf(stderr, "Unrecognized nod format '%s'", line);
287  goto fail;
288  }
289  rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
290  fail:
291  return rc;
292 }
293 
294 static int cpio_mkfile(const char *name, const char *location,
295  unsigned int mode, uid_t uid, gid_t gid,
296  unsigned int nlinks)
297 {
298  char s[256];
299  char *filebuf = NULL;
300  struct stat buf;
301  long size;
302  int file = -1;
303  int retval;
304  int rc = -1;
305  int namesize;
306  unsigned int i;
307 
308  mode |= S_IFREG;
309 
310  file = open (location, O_RDONLY);
311  if (file < 0) {
312  fprintf (stderr, "File %s could not be opened for reading\n", location);
313  goto error;
314  }
315 
316  retval = fstat(file, &buf);
317  if (retval) {
318  fprintf(stderr, "File %s could not be stat()'ed\n", location);
319  goto error;
320  }
321 
322  filebuf = malloc(buf.st_size);
323  if (!filebuf) {
324  fprintf (stderr, "out of memory\n");
325  goto error;
326  }
327 
328  retval = read (file, filebuf, buf.st_size);
329  if (retval < 0) {
330  fprintf (stderr, "Can not read %s file\n", location);
331  goto error;
332  }
333 
334  size = 0;
335  for (i = 1; i <= nlinks; i++) {
336  /* data goes on last link */
337  if (i == nlinks) size = buf.st_size;
338 
339  if (name[0] == '/')
340  name++;
341  namesize = strlen(name) + 1;
342  sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
343  "%08lX%08X%08X%08X%08X%08X%08X",
344  "070701", /* magic */
345  ino, /* ino */
346  mode, /* mode */
347  (long) uid, /* uid */
348  (long) gid, /* gid */
349  nlinks, /* nlink */
350  (long) buf.st_mtime, /* mtime */
351  size, /* filesize */
352  3, /* major */
353  1, /* minor */
354  0, /* rmajor */
355  0, /* rminor */
356  namesize, /* namesize */
357  0); /* chksum */
358  push_hdr(s);
359  push_string(name);
360  push_pad();
361 
362  if (size) {
363  if (fwrite(filebuf, size, 1, stdout) != 1) {
364  fprintf(stderr, "writing filebuf failed\n");
365  goto error;
366  }
367  offset += size;
368  push_pad();
369  }
370 
371  name += namesize;
372  }
373  ino++;
374  rc = 0;
375 
376 error:
377  if (filebuf) free(filebuf);
378  if (file >= 0) close(file);
379  return rc;
380 }
381 
382 static char *cpio_replace_env(char *new_location)
383 {
384  char expanded[PATH_MAX + 1];
385  char env_var[PATH_MAX + 1];
386  char *start;
387  char *end;
388 
389  for (start = NULL; (start = strstr(new_location, "${")); ) {
390  end = strchr(start, '}');
391  if (start < end) {
392  *env_var = *expanded = '\0';
393  strncat(env_var, start + 2, end - start - 2);
394  strncat(expanded, new_location, start - new_location);
395  strncat(expanded, getenv(env_var),
396  PATH_MAX - strlen(expanded));
397  strncat(expanded, end + 1,
398  PATH_MAX - strlen(expanded));
399  strncpy(new_location, expanded, PATH_MAX);
400  new_location[PATH_MAX] = 0;
401  } else
402  break;
403  }
404 
405  return new_location;
406 }
407 
408 
409 static int cpio_mkfile_line(const char *line)
410 {
411  char name[PATH_MAX + 1];
412  char *dname = NULL; /* malloc'ed buffer for hard links */
413  char location[PATH_MAX + 1];
414  unsigned int mode;
415  int uid;
416  int gid;
417  int nlinks = 1;
418  int end = 0, dname_len = 0;
419  int rc = -1;
420 
421  if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
422  "s %o %d %d %n",
423  name, location, &mode, &uid, &gid, &end)) {
424  fprintf(stderr, "Unrecognized file format '%s'", line);
425  goto fail;
426  }
427  if (end && isgraph(line[end])) {
428  int len;
429  int nend;
430 
431  dname = malloc(strlen(line));
432  if (!dname) {
433  fprintf (stderr, "out of memory (%d)\n", dname_len);
434  goto fail;
435  }
436 
437  dname_len = strlen(name) + 1;
438  memcpy(dname, name, dname_len);
439 
440  do {
441  nend = 0;
442  if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
443  name, &nend) < 1)
444  break;
445  len = strlen(name) + 1;
446  memcpy(dname + dname_len, name, len);
447  dname_len += len;
448  nlinks++;
449  end += nend;
450  } while (isgraph(line[end]));
451  } else {
452  dname = name;
453  }
454  rc = cpio_mkfile(dname, cpio_replace_env(location),
455  mode, uid, gid, nlinks);
456  fail:
457  if (dname_len) free(dname);
458  return rc;
459 }
460 
461 static void usage(const char *prog)
462 {
463  fprintf(stderr, "Usage:\n"
464  "\t%s [-t <timestamp>] <cpio_list>\n"
465  "\n"
466  "<cpio_list> is a file containing newline separated entries that\n"
467  "describe the files to be included in the initramfs archive:\n"
468  "\n"
469  "# a comment\n"
470  "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
471  "dir <name> <mode> <uid> <gid>\n"
472  "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
473  "slink <name> <target> <mode> <uid> <gid>\n"
474  "pipe <name> <mode> <uid> <gid>\n"
475  "sock <name> <mode> <uid> <gid>\n"
476  "\n"
477  "<name> name of the file/dir/nod/etc in the archive\n"
478  "<location> location of the file in the current filesystem\n"
479  " expands shell variables quoted with ${}\n"
480  "<target> link target\n"
481  "<mode> mode/permissions of the file\n"
482  "<uid> user id (0=root)\n"
483  "<gid> group id (0=root)\n"
484  "<dev_type> device type (b=block, c=character)\n"
485  "<maj> major number of nod\n"
486  "<min> minor number of nod\n"
487  "<hard links> space separated list of other links to file\n"
488  "\n"
489  "example:\n"
490  "# A simple initramfs\n"
491  "dir /dev 0755 0 0\n"
492  "nod /dev/console 0600 0 0 c 5 1\n"
493  "dir /root 0700 0 0\n"
494  "dir /sbin 0755 0 0\n"
495  "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n"
496  "\n"
497  "<timestamp> is time in seconds since Epoch that will be used\n"
498  "as mtime for symlinks, special files and directories. The default\n"
499  "is to use the current time for these entries.\n",
500  prog);
501 }
502 
504  {
505  .type = "file",
506  .handler = cpio_mkfile_line,
507  }, {
508  .type = "nod",
509  .handler = cpio_mknod_line,
510  }, {
511  .type = "dir",
512  .handler = cpio_mkdir_line,
513  }, {
514  .type = "slink",
515  .handler = cpio_mkslink_line,
516  }, {
517  .type = "pipe",
518  .handler = cpio_mkpipe_line,
519  }, {
520  .type = "sock",
521  .handler = cpio_mksock_line,
522  }, {
523  .type = NULL,
524  .handler = NULL,
525  }
526 };
527 
528 #define LINE_SIZE (2 * PATH_MAX + 50)
529 
530 int main (int argc, char *argv[])
531 {
532  FILE *cpio_list;
533  char line[LINE_SIZE];
534  char *args, *type;
535  int ec = 0;
536  int line_nr = 0;
537  const char *filename;
538 
539  default_mtime = time(NULL);
540  while (1) {
541  int opt = getopt(argc, argv, "t:h");
542  char *invalid;
543 
544  if (opt == -1)
545  break;
546  switch (opt) {
547  case 't':
548  default_mtime = strtol(optarg, &invalid, 10);
549  if (!*optarg || *invalid) {
550  fprintf(stderr, "Invalid timestamp: %s\n",
551  optarg);
552  usage(argv[0]);
553  exit(1);
554  }
555  break;
556  case 'h':
557  case '?':
558  usage(argv[0]);
559  exit(opt == 'h' ? 0 : 1);
560  }
561  }
562 
563  if (argc - optind != 1) {
564  usage(argv[0]);
565  exit(1);
566  }
567  filename = argv[optind];
568  if (!strcmp(filename, "-"))
569  cpio_list = stdin;
570  else if (!(cpio_list = fopen(filename, "r"))) {
571  fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
572  filename, strerror(errno));
573  usage(argv[0]);
574  exit(1);
575  }
576 
577  while (fgets(line, LINE_SIZE, cpio_list)) {
578  int type_idx;
579  size_t slen = strlen(line);
580 
581  line_nr++;
582 
583  if ('#' == *line) {
584  /* comment - skip to next line */
585  continue;
586  }
587 
588  if (! (type = strtok(line, " \t"))) {
589  fprintf(stderr,
590  "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
591  line_nr, line);
592  ec = -1;
593  break;
594  }
595 
596  if ('\n' == *type) {
597  /* a blank line */
598  continue;
599  }
600 
601  if (slen == strlen(type)) {
602  /* must be an empty line */
603  continue;
604  }
605 
606  if (! (args = strtok(NULL, "\n"))) {
607  fprintf(stderr,
608  "ERROR: incorrect format, newline required line %d: '%s'\n",
609  line_nr, line);
610  ec = -1;
611  }
612 
613  for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
614  int rc;
615  if (! strcmp(line, file_handler_table[type_idx].type)) {
616  if ((rc = file_handler_table[type_idx].handler(args))) {
617  ec = rc;
618  fprintf(stderr, " line %d\n", line_nr);
619  }
620  break;
621  }
622  }
623 
624  if (NULL == file_handler_table[type_idx].type) {
625  fprintf(stderr, "unknown file type line %d: '%s'\n",
626  line_nr, line);
627  }
628  }
629  if (ec == 0)
630  cpio_trailer();
631 
632  exit(ec);
633 }