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