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