file.c revision 224092
1/* 2 * Copyright (C) 2004, 2005, 2007, 2009, 2011 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: file.c,v 1.57 2011-01-11 23:47:14 tbox Exp $ */ 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_boolean_t 352isc_file_isabsolute(const char *filename) { 353 REQUIRE(filename != NULL); 354 return (ISC_TF(filename[0] == '/')); 355} 356 357isc_boolean_t 358isc_file_iscurrentdir(const char *filename) { 359 REQUIRE(filename != NULL); 360 return (ISC_TF(filename[0] == '.' && filename[1] == '\0')); 361} 362 363isc_boolean_t 364isc_file_ischdiridempotent(const char *filename) { 365 REQUIRE(filename != NULL); 366 if (isc_file_isabsolute(filename)) 367 return (ISC_TRUE); 368 if (isc_file_iscurrentdir(filename)) 369 return (ISC_TRUE); 370 return (ISC_FALSE); 371} 372 373const char * 374isc_file_basename(const char *filename) { 375 char *s; 376 377 REQUIRE(filename != NULL); 378 379 s = strrchr(filename, '/'); 380 if (s == NULL) 381 return (filename); 382 383 return (s + 1); 384} 385 386isc_result_t 387isc_file_progname(const char *filename, char *buf, size_t buflen) { 388 const char *base; 389 size_t len; 390 391 REQUIRE(filename != NULL); 392 REQUIRE(buf != NULL); 393 394 base = isc_file_basename(filename); 395 len = strlen(base) + 1; 396 397 if (len > buflen) 398 return (ISC_R_NOSPACE); 399 memcpy(buf, base, len); 400 401 return (ISC_R_SUCCESS); 402} 403 404/* 405 * Put the absolute name of the current directory into 'dirname', which is 406 * a buffer of at least 'length' characters. End the string with the 407 * appropriate path separator, such that the final product could be 408 * concatenated with a relative pathname to make a valid pathname string. 409 */ 410static isc_result_t 411dir_current(char *dirname, size_t length) { 412 char *cwd; 413 isc_result_t result = ISC_R_SUCCESS; 414 415 REQUIRE(dirname != NULL); 416 REQUIRE(length > 0U); 417 418 cwd = getcwd(dirname, length); 419 420 if (cwd == NULL) { 421 if (errno == ERANGE) 422 result = ISC_R_NOSPACE; 423 else 424 result = isc__errno2result(errno); 425 } else { 426 if (strlen(dirname) + 1 == length) 427 result = ISC_R_NOSPACE; 428 else if (dirname[1] != '\0') 429 strcat(dirname, "/"); 430 } 431 432 return (result); 433} 434 435isc_result_t 436isc_file_absolutepath(const char *filename, char *path, size_t pathlen) { 437 isc_result_t result; 438 result = dir_current(path, pathlen); 439 if (result != ISC_R_SUCCESS) 440 return (result); 441 if (strlen(path) + strlen(filename) + 1 > pathlen) 442 return (ISC_R_NOSPACE); 443 strcat(path, filename); 444 return (ISC_R_SUCCESS); 445} 446 447isc_result_t 448isc_file_truncate(const char *filename, isc_offset_t size) { 449 isc_result_t result = ISC_R_SUCCESS; 450 451 if (truncate(filename, size) < 0) 452 result = isc__errno2result(errno); 453 return (result); 454} 455 456isc_result_t 457isc_file_safecreate(const char *filename, FILE **fp) { 458 isc_result_t result; 459 int flags; 460 struct stat sb; 461 FILE *f; 462 int fd; 463 464 REQUIRE(filename != NULL); 465 REQUIRE(fp != NULL && *fp == NULL); 466 467 result = file_stats(filename, &sb); 468 if (result == ISC_R_SUCCESS) { 469 if ((sb.st_mode & S_IFREG) == 0) 470 return (ISC_R_INVALIDFILE); 471 flags = O_WRONLY | O_TRUNC; 472 } else if (result == ISC_R_FILENOTFOUND) { 473 flags = O_WRONLY | O_CREAT | O_EXCL; 474 } else 475 return (result); 476 477 fd = open(filename, flags, S_IRUSR | S_IWUSR); 478 if (fd == -1) 479 return (isc__errno2result(errno)); 480 481 f = fdopen(fd, "w"); 482 if (f == NULL) { 483 result = isc__errno2result(errno); 484 close(fd); 485 return (result); 486 } 487 488 *fp = f; 489 return (ISC_R_SUCCESS); 490} 491 492isc_result_t 493isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirname, char **basename) 494{ 495 char *dir, *file, *slash; 496 497 slash = strrchr(path, '/'); 498 499 if (slash == path) { 500 file = ++slash; 501 dir = isc_mem_strdup(mctx, "/"); 502 } else if (slash != NULL) { 503 file = ++slash; 504 dir = isc_mem_allocate(mctx, slash - path); 505 if (dir != NULL) 506 strlcpy(dir, path, slash - path); 507 } else { 508 file = path; 509 dir = isc_mem_strdup(mctx, "."); 510 } 511 512 if (dir == NULL) 513 return (ISC_R_NOMEMORY); 514 515 if (*file == '\0') { 516 isc_mem_free(mctx, dir); 517 return (ISC_R_INVALIDFILE); 518 } 519 520 *dirname = dir; 521 *basename = file; 522 523 return (ISC_R_SUCCESS); 524} 525