mirror of the now-defunct rocklinux.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

214 lines
5.4 KiB

  1. /* ----------------------------------------------------------------------- *
  2. *
  3. * Copyright 2004-2006 H. Peter Anvin - All Rights Reserved
  4. *
  5. * Permission is hereby granted, free of charge, to any person
  6. * obtaining a copy of this software and associated documentation
  7. * files (the "Software"), to deal in the Software without
  8. * restriction, including without limitation the rights to use,
  9. * copy, modify, merge, publish, distribute, sublicense, and/or
  10. * sell copies of the Software, and to permit persons to whom
  11. * the Software is furnished to do so, subject to the following
  12. * conditions:
  13. *
  14. * The above copyright notice and this permission notice shall
  15. * be included in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  19. * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  23. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  24. * OTHER DEALINGS IN THE SOFTWARE.
  25. *
  26. * ----------------------------------------------------------------------- */
  27. /*
  28. * runinitlib.c
  29. *
  30. * run_init(consoledev, realroot, init, initargs)
  31. *
  32. * This function should be called as the last thing in kinit,
  33. * from initramfs, it does the following:
  34. *
  35. * - Delete all files in the initramfs;
  36. * - Remounts /real-root onto the root filesystem;
  37. * - Chroots;
  38. * - Opens /dev/console;
  39. * - Spawns the specified init program (with arguments.)
  40. */
  41. #include <alloca.h>
  42. #include <assert.h>
  43. #include <dirent.h>
  44. #include <errno.h>
  45. #include <fcntl.h>
  46. #include <string.h>
  47. #include <stdlib.h>
  48. #include <stdio.h>
  49. #include <unistd.h>
  50. #include <sys/mount.h>
  51. #include <sys/stat.h>
  52. #include <sys/types.h>
  53. #include <sys/vfs.h>
  54. #include "run-init.h"
  55. /* Make it possible to compile on glibc by including constants that the
  56. always-behind shipped glibc headers may not include. Classic example
  57. on why the lack of ABI headers screw us up. */
  58. #ifndef TMPFS_MAGIC
  59. # define TMPFS_MAGIC 0x01021994
  60. #endif
  61. #ifndef RAMFS_MAGIC
  62. # define RAMFS_MAGIC 0x858458f6
  63. #endif
  64. #ifndef MS_MOVE
  65. # define MS_MOVE 8192
  66. #endif
  67. static int nuke(const char *what);
  68. static int nuke_dirent(int len, const char *dir, const char *name, dev_t me)
  69. {
  70. int bytes = len+strlen(name)+2;
  71. char path[bytes];
  72. int xlen;
  73. struct stat st;
  74. xlen = snprintf(path, bytes, "%s/%s", dir, name);
  75. assert(xlen < bytes);
  76. if ( lstat(path, &st) )
  77. return ENOENT; /* Return 0 since already gone? */
  78. if ( st.st_dev != me )
  79. return 0; /* DO NOT recurse down mount points!!!!! */
  80. return nuke(path);
  81. }
  82. /* Wipe the contents of a directory, but not the directory itself */
  83. static int nuke_dir(const char *what)
  84. {
  85. int len = strlen(what);
  86. DIR *dir;
  87. struct dirent *d;
  88. int err = 0;
  89. struct stat st;
  90. if ( lstat(what, &st) )
  91. return errno;
  92. if ( !S_ISDIR(st.st_mode) )
  93. return ENOTDIR;
  94. if ( !(dir = opendir(what)) ) {
  95. /* EACCES means we can't read it. Might be empty and removable;
  96. if not, the rmdir() in nuke() will trigger an error. */
  97. return (errno == EACCES) ? 0 : errno;
  98. }
  99. while ( (d = readdir(dir)) ) {
  100. /* Skip . and .. */
  101. if ( d->d_name[0] == '.' &&
  102. (d->d_name[1] == '\0' ||
  103. (d->d_name[1] == '.' && d->d_name[2] == '\0')) )
  104. continue;
  105. err = nuke_dirent(len, what, d->d_name, st.st_dev);
  106. if ( err ) {
  107. closedir(dir);
  108. return err;
  109. }
  110. }
  111. closedir(dir);
  112. return 0;
  113. }
  114. static int nuke(const char *what)
  115. {
  116. int rv;
  117. int err = 0;
  118. rv = unlink(what);
  119. if ( rv < 0 ) {
  120. if ( errno == EISDIR ) {
  121. /* It's a directory. */
  122. err = nuke_dir(what);
  123. if ( !err ) err = rmdir(what) ? errno : err;
  124. } else {
  125. err = errno;
  126. }
  127. }
  128. if ( err ) {
  129. errno = err;
  130. die(what);
  131. } else {
  132. return 0;
  133. }
  134. }
  135. int run_init(const char *realroot, const char *console,
  136. const char *init, char **initargs)
  137. {
  138. struct stat rst, cst, ist;
  139. struct statfs sfs;
  140. int confd;
  141. /* First, change to the new root directory */
  142. if ( chdir(realroot) )
  143. die("chdir to new root");
  144. /* This is a potentially highly destructive program. Take some
  145. extra precautions. */
  146. /* Make sure the current directory is not on the same filesystem
  147. as the root directory */
  148. if ( stat("/", &rst) || stat(".", &cst) )
  149. die("stat");
  150. if ( rst.st_dev == cst.st_dev )
  151. die("current directory on the same filesystem as the root");
  152. /* The initramfs should have /init */
  153. if ( stat("/init", &ist) || !S_ISREG(ist.st_mode) )
  154. die("can't find /init on initramfs");
  155. /* Make sure we're on a ramfs */
  156. if ( statfs("/", &sfs) )
  157. die("statfs /");
  158. if ( sfs.f_type != RAMFS_MAGIC && sfs.f_type != TMPFS_MAGIC )
  159. die("rootfs not a ramfs or tmpfs");
  160. /* Okay, I think we should be safe... */
  161. /* Delete rootfs contents */
  162. if ( nuke_dir("/") )
  163. die("nuking initramfs contents");
  164. /* Overmount the root */
  165. if ( mount(".", "/", NULL, MS_MOVE, NULL) )
  166. die("overmounting root");
  167. /* chroot, chdir */
  168. if ( chroot(".") || chdir("/") )
  169. die("chroot");
  170. /* Open /dev/console */
  171. if ( (confd = open(console, O_RDWR)) < 0 )
  172. die("opening console");
  173. dup2(confd, 0);
  174. dup2(confd, 1);
  175. dup2(confd, 2);
  176. close(confd);
  177. /* Spawn init */
  178. execv(init, initargs);
  179. die(init); /* Failed to spawn init */
  180. }