1/* $NetBSD: file.c,v 1.3.4.2 2012/12/15 05:40:11 riz Exp $ */ 2 3/* 4 * Copyright (C) 2004, 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/* Id */ 21 22#include <config.h> 23 24#undef rename 25#include <errno.h> 26#include <limits.h> 27#include <stdlib.h> 28#include <io.h> 29#include <process.h> 30 31#include <sys/stat.h> 32#include <fcntl.h> 33#include <sys/utime.h> 34 35#include <isc/file.h> 36#include <isc/mem.h> 37#include <isc/result.h> 38#include <isc/time.h> 39#include <isc/util.h> 40#include <isc/stat.h> 41#include <isc/string.h> 42 43#include "errno2result.h" 44 45/* 46 * Emulate UNIX mkstemp, which returns an open FD to the new file 47 * 48 */ 49static int 50gettemp(char *path, isc_boolean_t binary, int *doopen) { 51 char *start, *trv; 52 struct stat sbuf; 53 int pid; 54 int flags = O_CREAT|O_EXCL|O_RDWR; 55 56 if (binary) 57 flags |= _O_BINARY; 58 59 trv = strrchr(path, 'X'); 60 trv++; 61 pid = getpid(); 62 /* extra X's get set to 0's */ 63 while (*--trv == 'X') { 64 *trv = (pid % 10) + '0'; 65 pid /= 10; 66 } 67 /* 68 * check the target directory; if you have six X's and it 69 * doesn't exist this runs for a *very* long time. 70 */ 71 for (start = trv + 1;; --trv) { 72 if (trv <= path) 73 break; 74 if (*trv == '\\') { 75 *trv = '\0'; 76 if (stat(path, &sbuf)) 77 return (0); 78 if (!S_ISDIR(sbuf.st_mode)) { 79 errno = ENOTDIR; 80 return (0); 81 } 82 *trv = '\\'; 83 break; 84 } 85 } 86 87 for (;;) { 88 if (doopen) { 89 if ((*doopen = 90 open(path, flags, _S_IREAD | _S_IWRITE)) >= 0) 91 return (1); 92 if (errno != EEXIST) 93 return (0); 94 } else if (stat(path, &sbuf)) 95 return (errno == ENOENT ? 1 : 0); 96 97 /* tricky little algorithm for backward compatibility */ 98 for (trv = start;;) { 99 if (!*trv) 100 return (0); 101 if (*trv == 'z') 102 *trv++ = 'a'; 103 else { 104 if (isdigit(*trv)) 105 *trv = 'a'; 106 else 107 ++*trv; 108 break; 109 } 110 } 111 } 112 /*NOTREACHED*/ 113} 114 115static int 116mkstemp(char *path, isc_boolean_t binary) { 117 int fd; 118 119 return (gettemp(path, binary, &fd) ? fd : -1); 120} 121 122/* 123 * XXXDCL As the API for accessing file statistics undoubtedly gets expanded, 124 * it might be good to provide a mechanism that allows for the results 125 * of a previous stat() to be used again without having to do another stat, 126 * such as perl's mechanism of using "_" in place of a file name to indicate 127 * that the results of the last stat should be used. But then you get into 128 * annoying MP issues. BTW, Win32 has stat(). 129 */ 130static isc_result_t 131file_stats(const char *file, struct stat *stats) { 132 isc_result_t result = ISC_R_SUCCESS; 133 134 REQUIRE(file != NULL); 135 REQUIRE(stats != NULL); 136 137 if (stat(file, stats) != 0) 138 result = isc__errno2result(errno); 139 140 return (result); 141} 142 143/* 144 * isc_file_safemovefile is needed to be defined here to ensure that 145 * any file with the new name is renamed to a backup name and then the 146 * rename is done. If all goes well then the backup can be deleted, 147 * otherwise it gets renamed back. 148 */ 149 150int 151isc_file_safemovefile(const char *oldname, const char *newname) { 152 BOOL filestatus; 153 char buf[512]; 154 struct stat sbuf; 155 BOOL exists = FALSE; 156 int tmpfd; 157 158 /* 159 * Make sure we have something to do 160 */ 161 if (stat(oldname, &sbuf) != 0) { 162 errno = ENOENT; 163 return (-1); 164 } 165 166 /* 167 * Rename to a backup the new file if it still exists 168 */ 169 if (stat(newname, &sbuf) == 0) { 170 exists = TRUE; 171 strcpy(buf, newname); 172 strcat(buf, ".XXXXX"); 173 tmpfd = mkstemp(buf, ISC_TRUE); 174 if (tmpfd > 0) 175 _close(tmpfd); 176 DeleteFile(buf); 177 _chmod(newname, _S_IREAD | _S_IWRITE); 178 179 filestatus = MoveFile(newname, buf); 180 } 181 /* Now rename the file to the new name 182 */ 183 _chmod(oldname, _S_IREAD | _S_IWRITE); 184 185 filestatus = MoveFile(oldname, newname); 186 if (filestatus == 0) { 187 /* 188 * Try to rename the backup back to the original name 189 * if the backup got created 190 */ 191 if (exists == TRUE) { 192 filestatus = MoveFile(buf, newname); 193 if (filestatus == 0) 194 errno = EACCES; 195 } 196 return (-1); 197 } 198 199 /* 200 * Delete the backup file if it got created 201 */ 202 if (exists == TRUE) 203 filestatus = DeleteFile(buf); 204 return (0); 205} 206 207isc_result_t 208isc_file_getmodtime(const char *file, isc_time_t *time) { 209 int fh; 210 211 REQUIRE(file != NULL); 212 REQUIRE(time != NULL); 213 214 if ((fh = open(file, _O_RDONLY | _O_BINARY)) < 0) 215 return (isc__errno2result(errno)); 216 217 if (!GetFileTime((HANDLE) _get_osfhandle(fh), 218 NULL, 219 NULL, 220 &time->absolute)) 221 { 222 close(fh); 223 errno = EINVAL; 224 return (isc__errno2result(errno)); 225 } 226 close(fh); 227 return (ISC_R_SUCCESS); 228} 229 230isc_result_t 231isc_file_settime(const char *file, isc_time_t *time) { 232 int fh; 233 234 REQUIRE(file != NULL && time != NULL); 235 236 if ((fh = open(file, _O_RDWR | _O_BINARY)) < 0) 237 return (isc__errno2result(errno)); 238 239 /* 240 * Set the date via the filedate system call and return. Failing 241 * this call implies the new file times are not supported by the 242 * underlying file system. 243 */ 244 if (!SetFileTime((HANDLE) _get_osfhandle(fh), 245 NULL, 246 &time->absolute, 247 &time->absolute)) 248 { 249 close(fh); 250 errno = EINVAL; 251 return (isc__errno2result(errno)); 252 } 253 254 close(fh); 255 return (ISC_R_SUCCESS); 256 257} 258 259#undef TEMPLATE 260#define TEMPLATE "XXXXXXXXXX.tmp" /* 14 characters. */ 261 262isc_result_t 263isc_file_mktemplate(const char *path, char *buf, size_t buflen) { 264 return (isc_file_template(path, TEMPLATE, buf, buflen)); 265} 266 267isc_result_t 268isc_file_template(const char *path, const char *templet, char *buf, 269 size_t buflen) { 270 char *s; 271 272 REQUIRE(path != NULL); 273 REQUIRE(templet != NULL); 274 REQUIRE(buf != NULL); 275 276 s = strrchr(templet, '\\'); 277 if (s != NULL) 278 templet = s + 1; 279 280 s = strrchr(path, '\\'); 281 282 if (s != NULL) { 283 if ((s - path + 1 + strlen(templet) + 1) > buflen) 284 return (ISC_R_NOSPACE); 285 286 strncpy(buf, path, s - path + 1); 287 buf[s - path + 1] = '\0'; 288 strcat(buf, templet); 289 } else { 290 if ((strlen(templet) + 1) > buflen) 291 return (ISC_R_NOSPACE); 292 293 strcpy(buf, templet); 294 } 295 296 return (ISC_R_SUCCESS); 297} 298 299isc_result_t 300isc_file_renameunique(const char *file, char *templet) { 301 int fd = -1; 302 int res = 0; 303 isc_result_t result = ISC_R_SUCCESS; 304 305 REQUIRE(file != NULL); 306 REQUIRE(templet != NULL); 307 308 fd = mkstemp(templet, ISC_TRUE); 309 if (fd == -1) 310 result = isc__errno2result(errno); 311 else 312 close(fd); 313 314 if (result == ISC_R_SUCCESS) { 315 res = isc_file_safemovefile(file, templet); 316 if (res != 0) { 317 result = isc__errno2result(errno); 318 (void)unlink(templet); 319 } 320 } 321 return (result); 322} 323 324static isc_result_t 325openuniquemode(char *templet, int mode, isc_boolean_t binary, FILE **fp) { 326 int fd; 327 FILE *f; 328 isc_result_t result = ISC_R_SUCCESS; 329 330 REQUIRE(templet != NULL); 331 REQUIRE(fp != NULL && *fp == NULL); 332 333 /* 334 * Win32 does not have mkstemp. Using emulation above. 335 */ 336 fd = mkstemp(templet, binary); 337 338 if (fd == -1) 339 result = isc__errno2result(errno); 340 if (result == ISC_R_SUCCESS) { 341#if 1 342 UNUSED(mode); 343#else 344 (void)fchmod(fd, mode); 345#endif 346 f = fdopen(fd, binary ? "wb+" : "w+"); 347 if (f == NULL) { 348 result = isc__errno2result(errno); 349 (void)remove(templet); 350 (void)close(fd); 351 } else 352 *fp = f; 353 } 354 355 return (result); 356} 357 358isc_result_t 359isc_file_openuniqueprivate(char *templet, FILE **fp) { 360 int mode = _S_IREAD | _S_IWRITE; 361 return (openuniquemode(templet, mode, ISC_FALSE, fp)); 362} 363 364isc_result_t 365isc_file_openunique(char *templet, FILE **fp) { 366 int mode = _S_IREAD | _S_IWRITE; 367 return (openuniquemode(templet, mode, ISC_FALSE, fp)); 368} 369 370isc_result_t 371isc_file_openuniquemode(char *templet, int mode, FILE **fp) { 372 return (openuniquemode(templet, mode, ISC_FALSE, fp)); 373} 374 375isc_result_t 376isc_file_bopenuniqueprivate(char *templet, FILE **fp) { 377 int mode = _S_IREAD | _S_IWRITE; 378 return (openuniquemode(templet, mode, ISC_TRUE, fp)); 379} 380 381isc_result_t 382isc_file_bopenunique(char *templet, FILE **fp) { 383 int mode = _S_IREAD | _S_IWRITE; 384 return (openuniquemode(templet, mode, ISC_TRUE, fp)); 385} 386 387isc_result_t 388isc_file_bopenuniquemode(char *templet, int mode, FILE **fp) { 389 return (openuniquemode(templet, mode, ISC_TRUE, fp)); 390} 391 392isc_result_t 393isc_file_remove(const char *filename) { 394 int r; 395 396 REQUIRE(filename != NULL); 397 398 r = unlink(filename); 399 if (r == 0) 400 return (ISC_R_SUCCESS); 401 else 402 return (isc__errno2result(errno)); 403} 404 405isc_result_t 406isc_file_rename(const char *oldname, const char *newname) { 407 int r; 408 409 REQUIRE(oldname != NULL); 410 REQUIRE(newname != NULL); 411 412 r = isc_file_safemovefile(oldname, newname); 413 if (r == 0) 414 return (ISC_R_SUCCESS); 415 else 416 return (isc__errno2result(errno)); 417} 418 419isc_boolean_t 420isc_file_exists(const char *pathname) { 421 struct stat stats; 422 423 REQUIRE(pathname != NULL); 424 425 return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS)); 426} 427 428isc_result_t 429isc_file_isplainfile(const char *filename) { 430 /* 431 * This function returns success if filename is a plain file. 432 */ 433 struct stat filestat; 434 memset(&filestat,0,sizeof(struct stat)); 435 436 if ((stat(filename, &filestat)) == -1) 437 return(isc__errno2result(errno)); 438 439 if(! S_ISREG(filestat.st_mode)) 440 return(ISC_R_INVALIDFILE); 441 442 return(ISC_R_SUCCESS); 443} 444 445isc_boolean_t 446isc_file_isabsolute(const char *filename) { 447 REQUIRE(filename != NULL); 448 /* 449 * Look for c:\path\... style, c:/path/... or \\computer\shar\path... 450 * the UNC style file specs 451 */ 452 if ((filename[0] == '\\') && (filename[1] == '\\')) 453 return (ISC_TRUE); 454 if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '\\') 455 return (ISC_TRUE); 456 if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '/') 457 return (ISC_TRUE); 458 return (ISC_FALSE); 459} 460 461isc_boolean_t 462isc_file_iscurrentdir(const char *filename) { 463 REQUIRE(filename != NULL); 464 return (ISC_TF(filename[0] == '.' && filename[1] == '\0')); 465} 466 467isc_boolean_t 468isc_file_ischdiridempotent(const char *filename) { 469 REQUIRE(filename != NULL); 470 471 if (isc_file_isabsolute(filename)) 472 return (ISC_TRUE); 473 if (filename[0] == '\\') 474 return (ISC_TRUE); 475 if (filename[0] == '/') 476 return (ISC_TRUE); 477 if (isc_file_iscurrentdir(filename)) 478 return (ISC_TRUE); 479 return (ISC_FALSE); 480} 481 482const char * 483isc_file_basename(const char *filename) { 484 char *s; 485 486 REQUIRE(filename != NULL); 487 488 s = strrchr(filename, '\\'); 489 if (s == NULL) 490 return (filename); 491 return (s + 1); 492} 493 494isc_result_t 495isc_file_progname(const char *filename, char *progname, size_t namelen) { 496 const char *s; 497 char *p; 498 size_t len; 499 500 REQUIRE(filename != NULL); 501 REQUIRE(progname != NULL); 502 503 /* 504 * Strip the path from the name 505 */ 506 s = isc_file_basename(filename); 507 if (s == NULL) { 508 return (ISC_R_NOSPACE); 509 } 510 511 /* 512 * Strip any and all suffixes 513 */ 514 p = strchr(s, '.'); 515 if (p == NULL) { 516 if (namelen <= strlen(s)) 517 return (ISC_R_NOSPACE); 518 519 strcpy(progname, s); 520 return (ISC_R_SUCCESS); 521 } 522 523 /* 524 * Copy the result to the buffer 525 */ 526 len = p - s; 527 if (len >= namelen) 528 return (ISC_R_NOSPACE); 529 530 strncpy(progname, s, len); 531 progname[len] = '\0'; 532 return (ISC_R_SUCCESS); 533} 534 535isc_result_t 536isc_file_absolutepath(const char *filename, char *path, size_t pathlen) { 537 char *ptrname; 538 DWORD retval; 539 540 REQUIRE(filename != NULL); 541 REQUIRE(path != NULL); 542 543 retval = GetFullPathName(filename, pathlen, path, &ptrname); 544 545 /* Something went wrong in getting the path */ 546 if (retval == 0) 547 return (ISC_R_NOTFOUND); 548 /* Caller needs to provide a larger buffer to contain the string */ 549 if (retval >= pathlen) 550 return (ISC_R_NOSPACE); 551 return (ISC_R_SUCCESS); 552} 553 554isc_result_t 555isc_file_truncate(const char *filename, isc_offset_t size) { 556 int fh; 557 558 REQUIRE(filename != NULL && size >= 0); 559 560 if ((fh = open(filename, _O_RDWR | _O_BINARY)) < 0) 561 return (isc__errno2result(errno)); 562 563 if(_chsize(fh, size) != 0) { 564 close(fh); 565 return (isc__errno2result(errno)); 566 } 567 close(fh); 568 569 return (ISC_R_SUCCESS); 570} 571 572isc_result_t 573isc_file_safecreate(const char *filename, FILE **fp) { 574 isc_result_t result; 575 int flags; 576 struct stat sb; 577 FILE *f; 578 int fd; 579 580 REQUIRE(filename != NULL); 581 REQUIRE(fp != NULL && *fp == NULL); 582 583 result = file_stats(filename, &sb); 584 if (result == ISC_R_SUCCESS) { 585 if ((sb.st_mode & S_IFREG) == 0) 586 return (ISC_R_INVALIDFILE); 587 flags = O_WRONLY | O_TRUNC; 588 } else if (result == ISC_R_FILENOTFOUND) { 589 flags = O_WRONLY | O_CREAT | O_EXCL; 590 } else 591 return (result); 592 593 fd = open(filename, flags, S_IRUSR | S_IWUSR); 594 if (fd == -1) 595 return (isc__errno2result(errno)); 596 597 f = fdopen(fd, "w"); 598 if (f == NULL) { 599 result = isc__errno2result(errno); 600 close(fd); 601 return (result); 602 } 603 604 *fp = f; 605 return (ISC_R_SUCCESS); 606} 607 608isc_result_t 609isc_file_splitpath(isc_mem_t *mctx, char *path, char **dirname, char **basename) 610{ 611 char *dir, *file, *slash; 612 char *backslash; 613 614 slash = strrchr(path, '/'); 615 616 backslash = strrchr(path, '\\'); 617 if ((slash != NULL && backslash != NULL && backslash > slash) || 618 (slash == NULL && backslash != NULL)) 619 slash = backslash; 620 621 if (slash == path) { 622 file = ++slash; 623 dir = isc_mem_strdup(mctx, "/"); 624 } else if (slash != NULL) { 625 file = ++slash; 626 dir = isc_mem_allocate(mctx, slash - path); 627 if (dir != NULL) 628 strlcpy(dir, path, slash - path); 629 } else { 630 file = path; 631 dir = isc_mem_strdup(mctx, "."); 632 } 633 634 if (dir == NULL) 635 return (ISC_R_NOMEMORY); 636 637 if (*file == '\0') { 638 isc_mem_free(mctx, dir); 639 return (ISC_R_INVALIDFILE); 640 } 641 642 *dirname = dir; 643 *basename = file; 644 645 return (ISC_R_SUCCESS); 646} 647 648isc_result_t 649isc_file_mode(const char *file, mode_t *modep) { 650 isc_result_t result; 651 struct stat stats; 652 653 REQUIRE(modep != NULL); 654 655 result = file_stats(file, &stats); 656 if (result == ISC_R_SUCCESS) 657 *modep = (stats.st_mode & 07777); 658 return (result); 659} 660