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