/contrib/bind9/lib/isc/unix/file.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 541 lines · 356 code · 95 blank · 90 comment · 143 complexity · aafdbe616dae93173fb52e181a432a4c MD5 · raw file

  1. /*
  2. * Copyright (C) 2004, 2005, 2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
  3. * Copyright (C) 2000-2002 Internet Software Consortium.
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  10. * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  11. * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  12. * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  13. * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  14. * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  15. * PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. /*
  18. * Portions Copyright (c) 1987, 1993
  19. * The Regents of the University of California. All rights reserved.
  20. *
  21. * Redistribution and use in source and binary forms, with or without
  22. * modification, are permitted provided that the following conditions
  23. * are met:
  24. * 1. Redistributions of source code must retain the above copyright
  25. * notice, this list of conditions and the following disclaimer.
  26. * 2. Redistributions in binary form must reproduce the above copyright
  27. * notice, this list of conditions and the following disclaimer in the
  28. * documentation and/or other materials provided with the distribution.
  29. * 3. All advertising materials mentioning features or use of this software
  30. * must display the following acknowledgement:
  31. * This product includes software developed by the University of
  32. * California, Berkeley and its contributors.
  33. * 4. Neither the name of the University nor the names of its contributors
  34. * may be used to endorse or promote products derived from this software
  35. * without specific prior written permission.
  36. *
  37. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  38. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  39. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  40. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  41. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  42. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  43. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  44. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  45. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  46. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. */
  49. /* $Id$ */
  50. /*! \file */
  51. #include <config.h>
  52. #include <errno.h>
  53. #include <fcntl.h>
  54. #include <limits.h>
  55. #include <stdlib.h>
  56. #include <time.h> /* Required for utimes on some platforms. */
  57. #include <unistd.h> /* Required for mkstemp on NetBSD. */
  58. #include <sys/stat.h>
  59. #include <sys/time.h>
  60. #include <isc/dir.h>
  61. #include <isc/file.h>
  62. #include <isc/log.h>
  63. #include <isc/mem.h>
  64. #include <isc/random.h>
  65. #include <isc/string.h>
  66. #include <isc/time.h>
  67. #include <isc/util.h>
  68. #include "errno2result.h"
  69. /*
  70. * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
  71. * it might be good to provide a mechanism that allows for the results
  72. * of a previous stat() to be used again without having to do another stat,
  73. * such as perl's mechanism of using "_" in place of a file name to indicate
  74. * that the results of the last stat should be used. But then you get into
  75. * annoying MP issues. BTW, Win32 has stat().
  76. */
  77. static isc_result_t
  78. file_stats(const char *file, struct stat *stats) {
  79. isc_result_t result = ISC_R_SUCCESS;
  80. REQUIRE(file != NULL);
  81. REQUIRE(stats != NULL);
  82. if (stat(file, stats) != 0)
  83. result = isc__errno2result(errno);
  84. return (result);
  85. }
  86. isc_result_t
  87. isc_file_getmodtime(const char *file, isc_time_t *time) {
  88. isc_result_t result;
  89. struct stat stats;
  90. REQUIRE(file != NULL);
  91. REQUIRE(time != NULL);
  92. result = file_stats(file, &stats);
  93. if (result == ISC_R_SUCCESS)
  94. /*
  95. * XXXDCL some operating systems provide nanoseconds, too,
  96. * such as BSD/OS via st_mtimespec.
  97. */
  98. isc_time_set(time, stats.st_mtime, 0);
  99. return (result);
  100. }
  101. isc_result_t
  102. isc_file_settime(const char *file, isc_time_t *time) {
  103. struct timeval times[2];
  104. REQUIRE(file != NULL && time != NULL);
  105. /*
  106. * tv_sec is at least a 32 bit quantity on all platforms we're
  107. * dealing with, but it is signed on most (all?) of them,
  108. * so we need to make sure the high bit isn't set. This unfortunately
  109. * loses when either:
  110. * * tv_sec becomes a signed 64 bit integer but long is 32 bits
  111. * and isc_time_seconds > LONG_MAX, or
  112. * * isc_time_seconds is changed to be > 32 bits but long is 32 bits
  113. * and isc_time_seconds has at least 33 significant bits.
  114. */
  115. times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(time);
  116. /*
  117. * Here is the real check for the high bit being set.
  118. */
  119. if ((times[0].tv_sec &
  120. (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0)
  121. return (ISC_R_RANGE);
  122. /*
  123. * isc_time_nanoseconds guarantees a value that divided by 1000 will
  124. * fit into the minimum possible size tv_usec field. Unfortunately,
  125. * we don't know what that type is so can't cast directly ... but
  126. * we can at least cast to signed so the IRIX compiler shuts up.
  127. */
  128. times[0].tv_usec = times[1].tv_usec =
  129. (isc_int32_t)(isc_time_nanoseconds(time) / 1000);
  130. if (utimes(file, times) < 0)
  131. return (isc__errno2result(errno));
  132. return (ISC_R_SUCCESS);
  133. }
  134. #undef TEMPLATE
  135. #define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */
  136. isc_result_t
  137. isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
  138. return (isc_file_template(path, TEMPLATE, buf, buflen));
  139. }
  140. isc_result_t
  141. isc_file_template(const char *path, const char *templet, char *buf,
  142. size_t buflen) {
  143. char *s;
  144. REQUIRE(path != NULL);
  145. REQUIRE(templet != NULL);
  146. REQUIRE(buf != NULL);
  147. s = strrchr(templet, '/');
  148. if (s != NULL)
  149. templet = s + 1;
  150. s = strrchr(path, '/');
  151. if (s != NULL) {
  152. if ((s - path + 1 + strlen(templet) + 1) > buflen)
  153. return (ISC_R_NOSPACE);
  154. strncpy(buf, path, s - path + 1);
  155. buf[s - path + 1] = '\0';
  156. strcat(buf, templet);
  157. } else {
  158. if ((strlen(templet) + 1) > buflen)
  159. return (ISC_R_NOSPACE);
  160. strcpy(buf, templet);
  161. }
  162. return (ISC_R_SUCCESS);
  163. }
  164. static char alphnum[] =
  165. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  166. isc_result_t
  167. isc_file_renameunique(const char *file, char *templet) {
  168. char *x;
  169. char *cp;
  170. isc_uint32_t which;
  171. REQUIRE(file != NULL);
  172. REQUIRE(templet != NULL);
  173. cp = templet;
  174. while (*cp != '\0')
  175. cp++;
  176. if (cp == templet)
  177. return (ISC_R_FAILURE);
  178. x = cp--;
  179. while (cp >= templet && *cp == 'X') {
  180. isc_random_get(&which);
  181. *cp = alphnum[which % (sizeof(alphnum) - 1)];
  182. x = cp--;
  183. }
  184. while (link(file, templet) == -1) {
  185. if (errno != EEXIST)
  186. return (isc__errno2result(errno));
  187. for (cp = x;;) {
  188. char *t;
  189. if (*cp == '\0')
  190. return (ISC_R_FAILURE);
  191. t = strchr(alphnum, *cp);
  192. if (t == NULL || *++t == '\0')
  193. *cp++ = alphnum[0];
  194. else {
  195. *cp = *t;
  196. break;
  197. }
  198. }
  199. }
  200. if (unlink(file) < 0)
  201. if (errno != ENOENT)
  202. return (isc__errno2result(errno));
  203. return (ISC_R_SUCCESS);
  204. }
  205. isc_result_t
  206. isc_file_openunique(char *templet, FILE **fp) {
  207. int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
  208. return (isc_file_openuniquemode(templet, mode, fp));
  209. }
  210. isc_result_t
  211. isc_file_openuniqueprivate(char *templet, FILE **fp) {
  212. int mode = S_IWUSR|S_IRUSR;
  213. return (isc_file_openuniquemode(templet, mode, fp));
  214. }
  215. isc_result_t
  216. isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
  217. int fd;
  218. FILE *f;
  219. isc_result_t result = ISC_R_SUCCESS;
  220. char *x;
  221. char *cp;
  222. isc_uint32_t which;
  223. REQUIRE(templet != NULL);
  224. REQUIRE(fp != NULL && *fp == NULL);
  225. cp = templet;
  226. while (*cp != '\0')
  227. cp++;
  228. if (cp == templet)
  229. return (ISC_R_FAILURE);
  230. x = cp--;
  231. while (cp >= templet && *cp == 'X') {
  232. isc_random_get(&which);
  233. *cp = alphnum[which % (sizeof(alphnum) - 1)];
  234. x = cp--;
  235. }
  236. while ((fd = open(templet, O_RDWR|O_CREAT|O_EXCL, mode)) == -1) {
  237. if (errno != EEXIST)
  238. return (isc__errno2result(errno));
  239. for (cp = x;;) {
  240. char *t;
  241. if (*cp == '\0')
  242. return (ISC_R_FAILURE);
  243. t = strchr(alphnum, *cp);
  244. if (t == NULL || *++t == '\0')
  245. *cp++ = alphnum[0];
  246. else {
  247. *cp = *t;
  248. break;
  249. }
  250. }
  251. }
  252. f = fdopen(fd, "w+");
  253. if (f == NULL) {
  254. result = isc__errno2result(errno);
  255. if (remove(templet) < 0) {
  256. isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
  257. ISC_LOGMODULE_FILE, ISC_LOG_ERROR,
  258. "remove '%s': failed", templet);
  259. }
  260. (void)close(fd);
  261. } else
  262. *fp = f;
  263. return (result);
  264. }
  265. isc_result_t
  266. isc_file_remove(const char *filename) {
  267. int r;
  268. REQUIRE(filename != NULL);
  269. r = unlink(filename);
  270. if (r == 0)
  271. return (ISC_R_SUCCESS);
  272. else
  273. return (isc__errno2result(errno));
  274. }
  275. isc_result_t
  276. isc_file_rename(const char *oldname, const char *newname) {
  277. int r;
  278. REQUIRE(oldname != NULL);
  279. REQUIRE(newname != NULL);
  280. r = rename(oldname, newname);
  281. if (r == 0)
  282. return (ISC_R_SUCCESS);
  283. else
  284. return (isc__errno2result(errno));
  285. }
  286. isc_boolean_t
  287. isc_file_exists(const char *pathname) {
  288. struct stat stats;
  289. REQUIRE(pathname != NULL);
  290. return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
  291. }
  292. isc_result_t
  293. isc_file_isplainfile(const char *filename) {
  294. /*
  295. * This function returns success if filename is a plain file.
  296. */
  297. struct stat filestat;
  298. memset(&filestat,0,sizeof(struct stat));
  299. if ((stat(filename, &filestat)) == -1)
  300. return(isc__errno2result(errno));
  301. if(! S_ISREG(filestat.st_mode))
  302. return(ISC_R_INVALIDFILE);
  303. return(ISC_R_SUCCESS);
  304. }
  305. isc_boolean_t
  306. isc_file_isabsolute(const char *filename) {
  307. REQUIRE(filename != NULL);
  308. return (ISC_TF(filename[0] == '/'));
  309. }
  310. isc_boolean_t
  311. isc_file_iscurrentdir(const char *filename) {
  312. REQUIRE(filename != NULL);
  313. return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
  314. }
  315. isc_boolean_t
  316. isc_file_ischdiridempotent(const char *filename) {
  317. REQUIRE(filename != NULL);
  318. if (isc_file_isabsolute(filename))
  319. return (ISC_TRUE);
  320. if (isc_file_iscurrentdir(filename))
  321. return (ISC_TRUE);
  322. return (ISC_FALSE);
  323. }
  324. const char *
  325. isc_file_basename(const char *filename) {
  326. char *s;
  327. REQUIRE(filename != NULL);
  328. s = strrchr(filename, '/');
  329. if (s == NULL)
  330. return (filename);
  331. return (s + 1);
  332. }
  333. isc_result_t
  334. isc_file_progname(const char *filename, char *buf, size_t buflen) {
  335. const char *base;
  336. size_t len;
  337. REQUIRE(filename != NULL);
  338. REQUIRE(buf != NULL);
  339. base = isc_file_basename(filename);
  340. len = strlen(base) + 1;
  341. if (len > buflen)
  342. return (ISC_R_NOSPACE);
  343. memcpy(buf, base, len);
  344. return (ISC_R_SUCCESS);
  345. }
  346. /*
  347. * Put the absolute name of the current directory into 'dirname', which is
  348. * a buffer of at least 'length' characters. End the string with the
  349. * appropriate path separator, such that the final product could be
  350. * concatenated with a relative pathname to make a valid pathname string.
  351. */
  352. static isc_result_t
  353. dir_current(char *dirname, size_t length) {
  354. char *cwd;
  355. isc_result_t result = ISC_R_SUCCESS;
  356. REQUIRE(dirname != NULL);
  357. REQUIRE(length > 0U);
  358. cwd = getcwd(dirname, length);
  359. if (cwd == NULL) {
  360. if (errno == ERANGE)
  361. result = ISC_R_NOSPACE;
  362. else
  363. result = isc__errno2result(errno);
  364. } else {
  365. if (strlen(dirname) + 1 == length)
  366. result = ISC_R_NOSPACE;
  367. else if (dirname[1] != '\0')
  368. strcat(dirname, "/");
  369. }
  370. return (result);
  371. }
  372. isc_result_t
  373. isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
  374. isc_result_t result;
  375. result = dir_current(path, pathlen);
  376. if (result != ISC_R_SUCCESS)
  377. return (result);
  378. if (strlen(path) + strlen(filename) + 1 > pathlen)
  379. return (ISC_R_NOSPACE);
  380. strcat(path, filename);
  381. return (ISC_R_SUCCESS);
  382. }
  383. isc_result_t
  384. isc_file_truncate(const char *filename, isc_offset_t size) {
  385. isc_result_t result = ISC_R_SUCCESS;
  386. if (truncate(filename, size) < 0)
  387. result = isc__errno2result(errno);
  388. return (result);
  389. }
  390. isc_result_t
  391. isc_file_safecreate(const char *filename, FILE **fp) {
  392. isc_result_t result;
  393. int flags;
  394. struct stat sb;
  395. FILE *f;
  396. int fd;
  397. REQUIRE(filename != NULL);
  398. REQUIRE(fp != NULL && *fp == NULL);
  399. result = file_stats(filename, &sb);
  400. if (result == ISC_R_SUCCESS) {
  401. if ((sb.st_mode & S_IFREG) == 0)
  402. return (ISC_R_INVALIDFILE);
  403. flags = O_WRONLY | O_TRUNC;
  404. } else if (result == ISC_R_FILENOTFOUND) {
  405. flags = O_WRONLY | O_CREAT | O_EXCL;
  406. } else
  407. return (result);
  408. fd = open(filename, flags, S_IRUSR | S_IWUSR);
  409. if (fd == -1)
  410. return (isc__errno2result(errno));
  411. f = fdopen(fd, "w");
  412. if (f == NULL) {
  413. result = isc__errno2result(errno);
  414. close(fd);
  415. return (result);
  416. }
  417. *fp = f;
  418. return (ISC_R_SUCCESS);
  419. }
  420. isc_result_t
  421. isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirname, char **basename)
  422. {
  423. char *dir, *file, *slash;
  424. slash = strrchr(path, '/');
  425. if (slash == path) {
  426. file = ++slash;
  427. dir = isc_mem_strdup(mctx, "/");
  428. } else if (slash != NULL) {
  429. file = ++slash;
  430. dir = isc_mem_allocate(mctx, slash - path);
  431. if (dir != NULL)
  432. strlcpy(dir, path, slash - path);
  433. } else {
  434. file = path;
  435. dir = isc_mem_strdup(mctx, ".");
  436. }
  437. if (dir == NULL)
  438. return (ISC_R_NOMEMORY);
  439. if (*file == '\0') {
  440. isc_mem_free(mctx, dir);
  441. return (ISC_R_INVALIDFILE);
  442. }
  443. *dirname = dir;
  444. *basename = file;
  445. return (ISC_R_SUCCESS);
  446. }