copy_file.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * Mini copy_file implementation for busybox
  4. *
  5. * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  20. *
  21. */
  22. #include <sys/types.h>
  23. #include <sys/stat.h>
  24. #include <unistd.h>
  25. #include <fcntl.h>
  26. #include <utime.h>
  27. #include <errno.h>
  28. #include <dirent.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include "busybox.h"
  32. int copy_file(const char *source, const char *dest, int flags)
  33. {
  34. struct stat source_stat;
  35. struct stat dest_stat;
  36. int dest_exists = 0;
  37. int status = 0;
  38. if ((!(flags & FILEUTILS_DEREFERENCE) &&
  39. lstat(source, &source_stat) < 0) ||
  40. ((flags & FILEUTILS_DEREFERENCE) &&
  41. stat(source, &source_stat) < 0)) {
  42. bb_perror_msg("%s", source);
  43. return -1;
  44. }
  45. if (lstat(dest, &dest_stat) < 0) {
  46. if (errno != ENOENT) {
  47. bb_perror_msg("unable to stat `%s'", dest);
  48. return -1;
  49. }
  50. } else {
  51. if (source_stat.st_dev == dest_stat.st_dev &&
  52. source_stat.st_ino == dest_stat.st_ino) {
  53. bb_error_msg("`%s' and `%s' are the same file", source, dest);
  54. return -1;
  55. }
  56. dest_exists = 1;
  57. }
  58. if (S_ISDIR(source_stat.st_mode)) {
  59. DIR *dp;
  60. struct dirent *d;
  61. mode_t saved_umask = 0;
  62. if (!(flags & FILEUTILS_RECUR)) {
  63. bb_error_msg("%s: omitting directory", source);
  64. return -1;
  65. }
  66. /* Create DEST. */
  67. if (dest_exists) {
  68. if (!S_ISDIR(dest_stat.st_mode)) {
  69. bb_error_msg("`%s' is not a directory", dest);
  70. return -1;
  71. }
  72. } else {
  73. mode_t mode;
  74. saved_umask = umask(0);
  75. mode = source_stat.st_mode;
  76. if (!(flags & FILEUTILS_PRESERVE_STATUS))
  77. mode = source_stat.st_mode & ~saved_umask;
  78. mode |= S_IRWXU;
  79. if (mkdir(dest, mode) < 0) {
  80. umask(saved_umask);
  81. bb_perror_msg("cannot create directory `%s'", dest);
  82. return -1;
  83. }
  84. umask(saved_umask);
  85. }
  86. /* Recursively copy files in SOURCE. */
  87. if ((dp = opendir(source)) == NULL) {
  88. bb_perror_msg("unable to open directory `%s'", source);
  89. status = -1;
  90. goto end;
  91. }
  92. while ((d = readdir(dp)) != NULL) {
  93. char *new_source, *new_dest;
  94. new_source = concat_subpath_file(source, d->d_name);
  95. if(new_source == NULL)
  96. continue;
  97. new_dest = concat_path_file(dest, d->d_name);
  98. if (copy_file(new_source, new_dest, flags) < 0)
  99. status = -1;
  100. free(new_source);
  101. free(new_dest);
  102. }
  103. /* closedir have only EBADF error, but "dp" not changes */
  104. closedir(dp);
  105. if (!dest_exists &&
  106. chmod(dest, source_stat.st_mode & ~saved_umask) < 0) {
  107. bb_perror_msg("unable to change permissions of `%s'", dest);
  108. status = -1;
  109. }
  110. } else if (S_ISREG(source_stat.st_mode)) {
  111. int src_fd;
  112. int dst_fd;
  113. #ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS
  114. char *link_name;
  115. if (!(flags & FILEUTILS_DEREFERENCE) &&
  116. is_in_ino_dev_hashtable(&source_stat, &link_name)) {
  117. if (link(link_name, dest) < 0) {
  118. bb_perror_msg("unable to link `%s'", dest);
  119. return -1;
  120. }
  121. return 0;
  122. }
  123. #endif
  124. src_fd = open(source, O_RDONLY);
  125. if (src_fd == -1) {
  126. bb_perror_msg("unable to open `%s'", source);
  127. return(-1);
  128. }
  129. if (dest_exists) {
  130. if (flags & FILEUTILS_INTERACTIVE) {
  131. fprintf(stderr, "%s: overwrite `%s'? ", bb_applet_name, dest);
  132. if (!bb_ask_confirmation()) {
  133. close (src_fd);
  134. return 0;
  135. }
  136. }
  137. dst_fd = open(dest, O_WRONLY|O_TRUNC);
  138. if (dst_fd == -1) {
  139. if (!(flags & FILEUTILS_FORCE)) {
  140. bb_perror_msg("unable to open `%s'", dest);
  141. close(src_fd);
  142. return -1;
  143. }
  144. if (unlink(dest) < 0) {
  145. bb_perror_msg("unable to remove `%s'", dest);
  146. close(src_fd);
  147. return -1;
  148. }
  149. dest_exists = 0;
  150. }
  151. }
  152. if (!dest_exists) {
  153. dst_fd = open(dest, O_WRONLY|O_CREAT, source_stat.st_mode);
  154. if (dst_fd == -1) {
  155. bb_perror_msg("unable to open `%s'", dest);
  156. close(src_fd);
  157. return(-1);
  158. }
  159. }
  160. if (bb_copyfd_eof(src_fd, dst_fd) == -1)
  161. status = -1;
  162. if (close(dst_fd) < 0) {
  163. bb_perror_msg("unable to close `%s'", dest);
  164. status = -1;
  165. }
  166. if (close(src_fd) < 0) {
  167. bb_perror_msg("unable to close `%s'", source);
  168. status = -1;
  169. }
  170. }
  171. else if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) ||
  172. S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) ||
  173. S_ISLNK(source_stat.st_mode)) {
  174. if (dest_exists) {
  175. if((flags & FILEUTILS_FORCE) == 0) {
  176. fprintf(stderr, "`%s' exists\n", dest);
  177. return -1;
  178. }
  179. if(unlink(dest) < 0) {
  180. bb_perror_msg("unable to remove `%s'", dest);
  181. return -1;
  182. }
  183. }
  184. } else {
  185. bb_error_msg("internal error: unrecognized file type");
  186. return -1;
  187. }
  188. if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) ||
  189. S_ISSOCK(source_stat.st_mode)) {
  190. if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) {
  191. bb_perror_msg("unable to create `%s'", dest);
  192. return -1;
  193. }
  194. } else if (S_ISFIFO(source_stat.st_mode)) {
  195. if (mkfifo(dest, source_stat.st_mode) < 0) {
  196. bb_perror_msg("cannot create fifo `%s'", dest);
  197. return -1;
  198. }
  199. } else if (S_ISLNK(source_stat.st_mode)) {
  200. char *lpath;
  201. lpath = xreadlink(source);
  202. if (symlink(lpath, dest) < 0) {
  203. bb_perror_msg("cannot create symlink `%s'", dest);
  204. return -1;
  205. }
  206. free(lpath);
  207. #if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
  208. if (flags & FILEUTILS_PRESERVE_STATUS)
  209. if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
  210. bb_perror_msg("unable to preserve ownership of `%s'", dest);
  211. #endif
  212. #ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS
  213. add_to_ino_dev_hashtable(&source_stat, dest);
  214. #endif
  215. return 0;
  216. }
  217. #ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS
  218. if (! S_ISDIR(source_stat.st_mode)) {
  219. add_to_ino_dev_hashtable(&source_stat, dest);
  220. }
  221. #endif
  222. end:
  223. if (flags & FILEUTILS_PRESERVE_STATUS) {
  224. struct utimbuf times;
  225. times.actime = source_stat.st_atime;
  226. times.modtime = source_stat.st_mtime;
  227. if (utime(dest, &times) < 0)
  228. bb_perror_msg("unable to preserve times of `%s'", dest);
  229. if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) {
  230. source_stat.st_mode &= ~(S_ISUID | S_ISGID);
  231. bb_perror_msg("unable to preserve ownership of `%s'", dest);
  232. }
  233. if (chmod(dest, source_stat.st_mode) < 0)
  234. bb_perror_msg("unable to preserve permissions of `%s'", dest);
  235. }
  236. return status;
  237. }