misc_file.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /*
  2. * misc.c -- handles:
  3. * copyfile() movefile()
  4. *
  5. */
  6. #include "common.h"
  7. #include <sys/stat.h>
  8. #include <unistd.h>
  9. #include <fcntl.h>
  10. #include <errno.h>
  11. #include "stat.h"
  12. #include "misc_file.h"
  13. #include "main.h"
  14. #include "shell.h"
  15. #include "binary.h"
  16. static bool looking = 0;
  17. /* Copy a file from one place to another (possibly erasing old copy).
  18. *
  19. * returns: 0 if OK
  20. * 1 if can't open original file
  21. * 2 if can't open new file
  22. * 3 if original file isn't normal
  23. * 4 if ran out of disk space
  24. */
  25. int copyfile(const char *oldpath, const char *newpath)
  26. {
  27. int fi;
  28. #ifndef CYGWIN_HACKS
  29. fi = open(oldpath, O_RDONLY, 0);
  30. #else
  31. fi = open(oldpath, O_RDONLY | O_BINARY, 0);
  32. #endif
  33. if (fi < 0)
  34. return 1;
  35. struct stat st;
  36. fstat(fi, &st);
  37. if (!(st.st_mode & S_IFREG))
  38. return 3;
  39. int fo;
  40. fo = creat(newpath, (int) (st.st_mode & 0600));
  41. if (fo < 0) {
  42. close(fi);
  43. return 2;
  44. }
  45. char buf[512] = "";
  46. for (int x = 1; x > 0;) {
  47. x = read(fi, buf, 512);
  48. if (x > 0) {
  49. if (write(fo, buf, x) < x) { /* Couldn't write */
  50. close(fo);
  51. close(fi);
  52. unlink(newpath);
  53. return 4;
  54. }
  55. }
  56. }
  57. #ifdef HAVE_FSYNC
  58. fsync(fo);
  59. #endif /* HAVE_FSYNC */
  60. close(fo);
  61. close(fi);
  62. return 0;
  63. }
  64. int movefile(const char *oldpath, const char *newpath)
  65. {
  66. #ifdef HAVE_RENAME
  67. /* Try to use rename first */
  68. if (!rename(oldpath, newpath))
  69. return 0;
  70. #endif /* HAVE_RENAME */
  71. /* If that fails, fall back to just copying and then
  72. * deleting the file.
  73. */
  74. int ret = copyfile(oldpath, newpath);
  75. if (!ret)
  76. unlink(oldpath);
  77. return ret;
  78. }
  79. int is_file(const char *s)
  80. {
  81. struct stat ss;
  82. int i = stat(s, &ss);
  83. if (i < 0)
  84. return 0;
  85. if ((ss.st_mode & S_IFREG) || (ss.st_mode & S_IFLNK))
  86. return 1;
  87. return 0;
  88. }
  89. int can_stat(const char *s)
  90. {
  91. struct stat ss;
  92. int i = stat(s, &ss);
  93. if (i < 0)
  94. return 0;
  95. return 1;
  96. }
  97. int can_lstat(const char *s)
  98. {
  99. struct stat ss;
  100. int i = lstat(s, &ss);
  101. if (i < 0)
  102. return 0;
  103. return 1;
  104. }
  105. int is_symlink(const char *s)
  106. {
  107. struct stat ss;
  108. int i = lstat(s, &ss);
  109. if (i < 0)
  110. return 0;
  111. if (ss.st_mode & S_IFLNK)
  112. return 1;
  113. return 0;
  114. }
  115. int is_dir(const char *s)
  116. {
  117. struct stat ss;
  118. int i = stat(s, &ss);
  119. if (i < 0)
  120. return 0;
  121. if (ss.st_mode & S_IFDIR)
  122. return 1;
  123. return 0;
  124. }
  125. int fixmod(const char *s)
  126. {
  127. if (!can_stat(s))
  128. return 1;
  129. return chmod(s, S_IRUSR | S_IWUSR | S_IXUSR);
  130. }
  131. Tempfile::Tempfile(const char *prefix)
  132. {
  133. if (prefix) {
  134. plen = strlen(prefix) + 1;
  135. this->prefix = new char[plen];
  136. strlcpy(this->prefix, prefix, plen);
  137. } else {
  138. this->prefix = NULL;
  139. plen = -1; /* to swallow the '-' */
  140. }
  141. AllocTempfile();
  142. }
  143. void Tempfile::AllocTempfile()
  144. {
  145. len = strlen(tempdir) + 1 + plen + 1 + 6 + 1;
  146. file = new char[len];
  147. if (prefix)
  148. simple_snprintf(file, len, "%s.%s-XXXXXX", tempdir, prefix);
  149. else
  150. simple_snprintf(file, len, "%s.XXXXXX", tempdir);
  151. MakeTemp();
  152. }
  153. void Tempfile::MakeTemp()
  154. {
  155. if ((fd = mkstemp(file)) < 0) {
  156. f = NULL;
  157. goto error;
  158. }
  159. if ((f = fdopen(fd, "w+b")) == NULL)
  160. goto error;
  161. fchmod(fd, S_IRUSR | S_IWUSR);
  162. error = 0;
  163. return;
  164. error:
  165. putlog(LOG_ERRORS, "*", "Couldn't create temporary file '%s': %s", file, strerror(errno));
  166. error = 1;
  167. /* Since we failed to create a file in the given tempdir, let's try finding a new one */
  168. if (!looking) {
  169. /* ... Not finding a new tempdir is fatal. */
  170. if (FindDir() == ERROR)
  171. werr(ERR_TMPSTAT); /* FIXME: Perhaps this should be an exception? */
  172. /* ... If we found one, let's try all over! */
  173. else {
  174. error = 0;
  175. delete[] file;
  176. file = NULL;
  177. AllocTempfile();
  178. }
  179. }
  180. }
  181. void Tempfile::my_close()
  182. {
  183. if (f) {
  184. fclose(f);
  185. f = NULL;
  186. } else if (fd >= 0) {
  187. close(fd);
  188. fd = -1;
  189. }
  190. }
  191. Tempfile::~Tempfile()
  192. {
  193. unlink(file);
  194. my_close();
  195. delete[] file;
  196. }
  197. static bool check_tempdir(bool do_mod)
  198. {
  199. mkdir_p(tempdir);
  200. if (!can_stat(tempdir))
  201. return 0;
  202. if (do_mod && fixmod(tempdir))
  203. return 0;
  204. /* test tempdir: it's vital */
  205. Tempfile *testdir = new Tempfile("test");
  206. /* There was an error creating a file in this directory, return to move on in list of dirs */
  207. if (!testdir || testdir->error)
  208. return 0;
  209. fprintf(testdir->f, "\n");
  210. int result = fflush(testdir->f);
  211. delete testdir;
  212. if (result) {
  213. sdprintf("%s: %s", tempdir, strerror(errno));
  214. return 0;
  215. }
  216. return 1;
  217. }
  218. bool Tempfile::FindDir()
  219. {
  220. /* this is temporary until we make tmpdir customizable */
  221. #ifdef CYGWIN_HACKS
  222. simple_snprintf(tempdir, DIRMAX, "./tmp/");
  223. if (!check_tempdir(0)) {
  224. clear_tmpdir = 0;
  225. simple_snprintf(tempdir, DIRMAX, "./");
  226. }
  227. return OK;
  228. #else
  229. looking = 1;
  230. /* If this is a hub, use, "./tmp/" */
  231. if (conf.bots && conf.bots->nick && conf.bots->hub) {
  232. simple_snprintf(tempdir, DIRMAX, "%s/tmp/", conf.binpath);
  233. if (check_tempdir(0)) {
  234. looking = 0;
  235. return OK;
  236. }
  237. }
  238. /* The dirs we WANT to use aren't accessible, try a random one instead to get the job done. */
  239. clear_tmpdir = 0;
  240. char *dirs[] = {
  241. "/tmp/",
  242. "/usr/tmp/",
  243. "/var/tmp/",
  244. "./",
  245. NULL
  246. };
  247. for (int i = 0; dirs[i]; i++) {
  248. strlcpy(tempdir, dirs[i], DIRMAX);
  249. if (check_tempdir(0)) {
  250. looking = 0;
  251. return OK;
  252. }
  253. }
  254. werr(ERR_TMPSTAT);
  255. #endif /* CYGWIN_HACKS */
  256. }