PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/contrib/ntp/lib/isc/win32/file.c

https://bitbucket.org/freebsd/freebsd-base
C | 619 lines | 432 code | 104 blank | 83 comment | 170 complexity | b65da70f8b9cc23c875ac8209c93fbad MD5 | raw file
  1. /*
  2. * Copyright (C) 2004, 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. /* $Id$ */
  18. #include <config.h>
  19. #undef rename
  20. #include <errno.h>
  21. #include <limits.h>
  22. #include <stdlib.h>
  23. #include <io.h>
  24. #include <process.h>
  25. #include <sys/stat.h>
  26. #include <fcntl.h>
  27. #include <sys/utime.h>
  28. #include <isc/file.h>
  29. #include <isc/mem.h>
  30. #include <isc/result.h>
  31. #include <isc/time.h>
  32. #include <isc/util.h>
  33. #include <isc/stat.h>
  34. #include <isc/string.h>
  35. #include "errno2result.h"
  36. /*
  37. * Emulate UNIX mkstemp, which returns an open FD to the new file
  38. *
  39. */
  40. static int
  41. gettemp(char *path, int *doopen) {
  42. char *start, *trv;
  43. struct stat sbuf;
  44. int pid;
  45. trv = strrchr(path, 'X');
  46. trv++;
  47. pid = getpid();
  48. /* extra X's get set to 0's */
  49. while (*--trv == 'X') {
  50. *trv = (pid % 10) + '0';
  51. pid /= 10;
  52. }
  53. /*
  54. * check the target directory; if you have six X's and it
  55. * doesn't exist this runs for a *very* long time.
  56. */
  57. for (start = trv + 1;; --trv) {
  58. if (trv <= path)
  59. break;
  60. if (*trv == '\\') {
  61. *trv = '\0';
  62. if (stat(path, &sbuf))
  63. return (0);
  64. if (!S_ISDIR(sbuf.st_mode)) {
  65. errno = ENOTDIR;
  66. return (0);
  67. }
  68. *trv = '\\';
  69. break;
  70. }
  71. }
  72. for (;;) {
  73. if (doopen) {
  74. if ((*doopen =
  75. open(path, O_CREAT|O_EXCL|O_RDWR,
  76. _S_IREAD | _S_IWRITE)) >= 0)
  77. return (1);
  78. if (errno != EEXIST)
  79. return (0);
  80. } else if (stat(path, &sbuf))
  81. return (errno == ENOENT ? 1 : 0);
  82. /* tricky little algorithm for backward compatibility */
  83. for (trv = start;;) {
  84. if (!*trv)
  85. return (0);
  86. if (*trv == 'z')
  87. *trv++ = 'a';
  88. else {
  89. if (isdigit(*trv))
  90. *trv = 'a';
  91. else
  92. ++*trv;
  93. break;
  94. }
  95. }
  96. }
  97. /*NOTREACHED*/
  98. }
  99. static int
  100. mkstemp(char *path) {
  101. int fd;
  102. return (gettemp(path, &fd) ? fd : -1);
  103. }
  104. /*
  105. * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
  106. * it might be good to provide a mechanism that allows for the results
  107. * of a previous stat() to be used again without having to do another stat,
  108. * such as perl's mechanism of using "_" in place of a file name to indicate
  109. * that the results of the last stat should be used. But then you get into
  110. * annoying MP issues. BTW, Win32 has stat().
  111. */
  112. static isc_result_t
  113. file_stats(const char *file, struct stat *stats) {
  114. isc_result_t result = ISC_R_SUCCESS;
  115. REQUIRE(file != NULL);
  116. REQUIRE(stats != NULL);
  117. if (stat(file, stats) != 0)
  118. result = isc__errno2result(errno);
  119. return (result);
  120. }
  121. /*
  122. * isc_file_safemovefile is needed to be defined here to ensure that
  123. * any file with the new name is renamed to a backup name and then the
  124. * rename is done. If all goes well then the backup can be deleted,
  125. * otherwise it gets renamed back.
  126. */
  127. int
  128. isc_file_safemovefile(const char *oldname, const char *newname) {
  129. BOOL filestatus;
  130. char buf[512];
  131. struct stat sbuf;
  132. BOOL exists = FALSE;
  133. int tmpfd;
  134. /*
  135. * Make sure we have something to do
  136. */
  137. if (stat(oldname, &sbuf) != 0) {
  138. errno = ENOENT;
  139. return (-1);
  140. }
  141. /*
  142. * Rename to a backup the new file if it still exists
  143. */
  144. if (stat(newname, &sbuf) == 0) {
  145. exists = TRUE;
  146. strcpy(buf, newname);
  147. strcat(buf, ".XXXXX");
  148. tmpfd = mkstemp(buf);
  149. if (tmpfd > 0)
  150. _close(tmpfd);
  151. DeleteFile(buf);
  152. _chmod(newname, _S_IREAD | _S_IWRITE);
  153. filestatus = MoveFile(newname, buf);
  154. }
  155. /* Now rename the file to the new name
  156. */
  157. _chmod(oldname, _S_IREAD | _S_IWRITE);
  158. filestatus = MoveFile(oldname, newname);
  159. if (filestatus == 0) {
  160. /*
  161. * Try to rename the backup back to the original name
  162. * if the backup got created
  163. */
  164. if (exists == TRUE) {
  165. filestatus = MoveFile(buf, newname);
  166. if (filestatus == 0)
  167. errno = EACCES;
  168. }
  169. return (-1);
  170. }
  171. /*
  172. * Delete the backup file if it got created
  173. */
  174. if (exists == TRUE)
  175. filestatus = DeleteFile(buf);
  176. return (0);
  177. }
  178. isc_result_t
  179. isc_file_getmodtime(const char *file, isc_time_t *time) {
  180. int fh;
  181. REQUIRE(file != NULL);
  182. REQUIRE(time != NULL);
  183. if ((fh = open(file, _O_RDONLY | _O_BINARY)) < 0)
  184. return (isc__errno2result(errno));
  185. if (!GetFileTime((HANDLE) _get_osfhandle(fh),
  186. NULL,
  187. NULL,
  188. &time->absolute))
  189. {
  190. close(fh);
  191. errno = EINVAL;
  192. return (isc__errno2result(errno));
  193. }
  194. close(fh);
  195. return (ISC_R_SUCCESS);
  196. }
  197. isc_result_t
  198. isc_file_settime(const char *file, isc_time_t *time) {
  199. int fh;
  200. REQUIRE(file != NULL && time != NULL);
  201. if ((fh = open(file, _O_RDWR | _O_BINARY)) < 0)
  202. return (isc__errno2result(errno));
  203. /*
  204. * Set the date via the filedate system call and return. Failing
  205. * this call implies the new file times are not supported by the
  206. * underlying file system.
  207. */
  208. if (!SetFileTime((HANDLE) _get_osfhandle(fh),
  209. NULL,
  210. &time->absolute,
  211. &time->absolute))
  212. {
  213. close(fh);
  214. errno = EINVAL;
  215. return (isc__errno2result(errno));
  216. }
  217. close(fh);
  218. return (ISC_R_SUCCESS);
  219. }
  220. #undef TEMPLATE
  221. #define TEMPLATE "XXXXXXXXXX.tmp" /* 14 characters. */
  222. isc_result_t
  223. isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
  224. return (isc_file_template(path, TEMPLATE, buf, buflen));
  225. }
  226. isc_result_t
  227. isc_file_template(const char *path, const char *templet, char *buf,
  228. size_t buflen) {
  229. char *s;
  230. REQUIRE(path != NULL);
  231. REQUIRE(templet != NULL);
  232. REQUIRE(buf != NULL);
  233. s = strrchr(templet, '\\');
  234. if (s != NULL)
  235. templet = s + 1;
  236. s = strrchr(path, '\\');
  237. if (s != NULL) {
  238. if ((s - path + 1 + strlen(templet) + 1) > buflen)
  239. return (ISC_R_NOSPACE);
  240. strncpy(buf, path, s - path + 1);
  241. buf[s - path + 1] = '\0';
  242. strcat(buf, templet);
  243. } else {
  244. if ((strlen(templet) + 1) > buflen)
  245. return (ISC_R_NOSPACE);
  246. strcpy(buf, templet);
  247. }
  248. return (ISC_R_SUCCESS);
  249. }
  250. isc_result_t
  251. isc_file_renameunique(const char *file, char *templet) {
  252. int fd = -1;
  253. int res = 0;
  254. isc_result_t result = ISC_R_SUCCESS;
  255. REQUIRE(file != NULL);
  256. REQUIRE(templet != NULL);
  257. fd = mkstemp(templet);
  258. if (fd == -1)
  259. result = isc__errno2result(errno);
  260. else
  261. close(fd);
  262. if (result == ISC_R_SUCCESS) {
  263. res = isc_file_safemovefile(file, templet);
  264. if (res != 0) {
  265. result = isc__errno2result(errno);
  266. (void)unlink(templet);
  267. }
  268. }
  269. return (result);
  270. }
  271. isc_result_t
  272. isc_file_openuniqueprivate(char *templet, FILE **fp) {
  273. int mode = _S_IREAD | _S_IWRITE;
  274. return (isc_file_openuniquemode(templet, mode, fp));
  275. }
  276. isc_result_t
  277. isc_file_openunique(char *templet, FILE **fp) {
  278. int mode = _S_IREAD | _S_IWRITE;
  279. return (isc_file_openuniquemode(templet, mode, fp));
  280. }
  281. isc_result_t
  282. isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
  283. int fd;
  284. FILE *f;
  285. isc_result_t result = ISC_R_SUCCESS;
  286. REQUIRE(templet != NULL);
  287. REQUIRE(fp != NULL && *fp == NULL);
  288. /*
  289. * Win32 does not have mkstemp. Using emulation above.
  290. */
  291. fd = mkstemp(templet);
  292. if (fd == -1)
  293. result = isc__errno2result(errno);
  294. if (result == ISC_R_SUCCESS) {
  295. #if 1
  296. UNUSED(mode);
  297. #else
  298. (void)fchmod(fd, mode);
  299. #endif
  300. f = fdopen(fd, "w+");
  301. if (f == NULL) {
  302. result = isc__errno2result(errno);
  303. (void)remove(templet);
  304. (void)close(fd);
  305. } else
  306. *fp = f;
  307. }
  308. return (result);
  309. }
  310. isc_result_t
  311. isc_file_remove(const char *filename) {
  312. int r;
  313. REQUIRE(filename != NULL);
  314. r = unlink(filename);
  315. if (r == 0)
  316. return (ISC_R_SUCCESS);
  317. else
  318. return (isc__errno2result(errno));
  319. }
  320. isc_result_t
  321. isc_file_rename(const char *oldname, const char *newname) {
  322. int r;
  323. REQUIRE(oldname != NULL);
  324. REQUIRE(newname != NULL);
  325. r = isc_file_safemovefile(oldname, newname);
  326. if (r == 0)
  327. return (ISC_R_SUCCESS);
  328. else
  329. return (isc__errno2result(errno));
  330. }
  331. isc_boolean_t
  332. isc_file_exists(const char *pathname) {
  333. struct stat stats;
  334. REQUIRE(pathname != NULL);
  335. return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS));
  336. }
  337. isc_result_t
  338. isc_file_isplainfile(const char *filename) {
  339. /*
  340. * This function returns success if filename is a plain file.
  341. */
  342. struct stat filestat;
  343. memset(&filestat,0,sizeof(struct stat));
  344. if ((stat(filename, &filestat)) == -1)
  345. return(isc__errno2result(errno));
  346. if(! S_ISREG(filestat.st_mode))
  347. return(ISC_R_INVALIDFILE);
  348. return(ISC_R_SUCCESS);
  349. }
  350. isc_boolean_t
  351. isc_file_isabsolute(const char *filename) {
  352. REQUIRE(filename != NULL);
  353. /*
  354. * Look for c:\path\... style, c:/path/... or \\computer\shar\path...
  355. * the UNC style file specs
  356. */
  357. if ((filename[0] == '\\') && (filename[1] == '\\'))
  358. return (ISC_TRUE);
  359. if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '\\')
  360. return (ISC_TRUE);
  361. if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '/')
  362. return (ISC_TRUE);
  363. return (ISC_FALSE);
  364. }
  365. isc_boolean_t
  366. isc_file_iscurrentdir(const char *filename) {
  367. REQUIRE(filename != NULL);
  368. return (ISC_TF(filename[0] == '.' && filename[1] == '\0'));
  369. }
  370. isc_boolean_t
  371. isc_file_ischdiridempotent(const char *filename) {
  372. REQUIRE(filename != NULL);
  373. if (isc_file_isabsolute(filename))
  374. return (ISC_TRUE);
  375. if (filename[0] == '\\')
  376. return (ISC_TRUE);
  377. if (filename[0] == '/')
  378. return (ISC_TRUE);
  379. if (isc_file_iscurrentdir(filename))
  380. return (ISC_TRUE);
  381. return (ISC_FALSE);
  382. }
  383. const char *
  384. isc_file_basename(const char *filename) {
  385. char *s;
  386. REQUIRE(filename != NULL);
  387. s = strrchr(filename, '\\');
  388. if (s == NULL)
  389. return (filename);
  390. return (s + 1);
  391. }
  392. isc_result_t
  393. isc_file_progname(const char *filename, char *progname, size_t namelen) {
  394. const char *s;
  395. char *p;
  396. size_t len;
  397. REQUIRE(filename != NULL);
  398. REQUIRE(progname != NULL);
  399. /*
  400. * Strip the path from the name
  401. */
  402. s = isc_file_basename(filename);
  403. if (s == NULL) {
  404. return (ISC_R_NOSPACE);
  405. }
  406. /*
  407. * Strip any and all suffixes
  408. */
  409. p = strchr(s, '.');
  410. if (p == NULL) {
  411. if (namelen <= strlen(s))
  412. return (ISC_R_NOSPACE);
  413. strcpy(progname, s);
  414. return (ISC_R_SUCCESS);
  415. }
  416. /*
  417. * Copy the result to the buffer
  418. */
  419. len = p - s;
  420. if (len >= namelen)
  421. return (ISC_R_NOSPACE);
  422. strncpy(progname, s, len);
  423. progname[len] = '\0';
  424. return (ISC_R_SUCCESS);
  425. }
  426. isc_result_t
  427. isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
  428. char *ptrname;
  429. DWORD retval;
  430. REQUIRE(filename != NULL);
  431. REQUIRE(path != NULL);
  432. retval = GetFullPathName(filename, pathlen, path, &ptrname);
  433. /* Something went wrong in getting the path */
  434. if (retval == 0)
  435. return (ISC_R_NOTFOUND);
  436. /* Caller needs to provide a larger buffer to contain the string */
  437. if (retval >= pathlen)
  438. return (ISC_R_NOSPACE);
  439. return (ISC_R_SUCCESS);
  440. }
  441. isc_result_t
  442. isc_file_truncate(const char *filename, isc_offset_t size) {
  443. int fh;
  444. REQUIRE(filename != NULL && size >= 0);
  445. if ((fh = open(filename, _O_RDWR | _O_BINARY)) < 0)
  446. return (isc__errno2result(errno));
  447. if(_chsize(fh, size) != 0) {
  448. close(fh);
  449. return (isc__errno2result(errno));
  450. }
  451. close(fh);
  452. return (ISC_R_SUCCESS);
  453. }
  454. isc_result_t
  455. isc_file_safecreate(const char *filename, FILE **fp) {
  456. isc_result_t result;
  457. int flags;
  458. struct stat sb;
  459. FILE *f;
  460. int fd;
  461. REQUIRE(filename != NULL);
  462. REQUIRE(fp != NULL && *fp == NULL);
  463. result = file_stats(filename, &sb);
  464. if (result == ISC_R_SUCCESS) {
  465. if ((sb.st_mode & S_IFREG) == 0)
  466. return (ISC_R_INVALIDFILE);
  467. flags = O_WRONLY | O_TRUNC;
  468. } else if (result == ISC_R_FILENOTFOUND) {
  469. flags = O_WRONLY | O_CREAT | O_EXCL;
  470. } else
  471. return (result);
  472. fd = open(filename, flags, S_IRUSR | S_IWUSR);
  473. if (fd == -1)
  474. return (isc__errno2result(errno));
  475. f = fdopen(fd, "w");
  476. if (f == NULL) {
  477. result = isc__errno2result(errno);
  478. close(fd);
  479. return (result);
  480. }
  481. *fp = f;
  482. return (ISC_R_SUCCESS);
  483. }
  484. isc_result_t
  485. isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirname, char **basename)
  486. {
  487. char *dir, *file, *slash;
  488. char *backslash;
  489. slash = strrchr(path, '/');
  490. backslash = strrchr(path, '\\');
  491. if ((slash != NULL && backslash != NULL && backslash > slash) ||
  492. (slash == NULL && backslash != NULL))
  493. slash = backslash;
  494. if (slash == path) {
  495. file = ++slash;
  496. dir = isc_mem_strdup(mctx, "/");
  497. } else if (slash != NULL) {
  498. file = ++slash;
  499. dir = isc_mem_allocate(mctx, slash - path);
  500. if (dir != NULL)
  501. strlcpy(dir, path, slash - path);
  502. } else {
  503. file = path;
  504. dir = isc_mem_strdup(mctx, ".");
  505. }
  506. if (dir == NULL)
  507. return (ISC_R_NOMEMORY);
  508. if (*file == '\0') {
  509. isc_mem_free(mctx, dir);
  510. return (ISC_R_INVALIDFILE);
  511. }
  512. *dirname = dir;
  513. *basename = file;
  514. return (ISC_R_SUCCESS);
  515. }