migrate.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2018 K. Lange
  5. *
  6. * migrate - Relocate root filesystem to tmpfs
  7. *
  8. * Run as part of system startup to move the ext2 root ramdisk
  9. * into a flexible in-memory temporary filesystem, which allows
  10. * file creation and editing and is much faster than the using
  11. * the ext2 driver against the static in-memory ramdisk.
  12. *
  13. * Based on the original Python implementation.
  14. */
  15. #include <string.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <dirent.h>
  19. #include <unistd.h>
  20. #include <fcntl.h>
  21. #include <sys/stat.h>
  22. #include <sys/ioctl.h>
  23. #include <toaru/trace.h>
  24. #include <toaru/hashmap.h>
  25. #define TRACE_APP_NAME "migrate"
  26. #define TRACE_(...) do { \
  27. if (_splash) { char _trace_tmp[512]; sprintf(_trace_tmp, __VA_ARGS__); fprintf(_splash, ":%s", _trace_tmp); fflush(_splash); } \
  28. if (_debug) { TRACE(__VA_ARGS__); } \
  29. } while (0)
  30. #define CHUNK_SIZE 4096
  31. static int _debug = 0;
  32. static FILE * _splash = NULL;
  33. int tokenize(char * str, char * sep, char **buf) {
  34. char * pch_i;
  35. char * save_i;
  36. int argc = 0;
  37. pch_i = strtok_r(str,sep,&save_i);
  38. if (!pch_i) { return 0; }
  39. while (pch_i != NULL) {
  40. buf[argc] = (char *)pch_i;
  41. ++argc;
  42. pch_i = strtok_r(NULL,sep,&save_i);
  43. }
  44. buf[argc] = NULL;
  45. return argc;
  46. }
  47. void copy_link(char * source, char * dest, int mode, int uid, int gid) {
  48. //fprintf(stderr, "need to copy link %s to %s\n", source, dest);
  49. char tmp[1024];
  50. readlink(source, tmp, 1024);
  51. symlink(tmp, dest);
  52. //chmod(dest, mode); /* links don't have modes */
  53. chown(dest, uid, gid);
  54. }
  55. void copy_file(char * source, char * dest, int mode,int uid, int gid) {
  56. //fprintf(stderr, "need to copy file %s to %s %x\n", source, dest, mode);
  57. TRACE_("Copying %s...", dest);
  58. int d_fd = open(dest, O_WRONLY | O_CREAT, mode);
  59. int s_fd = open(source, O_RDONLY);
  60. ssize_t length;
  61. length = lseek(s_fd, 0, SEEK_END);
  62. lseek(s_fd, 0, SEEK_SET);
  63. //fprintf(stderr, "%d bytes to copy\n", length);
  64. char buf[CHUNK_SIZE];
  65. while (length > 0) {
  66. size_t r = read(s_fd, buf, length < CHUNK_SIZE ? length : CHUNK_SIZE);
  67. //fprintf(stderr, "copying %d bytes from %s to %s\n", r, source, dest);
  68. write(d_fd, buf, r);
  69. length -= r;
  70. //fprintf(stderr, "%d bytes remaining\n", length);
  71. }
  72. close(s_fd);
  73. close(d_fd);
  74. chown(dest, uid, gid);
  75. }
  76. void copy_directory(char * source, char * dest, int mode, int uid, int gid) {
  77. DIR * dirp = opendir(source);
  78. if (dirp == NULL) {
  79. fprintf(stderr, "Failed to copy directory %s\n", source);
  80. return;
  81. }
  82. TRACE_("Creating %s/...", dest);
  83. //fprintf(stderr, "Creating %s\n", dest);
  84. if (!strcmp(dest, "/")) {
  85. dest = "";
  86. } else {
  87. mkdir(dest, mode);
  88. }
  89. struct dirent * ent = readdir(dirp);
  90. while (ent != NULL) {
  91. if (!strcmp(ent->d_name,".") || !strcmp(ent->d_name,"..")) {
  92. //fprintf(stderr, "Skipping %s\n", ent->d_name);
  93. ent = readdir(dirp);
  94. continue;
  95. }
  96. //fprintf(stderr, "not skipping %s/%s → %s/%s\n", source, ent->d_name, dest, ent->d_name);
  97. struct stat statbuf;
  98. char tmp[strlen(source)+strlen(ent->d_name)+2];
  99. sprintf(tmp, "%s/%s", source, ent->d_name);
  100. char tmp2[strlen(dest)+strlen(ent->d_name)+2];
  101. sprintf(tmp2, "%s/%s", dest, ent->d_name);
  102. //fprintf(stderr,"%s → %s\n", tmp, tmp2);
  103. lstat(tmp,&statbuf);
  104. if (S_ISLNK(statbuf.st_mode)) {
  105. copy_link(tmp, tmp2, statbuf.st_mode & 07777, statbuf.st_uid, statbuf.st_gid);
  106. } else if (S_ISDIR(statbuf.st_mode)) {
  107. copy_directory(tmp, tmp2, statbuf.st_mode & 07777, statbuf.st_uid, statbuf.st_gid);
  108. } else if (S_ISREG(statbuf.st_mode)) {
  109. copy_file(tmp, tmp2, statbuf.st_mode & 07777, statbuf.st_uid, statbuf.st_gid);
  110. } else {
  111. fprintf(stderr, " %s is not any of the required file types?\n", tmp);
  112. }
  113. ent = readdir(dirp);
  114. }
  115. closedir(dirp);
  116. chown(dest, uid, gid);
  117. }
  118. void free_ramdisk(char * path) {
  119. int fd = open(path, O_RDONLY);
  120. ioctl(fd, 0x4001, NULL);
  121. close(fd);
  122. }
  123. hashmap_t * get_cmdline(void) {
  124. int fd = open("/proc/cmdline", O_RDONLY);
  125. char * out = malloc(1024);
  126. size_t r = read(fd, out, 1024);
  127. out[r] = '\0';
  128. if (out[r-1] == '\n') {
  129. out[r-1] = '\0';
  130. }
  131. char * arg = strdup(out);
  132. char * argv[1024];
  133. int argc = tokenize(arg, " ", argv);
  134. /* New let's parse the tokens into the arguments list so we can index by key */
  135. hashmap_t * args = hashmap_create(10);
  136. for (int i = 0; i < argc; ++i) {
  137. char * c = strdup(argv[i]);
  138. char * name;
  139. char * value;
  140. name = c;
  141. value = NULL;
  142. /* Find the first = and replace it with a null */
  143. char * v = c;
  144. while (*v) {
  145. if (*v == '=') {
  146. *v = '\0';
  147. v++;
  148. value = v;
  149. goto _break;
  150. }
  151. v++;
  152. }
  153. _break:
  154. hashmap_set(args, name, value);
  155. }
  156. free(arg);
  157. free(out);
  158. return args;
  159. }
  160. int main(int argc, char * argv[]) {
  161. hashmap_t * cmdline = get_cmdline();
  162. if (hashmap_has(cmdline, "logtoserial")) {
  163. _debug = 1;
  164. }
  165. _splash = fopen("/dev/pex/splash","r+");
  166. if (hashmap_has(cmdline, "root")) {
  167. TRACE_("Original root was %s", (char*)hashmap_get(cmdline, "root"));
  168. } else if (hashmap_has(cmdline,"init") && !strcmp(hashmap_get(cmdline,"init"),"/dev/ram0")) {
  169. TRACE_("Init is ram0, so this is probably a netboot image, going to assume root is /tmp/netboot.img");
  170. hashmap_set(cmdline,"root","/tmp/netboot.img");
  171. } else {
  172. TRACE_("Fatal: Don't know how to boot this. No root set.\n");
  173. return 1;
  174. }
  175. char * root = hashmap_get(cmdline,"root");
  176. char * start = hashmap_get(cmdline,"_start");
  177. if (!start) {
  178. start = "";
  179. }
  180. char * root_type = hashmap_get(cmdline,"root_type");
  181. if (!root_type) {
  182. root_type = "ext2";
  183. }
  184. char tmp[1024];
  185. TRACE_("Remounting root to /dev/base");
  186. sprintf(tmp, "mount %s %s /dev/base", root_type, root);
  187. system(tmp);
  188. TRACE_("Mounting tmpfs to /");
  189. system("mount tmpfs x,755 /");
  190. TRACE_("Migrating root...");
  191. copy_directory("/dev/base","/",0660,0,0);
  192. system("mount tmpfs x,755 /dev/base");
  193. if (strstr(root, "/dev/ram") != NULL) {
  194. char * tmp = strdup(root);
  195. char * c = strchr(tmp, ',');
  196. if (c) {
  197. *c = '\0';
  198. }
  199. TRACE_("Freeing ramdisk at %s", tmp);
  200. free_ramdisk(tmp);
  201. free(tmp);
  202. }
  203. return 0;
  204. }