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
26 struct file_handler {
27 const char *type;
28 int (*handler)(const char *line);
29 };
30
push_string(const char * name)31 static void push_string(const char *name)
32 {
33 unsigned int name_len = strlen(name) + 1;
34
35 fputs(name, stdout);
36 putchar(0);
37 offset += name_len;
38 }
39
push_pad(void)40 static void push_pad (void)
41 {
42 while (offset & 3) {
43 putchar(0);
44 offset++;
45 }
46 }
47
push_rest(const char * name)48 static void push_rest(const char *name)
49 {
50 unsigned int name_len = strlen(name) + 1;
51 unsigned int tmp_ofs;
52
53 fputs(name, stdout);
54 putchar(0);
55 offset += name_len;
56
57 tmp_ofs = name_len + 110;
58 while (tmp_ofs & 3) {
59 putchar(0);
60 offset++;
61 tmp_ofs++;
62 }
63 }
64
push_hdr(const char * s)65 static void push_hdr(const char *s)
66 {
67 fputs(s, stdout);
68 offset += 110;
69 }
70
cpio_trailer(void)71 static void cpio_trailer(void)
72 {
73 char s[256];
74 const char name[] = "TRAILER!!!";
75
76 sprintf(s, "%s%08X%08X%08lX%08lX%08X%08lX"
77 "%08X%08X%08X%08X%08X%08X%08X",
78 "070701", /* magic */
79 0, /* ino */
80 0, /* mode */
81 (long) 0, /* uid */
82 (long) 0, /* gid */
83 1, /* nlink */
84 (long) 0, /* mtime */
85 0, /* filesize */
86 0, /* major */
87 0, /* minor */
88 0, /* rmajor */
89 0, /* rminor */
90 (unsigned)strlen(name)+1, /* namesize */
91 0); /* chksum */
92 push_hdr(s);
93 push_rest(name);
94
95 while (offset % 512) {
96 putchar(0);
97 offset++;
98 }
99 }
100
cpio_mkslink(const char * name,const char * target,unsigned int mode,uid_t uid,gid_t gid)101 static int cpio_mkslink(const char *name, const char *target,
102 unsigned int mode, uid_t uid, gid_t gid)
103 {
104 char s[256];
105 time_t mtime = time(NULL);
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) 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
cpio_mkslink_line(const char * line)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
cpio_mkgeneric(const char * name,unsigned int mode,uid_t uid,gid_t gid)151 static int cpio_mkgeneric(const char *name, unsigned int mode,
152 uid_t uid, gid_t gid)
153 {
154 char s[256];
155 time_t mtime = time(NULL);
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) 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
cpio_mkgeneric_line(const char * line,enum generic_types gt)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
cpio_mkdir_line(const char * line)225 static int cpio_mkdir_line(const char *line)
226 {
227 return cpio_mkgeneric_line(line, GT_DIR);
228 }
229
cpio_mkpipe_line(const char * line)230 static int cpio_mkpipe_line(const char *line)
231 {
232 return cpio_mkgeneric_line(line, GT_PIPE);
233 }
234
cpio_mksock_line(const char * line)235 static int cpio_mksock_line(const char *line)
236 {
237 return cpio_mkgeneric_line(line, GT_SOCK);
238 }
239
cpio_mknod(const char * name,unsigned int mode,uid_t uid,gid_t gid,char dev_type,unsigned int maj,unsigned int min)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 time_t mtime = time(NULL);
246
247 if (dev_type == 'b')
248 mode |= S_IFBLK;
249 else
250 mode |= S_IFCHR;
251
252 if (name[0] == '/')
253 name++;
254 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
255 "%08X%08X%08X%08X%08X%08X%08X",
256 "070701", /* magic */
257 ino++, /* ino */
258 mode, /* mode */
259 (long) uid, /* uid */
260 (long) gid, /* gid */
261 1, /* nlink */
262 (long) mtime, /* mtime */
263 0, /* filesize */
264 3, /* major */
265 1, /* minor */
266 maj, /* rmajor */
267 min, /* rminor */
268 (unsigned)strlen(name) + 1,/* namesize */
269 0); /* chksum */
270 push_hdr(s);
271 push_rest(name);
272 return 0;
273 }
274
cpio_mknod_line(const char * line)275 static int cpio_mknod_line(const char *line)
276 {
277 char name[PATH_MAX + 1];
278 unsigned int mode;
279 int uid;
280 int gid;
281 char dev_type;
282 unsigned int maj;
283 unsigned int min;
284 int rc = -1;
285
286 if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",
287 name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
288 fprintf(stderr, "Unrecognized nod format '%s'", line);
289 goto fail;
290 }
291 rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
292 fail:
293 return rc;
294 }
295
cpio_mkfile(const char * name,const char * location,unsigned int mode,uid_t uid,gid_t gid,unsigned int nlinks)296 static int cpio_mkfile(const char *name, const char *location,
297 unsigned int mode, uid_t uid, gid_t gid,
298 unsigned int nlinks)
299 {
300 char s[256];
301 char *filebuf = NULL;
302 struct stat buf;
303 long size;
304 int file = -1;
305 int retval;
306 int rc = -1;
307 int namesize;
308 int i;
309
310 mode |= S_IFREG;
311
312 file = open (location, O_RDONLY);
313 if (file < 0) {
314 fprintf (stderr, "File %s could not be opened for reading\n", location);
315 goto error;
316 }
317
318 retval = fstat(file, &buf);
319 if (retval) {
320 fprintf(stderr, "File %s could not be stat()'ed\n", location);
321 goto error;
322 }
323
324 filebuf = malloc(buf.st_size);
325 if (!filebuf) {
326 fprintf (stderr, "out of memory\n");
327 goto error;
328 }
329
330 retval = read (file, filebuf, buf.st_size);
331 if (retval < 0) {
332 fprintf (stderr, "Can not read %s file\n", location);
333 goto error;
334 }
335
336 size = 0;
337 for (i = 1; i <= nlinks; i++) {
338 /* data goes on last link */
339 if (i == nlinks) size = buf.st_size;
340
341 if (name[0] == '/')
342 name++;
343 namesize = strlen(name) + 1;
344 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
345 "%08lX%08X%08X%08X%08X%08X%08X",
346 "070701", /* magic */
347 ino, /* ino */
348 mode, /* mode */
349 (long) uid, /* uid */
350 (long) gid, /* gid */
351 nlinks, /* nlink */
352 (long) buf.st_mtime, /* mtime */
353 size, /* filesize */
354 3, /* major */
355 1, /* minor */
356 0, /* rmajor */
357 0, /* rminor */
358 namesize, /* namesize */
359 0); /* chksum */
360 push_hdr(s);
361 push_string(name);
362 push_pad();
363
364 if (size) {
365 if (fwrite(filebuf, size, 1, stdout) != 1) {
366 fprintf(stderr, "writing filebuf failed\n");
367 goto error;
368 }
369 offset += size;
370 push_pad();
371 }
372
373 name += namesize;
374 }
375 ino++;
376 rc = 0;
377
378 error:
379 if (filebuf) free(filebuf);
380 if (file >= 0) close(file);
381 return rc;
382 }
383
cpio_replace_env(char * new_location)384 static char *cpio_replace_env(char *new_location)
385 {
386 char expanded[PATH_MAX + 1];
387 char env_var[PATH_MAX + 1];
388 char *start;
389 char *end;
390
391 for (start = NULL; (start = strstr(new_location, "${")); ) {
392 end = strchr(start, '}');
393 if (start < end) {
394 *env_var = *expanded = '\0';
395 strncat(env_var, start + 2, end - start - 2);
396 strncat(expanded, new_location, start - new_location);
397 strncat(expanded, getenv(env_var), PATH_MAX);
398 strncat(expanded, end + 1, PATH_MAX);
399 strncpy(new_location, expanded, PATH_MAX);
400 } else
401 break;
402 }
403
404 return new_location;
405 }
406
407
cpio_mkfile_line(const char * line)408 static int cpio_mkfile_line(const char *line)
409 {
410 char name[PATH_MAX + 1];
411 char *dname = NULL; /* malloc'ed buffer for hard links */
412 char location[PATH_MAX + 1];
413 unsigned int mode;
414 int uid;
415 int gid;
416 int nlinks = 1;
417 int end = 0, dname_len = 0;
418 int rc = -1;
419
420 if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
421 "s %o %d %d %n",
422 name, location, &mode, &uid, &gid, &end)) {
423 fprintf(stderr, "Unrecognized file format '%s'", line);
424 goto fail;
425 }
426 if (end && isgraph(line[end])) {
427 int len;
428 int nend;
429
430 dname = malloc(strlen(line));
431 if (!dname) {
432 fprintf (stderr, "out of memory (%d)\n", dname_len);
433 goto fail;
434 }
435
436 dname_len = strlen(name) + 1;
437 memcpy(dname, name, dname_len);
438
439 do {
440 nend = 0;
441 if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
442 name, &nend) < 1)
443 break;
444 len = strlen(name) + 1;
445 memcpy(dname + dname_len, name, len);
446 dname_len += len;
447 nlinks++;
448 end += nend;
449 } while (isgraph(line[end]));
450 } else {
451 dname = name;
452 }
453 rc = cpio_mkfile(dname, cpio_replace_env(location),
454 mode, uid, gid, nlinks);
455 fail:
456 if (dname_len) free(dname);
457 return rc;
458 }
459
usage(const char * prog)460 static void usage(const char *prog)
461 {
462 fprintf(stderr, "Usage:\n"
463 "\t%s <cpio_list>\n"
464 "\n"
465 "<cpio_list> is a file containing newline separated entries that\n"
466 "describe the files to be included in the initramfs archive:\n"
467 "\n"
468 "# a comment\n"
469 "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
470 "dir <name> <mode> <uid> <gid>\n"
471 "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
472 "slink <name> <target> <mode> <uid> <gid>\n"
473 "pipe <name> <mode> <uid> <gid>\n"
474 "sock <name> <mode> <uid> <gid>\n"
475 "\n"
476 "<name> name of the file/dir/nod/etc in the archive\n"
477 "<location> location of the file in the current filesystem\n"
478 " expands shell variables quoted with ${}\n"
479 "<target> link target\n"
480 "<mode> mode/permissions of the file\n"
481 "<uid> user id (0=root)\n"
482 "<gid> group id (0=root)\n"
483 "<dev_type> device type (b=block, c=character)\n"
484 "<maj> major number of nod\n"
485 "<min> minor number of nod\n"
486 "<hard links> space separated list of other links to file\n"
487 "\n"
488 "example:\n"
489 "# A simple initramfs\n"
490 "dir /dev 0755 0 0\n"
491 "nod /dev/console 0600 0 0 c 5 1\n"
492 "dir /root 0700 0 0\n"
493 "dir /sbin 0755 0 0\n"
494 "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n",
495 prog);
496 }
497
498 struct file_handler file_handler_table[] = {
499 {
500 .type = "file",
501 .handler = cpio_mkfile_line,
502 }, {
503 .type = "nod",
504 .handler = cpio_mknod_line,
505 }, {
506 .type = "dir",
507 .handler = cpio_mkdir_line,
508 }, {
509 .type = "slink",
510 .handler = cpio_mkslink_line,
511 }, {
512 .type = "pipe",
513 .handler = cpio_mkpipe_line,
514 }, {
515 .type = "sock",
516 .handler = cpio_mksock_line,
517 }, {
518 .type = NULL,
519 .handler = NULL,
520 }
521 };
522
523 #define LINE_SIZE (2 * PATH_MAX + 50)
524
main(int argc,char * argv[])525 int main (int argc, char *argv[])
526 {
527 FILE *cpio_list;
528 char line[LINE_SIZE];
529 char *args, *type;
530 int ec = 0;
531 int line_nr = 0;
532
533 if (2 != argc) {
534 usage(argv[0]);
535 exit(1);
536 }
537
538 if (!strcmp(argv[1], "-"))
539 cpio_list = stdin;
540 else if (! (cpio_list = fopen(argv[1], "r"))) {
541 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
542 argv[1], strerror(errno));
543 usage(argv[0]);
544 exit(1);
545 }
546
547 while (fgets(line, LINE_SIZE, cpio_list)) {
548 int type_idx;
549 size_t slen = strlen(line);
550
551 line_nr++;
552
553 if ('#' == *line) {
554 /* comment - skip to next line */
555 continue;
556 }
557
558 if (! (type = strtok(line, " \t"))) {
559 fprintf(stderr,
560 "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
561 line_nr, line);
562 ec = -1;
563 break;
564 }
565
566 if ('\n' == *type) {
567 /* a blank line */
568 continue;
569 }
570
571 if (slen == strlen(type)) {
572 /* must be an empty line */
573 continue;
574 }
575
576 if (! (args = strtok(NULL, "\n"))) {
577 fprintf(stderr,
578 "ERROR: incorrect format, newline required line %d: '%s'\n",
579 line_nr, line);
580 ec = -1;
581 }
582
583 for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
584 int rc;
585 if (! strcmp(line, file_handler_table[type_idx].type)) {
586 if ((rc = file_handler_table[type_idx].handler(args))) {
587 ec = rc;
588 fprintf(stderr, " line %d\n", line_nr);
589 }
590 break;
591 }
592 }
593
594 if (NULL == file_handler_table[type_idx].type) {
595 fprintf(stderr, "unknown file type line %d: '%s'\n",
596 line_nr, line);
597 }
598 }
599 if (ec == 0)
600 cpio_trailer();
601
602 exit(ec);
603 }
604