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