logo

bootstrap-initrd

Linux initrd to bootstrap from a small binary seed git clone https://hacktivis.me/git/bootstrap-initrd.git

cp-stub.c (7066B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #define _GNU_SOURCE // copy_file_range
  6. #include <assert.h>
  7. #include <errno.h>
  8. #include <fcntl.h> // linkat, AT_SYMLINK_FOLLOW
  9. #include <limits.h> // PATH_MAX
  10. #include <stdbool.h>
  11. #include <stdio.h> // fprintf
  12. #include <string.h> // strerror
  13. #include <sys/stat.h>
  14. #include <unistd.h> // getopt, copy_file_range
  15. bool preserve_metadata = false, opt_R = false;
  16. bool follow_src_symlinks = false;
  17. mode_t mode = 00755;
  18. char *argv0 = "cp";
  19. // Copied from utils-std/lib/path.c remove on de-stubbing
  20. static char *
  21. static_basename(char *path)
  22. {
  23. char *sep = strrchr(path, '/');
  24. return (sep == NULL) ? path : sep + 1;
  25. }
  26. static int
  27. do_copy(char *src, char *dest, bool multi_src)
  28. {
  29. assert(errno == 0);
  30. struct stat src_stat;
  31. if(fstatat(AT_FDCWD, src, &src_stat, follow_src_symlinks ? 0 : AT_SYMLINK_NOFOLLOW) < 0)
  32. {
  33. fprintf(
  34. stderr, "%s: Failed getting status for source '%s': %s\n", argv0, dest, strerror(errno));
  35. return -1;
  36. }
  37. struct stat dest_stat;
  38. if(stat(dest, &dest_stat) < 0)
  39. {
  40. if(errno == ENOENT)
  41. {
  42. errno = 0;
  43. }
  44. else
  45. {
  46. fprintf(stderr,
  47. "%s: Failed getting status for destination '%s': %s\n",
  48. argv0,
  49. dest,
  50. strerror(errno));
  51. return -1;
  52. }
  53. }
  54. char *dest_path = dest;
  55. char target[PATH_MAX] = "";
  56. if(multi_src || S_ISDIR(dest_stat.st_mode))
  57. {
  58. char *src_basename = static_basename(src);
  59. if(snprintf(target, PATH_MAX, "%s/%s", dest, src_basename) < 0)
  60. {
  61. fprintf(stderr,
  62. "%s: Failed joining destination '%s' and source '%s'\n",
  63. argv0,
  64. dest,
  65. src_basename);
  66. return -1;
  67. }
  68. dest_path = target;
  69. }
  70. if(S_ISDIR(src_stat.st_mode))
  71. {
  72. if(!opt_R)
  73. {
  74. fprintf(
  75. stderr, "%s: Option -R not specified and source file '%s' is a directory\n", argv0, src);
  76. return -1;
  77. }
  78. if(mkdir(dest_path, mode | S_IRWXU) != 0)
  79. {
  80. if(errno != EEXIST)
  81. {
  82. fprintf(stderr,
  83. "%s: Failed creating target directory '%s': %s\n",
  84. argv0,
  85. dest,
  86. strerror(errno));
  87. return -1;
  88. }
  89. else
  90. errno = 0;
  91. }
  92. if(preserve_metadata)
  93. {
  94. struct timespec src_times[2] = {src_stat.st_atim, src_stat.st_mtim};
  95. if(utimensat(AT_FDCWD, dest_path, src_times, 0) != 0)
  96. {
  97. fprintf(stderr,
  98. "%s: Error while setting access/modification times on '%s': %s\n",
  99. argv0,
  100. dest_path,
  101. strerror(errno));
  102. return -1;
  103. }
  104. if(chown(dest_path, src_stat.st_uid, src_stat.st_gid) < 0)
  105. {
  106. fprintf(stderr,
  107. "%s: Error: Failed setting ownership on file '%s': %s\n",
  108. argv0,
  109. dest_path,
  110. strerror(errno));
  111. return -1;
  112. }
  113. }
  114. fprintf(stderr, "%s: FIXME: Copy the files inside '%s/'\n", argv0, dest);
  115. return -1;
  116. }
  117. else if(S_ISLNK(src_stat.st_mode))
  118. {
  119. char link[PATH_MAX] = "";
  120. if(readlink(src, link, sizeof(link)) < 0)
  121. {
  122. fprintf(stderr, "%s: Failed reading symlink of '%s': %s\n", argv0, src, strerror(errno));
  123. return -1;
  124. }
  125. if(symlink(link, dest_path) != 0)
  126. {
  127. fprintf(stderr,
  128. "%s: Failed copying symlink '%s' to '%s': %s\n",
  129. argv0,
  130. src,
  131. dest_path,
  132. strerror(errno));
  133. return -1;
  134. }
  135. if(preserve_metadata)
  136. {
  137. struct timespec src_times[2] = {src_stat.st_atim, src_stat.st_mtim};
  138. if(utimensat(AT_FDCWD, dest_path, src_times, AT_SYMLINK_NOFOLLOW) != 0)
  139. {
  140. fprintf(stderr,
  141. "%s: Error while setting access/modification times on '%s': %s\n",
  142. argv0,
  143. dest_path,
  144. strerror(errno));
  145. return -1;
  146. }
  147. if(chown(dest, src_stat.st_uid, src_stat.st_gid) < 0)
  148. {
  149. fprintf(stderr,
  150. "%s: Error: Failed setting ownership on file '%s': %s\n",
  151. argv0,
  152. dest_path,
  153. strerror(errno));
  154. return -1;
  155. }
  156. }
  157. }
  158. else if(S_ISREG(src_stat.st_mode))
  159. {
  160. // FIXME: Handle writing into block devices
  161. int src_fd = open(src, O_RDONLY | O_NOFOLLOW);
  162. if(src_fd < 0)
  163. {
  164. fprintf(
  165. stderr, "%s: Failed opening file '%s' for reading: %s\n", argv0, src, strerror(errno));
  166. return -1;
  167. }
  168. // FIXME: Handle -i (consent)
  169. int dest_fd = open(dest_path, O_CREAT | O_WRONLY | O_TRUNC, mode);
  170. if(dest_fd < 0)
  171. {
  172. // FIXME: Handle -f (unlink on creation-opening fail)
  173. fprintf(stderr,
  174. "%s: Failed create-opening file '%s' for writing: %s\n",
  175. argv0,
  176. dest_path,
  177. strerror(errno));
  178. return -1;
  179. }
  180. off_t len = src_stat.st_size;
  181. off_t ret = -1;
  182. do
  183. {
  184. ret = copy_file_range(src_fd, NULL, dest_fd, NULL, len, 0);
  185. if(ret == -1)
  186. {
  187. fprintf(stderr,
  188. "%s: Error: Failed copying data from '%s' to '%s': %s\n",
  189. argv0,
  190. src,
  191. dest_path,
  192. strerror(errno));
  193. return -1;
  194. }
  195. len -= ret;
  196. } while(len > 0 && ret > 0);
  197. assert(errno == 0);
  198. if(preserve_metadata)
  199. {
  200. struct timespec src_times[2] = {src_stat.st_atim, src_stat.st_mtim};
  201. if(futimens(dest_fd, src_times) != 0)
  202. {
  203. fprintf(stderr,
  204. "%s: Error while setting access/modification times on '%s': %s\n",
  205. argv0,
  206. dest_path,
  207. strerror(errno));
  208. return -1;
  209. }
  210. if(fchown(dest_fd, src_stat.st_uid, src_stat.st_gid) < 0)
  211. {
  212. fprintf(stderr,
  213. "%s: Error: Failed setting ownership on file '%s': %s\n",
  214. argv0,
  215. dest_path,
  216. strerror(errno));
  217. return -1;
  218. }
  219. }
  220. assert(errno == 0);
  221. if(close(src_fd) < 0)
  222. {
  223. fprintf(stderr, "%s: Error closing '%s'\n", argv0, src);
  224. return -1;
  225. }
  226. if(close(dest_fd) < 0)
  227. {
  228. fprintf(stderr, "%s: Error closing '%s'\n", argv0, dest_path);
  229. return -1;
  230. }
  231. }
  232. else
  233. {
  234. fprintf(stderr, "%s: Unhandled file '%s' of type-mode %o\n", argv0, dest, src_stat.st_mode);
  235. return -1;
  236. }
  237. assert(errno == 0);
  238. return 0;
  239. }
  240. static void
  241. usage()
  242. {
  243. fprintf(stderr, "Usage: cp [-p] [-rR [-H|-L|-P]] source... destination\n");
  244. }
  245. int
  246. main(int argc, char *argv[])
  247. {
  248. int c = -1;
  249. while((c = getopt(argc, argv, ":pRHLP")) != -1)
  250. {
  251. switch(c)
  252. {
  253. case 'p':
  254. preserve_metadata = true;
  255. break;
  256. case 'R':
  257. case 'r':
  258. opt_R = true;
  259. break;
  260. case 'H':
  261. // FIXME: Only first level aka arguments, not traversal
  262. follow_src_symlinks = true;
  263. break;
  264. case 'L':
  265. follow_src_symlinks = true;
  266. break;
  267. case 'P':
  268. follow_src_symlinks = false;
  269. break;
  270. case '?':
  271. fprintf(stderr, "install: Unknown option '-%c'\n", optopt);
  272. usage();
  273. break;
  274. }
  275. }
  276. assert(errno == 0);
  277. argc -= optind;
  278. argv += optind;
  279. if(argc == 2)
  280. {
  281. if(do_copy(argv[0], argv[1], false) < 0) return 1;
  282. }
  283. else
  284. {
  285. char *dest = argv[argc - 1];
  286. for(int i = 0; i < argc - 1; i++)
  287. {
  288. char *src = argv[i];
  289. if(do_copy(src, dest, true) < 0) return 1;
  290. }
  291. }
  292. return 0;
  293. }