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