1 /* $NetBSD: file.c,v 1.2 2024/08/18 20:47:15 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2004, 2005, 2007, 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (C) 2000-2002 Internet Software Consortium. 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* 21 * Portions Copyright (c) 1987, 1993 22 * The Regents of the University of California. All rights reserved. 23 * 24 * Redistribution and use in source and binary forms, with or without 25 * modification, are permitted provided that the following conditions 26 * are met: 27 * 1. Redistributions of source code must retain the above copyright 28 * notice, this list of conditions and the following disclaimer. 29 * 2. Redistributions in binary form must reproduce the above copyright 30 * notice, this list of conditions and the following disclaimer in the 31 * documentation and/or other materials provided with the distribution. 32 * 3. All advertising materials mentioning features or use of this software 33 * must display the following acknowledgement: 34 * This product includes software developed by the University of 35 * California, Berkeley and its contributors. 36 * 4. Neither the name of the University nor the names of its contributors 37 * may be used to endorse or promote products derived from this software 38 * without specific prior written permission. 39 * 40 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 41 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 43 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 44 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 45 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 46 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 48 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 49 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 50 * SUCH DAMAGE. 51 */ 52 53 /* Id */ 54 55 /*! \file */ 56 57 #include <config.h> 58 59 #include <errno.h> 60 #include <fcntl.h> 61 #include <limits.h> 62 #include <stdlib.h> 63 #include <time.h> /* Required for utimes on some platforms. */ 64 #include <unistd.h> /* Required for mkstemp on NetBSD. */ 65 66 67 #include <sys/stat.h> 68 #include <sys/time.h> 69 70 #include <isc/dir.h> 71 #include <isc/file.h> 72 #include <isc/log.h> 73 #include <isc/mem.h> 74 #include <isc/random.h> 75 #include <isc/string.h> 76 #include <isc/time.h> 77 #include <isc/util.h> 78 79 #include "errno2result.h" 80 #include "ntp_stdlib.h" /* NTP change for strlcpy, strlcat */ 81 82 /* 83 * XXXDCL As the API for accessing file statistics undoubtedly gets expanded, 84 * it might be good to provide a mechanism that allows for the results 85 * of a previous stat() to be used again without having to do another stat, 86 * such as perl's mechanism of using "_" in place of a file name to indicate 87 * that the results of the last stat should be used. But then you get into 88 * annoying MP issues. BTW, Win32 has stat(). 89 */ 90 static isc_result_t 91 file_stats(const char *file, struct stat *stats) { 92 isc_result_t result = ISC_R_SUCCESS; 93 94 REQUIRE(file != NULL); 95 REQUIRE(stats != NULL); 96 97 if (stat(file, stats) != 0) 98 result = isc__errno2result(errno); 99 100 return (result); 101 } 102 103 isc_result_t 104 isc_file_getmodtime(const char *file, isc_time_t *itime) { 105 isc_result_t result; 106 struct stat stats; 107 108 REQUIRE(file != NULL); 109 REQUIRE(itime != NULL); 110 111 result = file_stats(file, &stats); 112 113 if (result == ISC_R_SUCCESS) 114 /* 115 * XXXDCL some operating systems provide nanoseconds, too, 116 * such as BSD/OS via st_mtimespec. 117 */ 118 isc_time_set(itime, stats.st_mtime, 0); 119 120 return (result); 121 } 122 123 isc_result_t 124 isc_file_settime(const char *file, isc_time_t *itime) { 125 struct timeval times[2]; 126 127 REQUIRE(file != NULL && itime != NULL); 128 129 /* 130 * tv_sec is at least a 32 bit quantity on all platforms we're 131 * dealing with, but it is signed on most (all?) of them, 132 * so we need to make sure the high bit isn't set. This unfortunately 133 * loses when either: 134 * * tv_sec becomes a signed 64 bit integer but long is 32 bits 135 * and isc_time_seconds > LONG_MAX, or 136 * * isc_time_seconds is changed to be > 32 bits but long is 32 bits 137 * and isc_time_seconds has at least 33 significant bits. 138 */ 139 times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(itime); 140 141 /* 142 * Here is the real check for the high bit being set. 143 */ 144 if ((times[0].tv_sec & 145 (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0) 146 return (ISC_R_RANGE); 147 148 /* 149 * isc_time_nanoseconds guarantees a value that divided by 1000 will 150 * fit into the minimum possible size tv_usec field. Unfortunately, 151 * we don't know what that type is so can't cast directly ... but 152 * we can at least cast to signed so the IRIX compiler shuts up. 153 */ 154 times[0].tv_usec = times[1].tv_usec = 155 (isc_int32_t)(isc_time_nanoseconds(itime) / 1000); 156 157 if (utimes(file, times) < 0) 158 return (isc__errno2result(errno)); 159 160 return (ISC_R_SUCCESS); 161 } 162 163 #undef TEMPLATE 164 #define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */ 165 166 isc_result_t 167 isc_file_mktemplate(const char *path, char *buf, size_t buflen) { 168 return (isc_file_template(path, TEMPLATE, buf, buflen)); 169 } 170 171 isc_result_t 172 isc_file_template(const char *path, const char *templet, char *buf, 173 size_t buflen) { 174 char *s; 175 176 REQUIRE(path != NULL); 177 REQUIRE(templet != NULL); 178 REQUIRE(buf != NULL); 179 180 s = strrchr(templet, '/'); 181 if (s != NULL) 182 templet = s + 1; 183 184 s = strrchr(path, '/'); 185 186 if (s != NULL) { 187 if ((s - path + 1 + strlen(templet) + 1) > buflen) 188 return (ISC_R_NOSPACE); 189 190 strlcpy(buf, path, buflen); 191 buf[s - path + 1] = '\0'; 192 strlcat(buf, templet, buflen); 193 } else { 194 if ((strlen(templet) + 1) > buflen) 195 return (ISC_R_NOSPACE); 196 197 strlcpy(buf, templet, buflen); 198 } 199 200 return (ISC_R_SUCCESS); 201 } 202 203 static char alphnum[] = 204 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 205 206 isc_result_t 207 isc_file_renameunique(const char *file, char *templet) { 208 char *x; 209 char *cp; 210 isc_uint32_t which; 211 212 REQUIRE(file != NULL); 213 REQUIRE(templet != NULL); 214 215 cp = templet; 216 while (*cp != '\0') 217 cp++; 218 if (cp == templet) 219 return (ISC_R_FAILURE); 220 221 x = cp--; 222 while (cp >= templet && *cp == 'X') { 223 isc_random_get(&which); 224 *cp = alphnum[which % (sizeof(alphnum) - 1)]; 225 x = cp--; 226 } 227 while (link(file, templet) == -1) { 228 if (errno != EEXIST) 229 return (isc__errno2result(errno)); 230 for (cp = x;;) { 231 char *t; 232 if (*cp == '\0') 233 return (ISC_R_FAILURE); 234 t = strchr(alphnum, *cp); 235 if (t == NULL || *++t == '\0') 236 *cp++ = alphnum[0]; 237 else { 238 *cp = *t; 239 break; 240 } 241 } 242 } 243 if (unlink(file) < 0) 244 if (errno != ENOENT) 245 return (isc__errno2result(errno)); 246 return (ISC_R_SUCCESS); 247 } 248 249 isc_result_t 250 isc_file_openunique(char *templet, FILE **fp) { 251 int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; 252 return (isc_file_openuniquemode(templet, mode, fp)); 253 } 254 255 isc_result_t 256 isc_file_openuniqueprivate(char *templet, FILE **fp) { 257 int mode = S_IWUSR|S_IRUSR; 258 return (isc_file_openuniquemode(templet, mode, fp)); 259 } 260 261 isc_result_t 262 isc_file_openuniquemode(char *templet, int mode, FILE **fp) { 263 int fd; 264 FILE *f; 265 isc_result_t result = ISC_R_SUCCESS; 266 char *x; 267 char *cp; 268 isc_uint32_t which; 269 270 REQUIRE(templet != NULL); 271 REQUIRE(fp != NULL && *fp == NULL); 272 273 cp = templet; 274 while (*cp != '\0') 275 cp++; 276 if (cp == templet) 277 return (ISC_R_FAILURE); 278 279 x = cp--; 280 while (cp >= templet && *cp == 'X') { 281 isc_random_get(&which); 282 *cp = alphnum[which % (sizeof(alphnum) - 1)]; 283 x = cp--; 284 } 285 286 287 while ((fd = open(templet, O_RDWR|O_CREAT|O_EXCL, mode)) == -1) { 288 if (errno != EEXIST) 289 return (isc__errno2result(errno)); 290 for (cp = x;;) { 291 char *t; 292 if (*cp == '\0') 293 return (ISC_R_FAILURE); 294 t = strchr(alphnum, *cp); 295 if (t == NULL || *++t == '\0') 296 *cp++ = alphnum[0]; 297 else { 298 *cp = *t; 299 break; 300 } 301 } 302 } 303 f = fdopen(fd, "w+"); 304 if (f == NULL) { 305 result = isc__errno2result(errno); 306 if (remove(templet) < 0) { 307 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 308 ISC_LOGMODULE_FILE, ISC_LOG_ERROR, 309 "remove '%s': failed", templet); 310 } 311 (void)close(fd); 312 } else 313 *fp = f; 314 315 return (result); 316 } 317 318 isc_result_t 319 isc_file_remove(const char *filename) { 320 int r; 321 322 REQUIRE(filename != NULL); 323 324 r = unlink(filename); 325 if (r == 0) 326 return (ISC_R_SUCCESS); 327 else 328 return (isc__errno2result(errno)); 329 } 330 331 isc_result_t 332 isc_file_rename(const char *oldname, const char *newname) { 333 int r; 334 335 REQUIRE(oldname != NULL); 336 REQUIRE(newname != NULL); 337 338 r = rename(oldname, newname); 339 if (r == 0) 340 return (ISC_R_SUCCESS); 341 else 342 return (isc__errno2result(errno)); 343 } 344 345 isc_boolean_t 346 isc_file_exists(const char *pathname) { 347 struct stat stats; 348 349 REQUIRE(pathname != NULL); 350 351 return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS)); 352 } 353 354 isc_result_t 355 isc_file_isplainfile(const char *filename) { 356 /* 357 * This function returns success if filename is a plain file. 358 */ 359 struct stat filestat; 360 memset(&filestat,0,sizeof(struct stat)); 361 362 if ((stat(filename, &filestat)) == -1) 363 return(isc__errno2result(errno)); 364 365 if(! S_ISREG(filestat.st_mode)) 366 return(ISC_R_INVALIDFILE); 367 368 return(ISC_R_SUCCESS); 369 } 370 371 isc_boolean_t 372 isc_file_isabsolute(const char *filename) { 373 REQUIRE(filename != NULL); 374 return (ISC_TF(filename[0] == '/')); 375 } 376 377 isc_boolean_t 378 isc_file_iscurrentdir(const char *filename) { 379 REQUIRE(filename != NULL); 380 return (ISC_TF(filename[0] == '.' && filename[1] == '\0')); 381 } 382 383 isc_boolean_t 384 isc_file_ischdiridempotent(const char *filename) { 385 REQUIRE(filename != NULL); 386 if (isc_file_isabsolute(filename)) 387 return (ISC_TRUE); 388 if (isc_file_iscurrentdir(filename)) 389 return (ISC_TRUE); 390 return (ISC_FALSE); 391 } 392 393 const char * 394 isc_file_basename(const char *filename) { 395 char *s; 396 397 REQUIRE(filename != NULL); 398 399 s = strrchr(filename, '/'); 400 if (s == NULL) 401 return (filename); 402 403 return (s + 1); 404 } 405 406 isc_result_t 407 isc_file_progname(const char *filename, char *buf, size_t buflen) { 408 const char *base; 409 size_t len; 410 411 REQUIRE(filename != NULL); 412 REQUIRE(buf != NULL); 413 414 base = isc_file_basename(filename); 415 len = strlen(base) + 1; 416 417 if (len > buflen) 418 return (ISC_R_NOSPACE); 419 memcpy(buf, base, len); 420 421 return (ISC_R_SUCCESS); 422 } 423 424 /* 425 * Put the absolute name of the current directory into 'dirname', which is 426 * a buffer of at least 'length' characters. End the string with the 427 * appropriate path separator, such that the final product could be 428 * concatenated with a relative pathname to make a valid pathname string. 429 */ 430 static isc_result_t 431 dir_current(char *dirname, size_t length) { 432 char *cwd; 433 isc_result_t result = ISC_R_SUCCESS; 434 435 REQUIRE(dirname != NULL); 436 REQUIRE(length > 0U); 437 438 cwd = getcwd(dirname, length); 439 440 if (cwd == NULL) { 441 if (errno == ERANGE) 442 result = ISC_R_NOSPACE; 443 else 444 result = isc__errno2result(errno); 445 } else { 446 if (strlen(dirname) + 1 == length) 447 result = ISC_R_NOSPACE; 448 else if (dirname[1] != '\0') 449 strlcat(dirname, "/", length); 450 } 451 452 return (result); 453 } 454 455 isc_result_t 456 isc_file_absolutepath(const char *filename, char *path, size_t pathlen) { 457 isc_result_t result; 458 result = dir_current(path, pathlen); 459 if (result != ISC_R_SUCCESS) 460 return (result); 461 if (strlen(path) + strlen(filename) + 1 > pathlen) 462 return (ISC_R_NOSPACE); 463 strlcat(path, filename, pathlen); 464 return (ISC_R_SUCCESS); 465 } 466 467 isc_result_t 468 isc_file_truncate(const char *filename, isc_offset_t size) { 469 isc_result_t result = ISC_R_SUCCESS; 470 471 if (truncate(filename, size) < 0) 472 result = isc__errno2result(errno); 473 return (result); 474 } 475 476 isc_result_t 477 isc_file_safecreate(const char *filename, FILE **fp) { 478 isc_result_t result; 479 int flags; 480 struct stat sb; 481 FILE *f; 482 int fd; 483 484 REQUIRE(filename != NULL); 485 REQUIRE(fp != NULL && *fp == NULL); 486 487 result = file_stats(filename, &sb); 488 if (result == ISC_R_SUCCESS) { 489 if ((sb.st_mode & S_IFREG) == 0) 490 return (ISC_R_INVALIDFILE); 491 flags = O_WRONLY | O_TRUNC; 492 } else if (result == ISC_R_FILENOTFOUND) { 493 flags = O_WRONLY | O_CREAT | O_EXCL; 494 } else 495 return (result); 496 497 fd = open(filename, flags, S_IRUSR | S_IWUSR); 498 if (fd == -1) 499 return (isc__errno2result(errno)); 500 501 f = fdopen(fd, "w"); 502 if (f == NULL) { 503 result = isc__errno2result(errno); 504 close(fd); 505 return (result); 506 } 507 508 *fp = f; 509 return (ISC_R_SUCCESS); 510 } 511 512 isc_result_t 513 isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirnam, char **basenam) 514 { 515 char *dir, *file, *slash; 516 517 REQUIRE(path != NULL); 518 519 slash = strrchr(path, '/'); 520 521 if (slash == path) { 522 file = ++slash; 523 dir = isc_mem_strdup(mctx, "/"); 524 } else if (slash != NULL) { 525 file = ++slash; 526 dir = isc_mem_allocate(mctx, slash - path); 527 if (dir != NULL) 528 strlcpy(dir, path, slash - path); 529 } else { 530 file = path; 531 dir = isc_mem_strdup(mctx, "."); 532 } 533 534 if (dir == NULL) 535 return (ISC_R_NOMEMORY); 536 537 if (*file == '\0') { 538 isc_mem_free(mctx, dir); 539 return (ISC_R_INVALIDFILE); 540 } 541 542 *dirnam = dir; 543 *basenam = file; 544 545 return (ISC_R_SUCCESS); 546 } 547