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 79/* 80 * XXXDCL As the API for accessing file statistics undoubtedly gets expanded, 81 * it might be good to provide a mechanism that allows for the results 82 * of a previous stat() to be used again without having to do another stat, 83 * such as perl's mechanism of using "_" in place of a file name to indicate 84 * that the results of the last stat should be used. But then you get into 85 * annoying MP issues. BTW, Win32 has stat(). 86 */ 87static isc_result_t 88file_stats(const char *file, struct stat *stats) { 89 isc_result_t result = ISC_R_SUCCESS; 90 91 REQUIRE(file != NULL); 92 REQUIRE(stats != NULL); 93 94 if (stat(file, stats) != 0) 95 result = isc__errno2result(errno); 96 97 return (result); 98} 99 100isc_result_t 101isc_file_getmodtime(const char *file, isc_time_t *time) { 102 isc_result_t result; 103 struct stat stats; 104 105 REQUIRE(file != NULL); 106 REQUIRE(time != NULL); 107 108 result = file_stats(file, &stats); 109 110 if (result == ISC_R_SUCCESS) 111 /* 112 * XXXDCL some operating systems provide nanoseconds, too, 113 * such as BSD/OS via st_mtimespec. 114 */ 115 isc_time_set(time, stats.st_mtime, 0); 116 117 return (result); 118} 119 120isc_result_t 121isc_file_settime(const char *file, isc_time_t *time) { 122 struct timeval times[2]; 123 124 REQUIRE(file != NULL && time != NULL); 125 126 /* 127 * tv_sec is at least a 32 bit quantity on all platforms we're 128 * dealing with, but it is signed on most (all?) of them, 129 * so we need to make sure the high bit isn't set. This unfortunately 130 * loses when either: 131 * * tv_sec becomes a signed 64 bit integer but long is 32 bits 132 * and isc_time_seconds > LONG_MAX, or 133 * * isc_time_seconds is changed to be > 32 bits but long is 32 bits 134 * and isc_time_seconds has at least 33 significant bits. 135 */ 136 times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(time); 137 138 /* 139 * Here is the real check for the high bit being set. 140 */ 141 if ((times[0].tv_sec & 142 (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0) 143 return (ISC_R_RANGE); 144 145 /* 146 * isc_time_nanoseconds guarantees a value that divided by 1000 will 147 * fit into the minimum possible size tv_usec field. Unfortunately, 148 * we don't know what that type is so can't cast directly ... but 149 * we can at least cast to signed so the IRIX compiler shuts up. 150 */ 151 times[0].tv_usec = times[1].tv_usec = 152 (isc_int32_t)(isc_time_nanoseconds(time) / 1000); 153 154 if (utimes(file, times) < 0) 155 return (isc__errno2result(errno)); 156 157 return (ISC_R_SUCCESS); 158} 159 160#undef TEMPLATE 161#define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */ 162 163isc_result_t 164isc_file_mktemplate(const char *path, char *buf, size_t buflen) { 165 return (isc_file_template(path, TEMPLATE, buf, buflen)); 166} 167 168isc_result_t 169isc_file_template(const char *path, const char *templet, char *buf, 170 size_t buflen) { 171 char *s; 172 173 REQUIRE(path != NULL); 174 REQUIRE(templet != NULL); 175 REQUIRE(buf != NULL); 176 177 s = strrchr(templet, '/'); 178 if (s != NULL) 179 templet = s + 1; 180 181 s = strrchr(path, '/'); 182 183 if (s != NULL) { 184 if ((s - path + 1 + strlen(templet) + 1) > buflen) 185 return (ISC_R_NOSPACE); 186 187 strncpy(buf, path, s - path + 1); 188 buf[s - path + 1] = '\0'; 189 strcat(buf, templet); 190 } else { 191 if ((strlen(templet) + 1) > buflen) 192 return (ISC_R_NOSPACE); 193 194 strcpy(buf, templet); 195 } 196 197 return (ISC_R_SUCCESS); 198} 199 200static char alphnum[] = 201 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 202 203isc_result_t 204isc_file_renameunique(const char *file, char *templet) { 205 char *x; 206 char *cp; 207 isc_uint32_t which; 208 209 REQUIRE(file != NULL); 210 REQUIRE(templet != NULL); 211 212 cp = templet; 213 while (*cp != '\0') 214 cp++; 215 if (cp == templet) 216 return (ISC_R_FAILURE); 217 218 x = cp--; 219 while (cp >= templet && *cp == 'X') { 220 isc_random_get(&which); 221 *cp = alphnum[which % (sizeof(alphnum) - 1)]; 222 x = cp--; 223 } 224 while (link(file, templet) == -1) { 225 if (errno != EEXIST) 226 return (isc__errno2result(errno)); 227 for (cp = x;;) { 228 char *t; 229 if (*cp == '\0') 230 return (ISC_R_FAILURE); 231 t = strchr(alphnum, *cp); 232 if (t == NULL || *++t == '\0') 233 *cp++ = alphnum[0]; 234 else { 235 *cp = *t; 236 break; 237 } 238 } 239 } 240 if (unlink(file) < 0) 241 if (errno != ENOENT) 242 return (isc__errno2result(errno)); 243 return (ISC_R_SUCCESS); 244} 245 246isc_result_t 247isc_file_openunique(char *templet, FILE **fp) { 248 int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; 249 return (isc_file_openuniquemode(templet, mode, fp)); 250} 251 252isc_result_t 253isc_file_openuniqueprivate(char *templet, FILE **fp) { 254 int mode = S_IWUSR|S_IRUSR; 255 return (isc_file_openuniquemode(templet, mode, fp)); 256} 257 258isc_result_t 259isc_file_openuniquemode(char *templet, int mode, FILE **fp) { 260 int fd; 261 FILE *f; 262 isc_result_t result = ISC_R_SUCCESS; 263 char *x; 264 char *cp; 265 isc_uint32_t which; 266 267 REQUIRE(templet != NULL); 268 REQUIRE(fp != NULL && *fp == NULL); 269 270 cp = templet; 271 while (*cp != '\0') 272 cp++; 273 if (cp == templet) 274 return (ISC_R_FAILURE); 275 276 x = cp--; 277 while (cp >= templet && *cp == 'X') { 278 isc_random_get(&which); 279 *cp = alphnum[which % (sizeof(alphnum) - 1)]; 280 x = cp--; 281 } 282 283 284 while ((fd = open(templet, O_RDWR|O_CREAT|O_EXCL, mode)) == -1) { 285 if (errno != EEXIST) 286 return (isc__errno2result(errno)); 287 for (cp = x;;) { 288 char *t; 289 if (*cp == '\0') 290 return (ISC_R_FAILURE); 291 t = strchr(alphnum, *cp); 292 if (t == NULL || *++t == '\0') 293 *cp++ = alphnum[0]; 294 else { 295 *cp = *t; 296 break; 297 } 298 } 299 } 300 f = fdopen(fd, "w+"); 301 if (f == NULL) { 302 result = isc__errno2result(errno); 303 if (remove(templet) < 0) { 304 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 305 ISC_LOGMODULE_FILE, ISC_LOG_ERROR, 306 "remove '%s': failed", templet); 307 } 308 (void)close(fd); 309 } else 310 *fp = f; 311 312 return (result); 313} 314 315isc_result_t 316isc_file_remove(const char *filename) { 317 int r; 318 319 REQUIRE(filename != NULL); 320 321 r = unlink(filename); 322 if (r == 0) 323 return (ISC_R_SUCCESS); 324 else 325 return (isc__errno2result(errno)); 326} 327 328isc_result_t 329isc_file_rename(const char *oldname, const char *newname) { 330 int r; 331 332 REQUIRE(oldname != NULL); 333 REQUIRE(newname != NULL); 334 335 r = rename(oldname, newname); 336 if (r == 0) 337 return (ISC_R_SUCCESS); 338 else 339 return (isc__errno2result(errno)); 340} 341 342isc_boolean_t 343isc_file_exists(const char *pathname) { 344 struct stat stats; 345 346 REQUIRE(pathname != NULL); 347 348 return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS)); 349} 350 351isc_result_t 352isc_file_isplainfile(const char *filename) { 353 /* 354 * This function returns success if filename is a plain file. 355 */ 356 struct stat filestat; 357 memset(&filestat,0,sizeof(struct stat)); 358 359 if ((stat(filename, &filestat)) == -1) 360 return(isc__errno2result(errno)); 361 362 if(! S_ISREG(filestat.st_mode)) 363 return(ISC_R_INVALIDFILE); 364 365 return(ISC_R_SUCCESS); 366} 367 368isc_boolean_t 369isc_file_isabsolute(const char *filename) { 370 REQUIRE(filename != NULL); 371 return (ISC_TF(filename[0] == '/')); 372} 373 374isc_boolean_t 375isc_file_iscurrentdir(const char *filename) { 376 REQUIRE(filename != NULL); 377 return (ISC_TF(filename[0] == '.' && filename[1] == '\0')); 378} 379 380isc_boolean_t 381isc_file_ischdiridempotent(const char *filename) { 382 REQUIRE(filename != NULL); 383 if (isc_file_isabsolute(filename)) 384 return (ISC_TRUE); 385 if (isc_file_iscurrentdir(filename)) 386 return (ISC_TRUE); 387 return (ISC_FALSE); 388} 389 390const char * 391isc_file_basename(const char *filename) { 392 char *s; 393 394 REQUIRE(filename != NULL); 395 396 s = strrchr(filename, '/'); 397 if (s == NULL) 398 return (filename); 399 400 return (s + 1); 401} 402 403isc_result_t 404isc_file_progname(const char *filename, char *buf, size_t buflen) { 405 const char *base; 406 size_t len; 407 408 REQUIRE(filename != NULL); 409 REQUIRE(buf != NULL); 410 411 base = isc_file_basename(filename); 412 len = strlen(base) + 1; 413 414 if (len > buflen) 415 return (ISC_R_NOSPACE); 416 memcpy(buf, base, len); 417 418 return (ISC_R_SUCCESS); 419} 420 421/* 422 * Put the absolute name of the current directory into 'dirname', which is 423 * a buffer of at least 'length' characters. End the string with the 424 * appropriate path separator, such that the final product could be 425 * concatenated with a relative pathname to make a valid pathname string. 426 */ 427static isc_result_t 428dir_current(char *dirname, size_t length) { 429 char *cwd; 430 isc_result_t result = ISC_R_SUCCESS; 431 432 REQUIRE(dirname != NULL); 433 REQUIRE(length > 0U); 434 435 cwd = getcwd(dirname, length); 436 437 if (cwd == NULL) { 438 if (errno == ERANGE) 439 result = ISC_R_NOSPACE; 440 else 441 result = isc__errno2result(errno); 442 } else { 443 if (strlen(dirname) + 1 == length) 444 result = ISC_R_NOSPACE; 445 else if (dirname[1] != '\0') 446 strcat(dirname, "/"); 447 } 448 449 return (result); 450} 451 452isc_result_t 453isc_file_absolutepath(const char *filename, char *path, size_t pathlen) { 454 isc_result_t result; 455 result = dir_current(path, pathlen); 456 if (result != ISC_R_SUCCESS) 457 return (result); 458 if (strlen(path) + strlen(filename) + 1 > pathlen) 459 return (ISC_R_NOSPACE); 460 strcat(path, filename); 461 return (ISC_R_SUCCESS); 462} 463 464isc_result_t 465isc_file_truncate(const char *filename, isc_offset_t size) { 466 isc_result_t result = ISC_R_SUCCESS; 467 468 if (truncate(filename, size) < 0) 469 result = isc__errno2result(errno); 470 return (result); 471} 472 473isc_result_t 474isc_file_safecreate(const char *filename, FILE **fp) { 475 isc_result_t result; 476 int flags; 477 struct stat sb; 478 FILE *f; 479 int fd; 480 481 REQUIRE(filename != NULL); 482 REQUIRE(fp != NULL && *fp == NULL); 483 484 result = file_stats(filename, &sb); 485 if (result == ISC_R_SUCCESS) { 486 if ((sb.st_mode & S_IFREG) == 0) 487 return (ISC_R_INVALIDFILE); 488 flags = O_WRONLY | O_TRUNC; 489 } else if (result == ISC_R_FILENOTFOUND) { 490 flags = O_WRONLY | O_CREAT | O_EXCL; 491 } else 492 return (result); 493 494 fd = open(filename, flags, S_IRUSR | S_IWUSR); 495 if (fd == -1) 496 return (isc__errno2result(errno)); 497 498 f = fdopen(fd, "w"); 499 if (f == NULL) { 500 result = isc__errno2result(errno); 501 close(fd); 502 return (result); 503 } 504 505 *fp = f; 506 return (ISC_R_SUCCESS); 507} 508 509isc_result_t 510isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirname, char **basename) 511{ 512 char *dir, *file, *slash; 513 514 slash = strrchr(path, '/'); 515 516 if (slash == path) { 517 file = ++slash; 518 dir = isc_mem_strdup(mctx, "/"); 519 } else if (slash != NULL) { 520 file = ++slash; 521 dir = isc_mem_allocate(mctx, slash - path); 522 if (dir != NULL) 523 strlcpy(dir, path, slash - path); 524 } else { 525 file = path; 526 dir = isc_mem_strdup(mctx, "."); 527 } 528 529 if (dir == NULL) 530 return (ISC_R_NOMEMORY); 531 532 if (*file == '\0') { 533 isc_mem_free(mctx, dir); 534 return (ISC_R_INVALIDFILE); 535 } 536 537 *dirname = dir; 538 *basename = file; 539 540 return (ISC_R_SUCCESS); 541} 542