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