gen_init_cpio.c 12 KB

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