1 /* $NetBSD: file.c,v 1.1.1.1 2009/12/13 16:54:40 kardel Exp $ */ 2 3 /* 4 * Copyright (C) 2004, 2007 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: file.c,v 1.31 2007/06/19 23:47:19 tbox Exp */ 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/result.h> 37 #include <isc/time.h> 38 #include <isc/util.h> 39 #include <isc/stat.h> 40 41 #include "errno2result.h" 42 43 /* 44 * Emulate UNIX mkstemp, which returns an open FD to the new file 45 * 46 */ 47 static int 48 gettemp(char *path, int *doopen) { 49 char *start, *trv; 50 struct stat sbuf; 51 int pid; 52 53 trv = strrchr(path, 'X'); 54 trv++; 55 pid = getpid(); 56 /* extra X's get set to 0's */ 57 while (*--trv == 'X') { 58 *trv = (pid % 10) + '0'; 59 pid /= 10; 60 } 61 /* 62 * check the target directory; if you have six X's and it 63 * doesn't exist this runs for a *very* long time. 64 */ 65 for (start = trv + 1;; --trv) { 66 if (trv <= path) 67 break; 68 if (*trv == '\\') { 69 *trv = '\0'; 70 if (stat(path, &sbuf)) 71 return (0); 72 if (!S_ISDIR(sbuf.st_mode)) { 73 errno = ENOTDIR; 74 return (0); 75 } 76 *trv = '\\'; 77 break; 78 } 79 } 80 81 for (;;) { 82 if (doopen) { 83 if ((*doopen = 84 open(path, O_CREAT|O_EXCL|O_RDWR, 85 _S_IREAD | _S_IWRITE)) >= 0) 86 return (1); 87 if (errno != EEXIST) 88 return (0); 89 } else if (stat(path, &sbuf)) 90 return (errno == ENOENT ? 1 : 0); 91 92 /* tricky little algorithm for backward compatibility */ 93 for (trv = start;;) { 94 if (!*trv) 95 return (0); 96 if (*trv == 'z') 97 *trv++ = 'a'; 98 else { 99 if (isdigit(*trv)) 100 *trv = 'a'; 101 else 102 ++*trv; 103 break; 104 } 105 } 106 } 107 /*NOTREACHED*/ 108 } 109 110 static int 111 mkstemp(char *path) { 112 int fd; 113 114 return (gettemp(path, &fd) ? fd : -1); 115 } 116 117 /* 118 * XXXDCL As the API for accessing file statistics undoubtedly gets expanded, 119 * it might be good to provide a mechanism that allows for the results 120 * of a previous stat() to be used again without having to do another stat, 121 * such as perl's mechanism of using "_" in place of a file name to indicate 122 * that the results of the last stat should be used. But then you get into 123 * annoying MP issues. BTW, Win32 has stat(). 124 */ 125 static isc_result_t 126 file_stats(const char *file, struct stat *stats) { 127 isc_result_t result = ISC_R_SUCCESS; 128 129 REQUIRE(file != NULL); 130 REQUIRE(stats != NULL); 131 132 if (stat(file, stats) != 0) 133 result = isc__errno2result(errno); 134 135 return (result); 136 } 137 138 /* 139 * isc_file_safemovefile is needed to be defined here to ensure that 140 * any file with the new name is renamed to a backup name and then the 141 * rename is done. If all goes well then the backup can be deleted, 142 * otherwise it gets renamed back. 143 */ 144 145 int 146 isc_file_safemovefile(const char *oldname, const char *newname) { 147 BOOL filestatus; 148 char buf[512]; 149 struct stat sbuf; 150 BOOL exists = FALSE; 151 int tmpfd; 152 153 /* 154 * Make sure we have something to do 155 */ 156 if (stat(oldname, &sbuf) != 0) { 157 errno = ENOENT; 158 return (-1); 159 } 160 161 /* 162 * Rename to a backup the new file if it still exists 163 */ 164 if (stat(newname, &sbuf) == 0) { 165 exists = TRUE; 166 strcpy(buf, newname); 167 strcat(buf, ".XXXXX"); 168 tmpfd = mkstemp(buf); 169 if (tmpfd > 0) 170 _close(tmpfd); 171 DeleteFile(buf); 172 _chmod(newname, _S_IREAD | _S_IWRITE); 173 174 filestatus = MoveFile(newname, buf); 175 } 176 /* Now rename the file to the new name 177 */ 178 _chmod(oldname, _S_IREAD | _S_IWRITE); 179 180 filestatus = MoveFile(oldname, newname); 181 if (filestatus == 0) { 182 /* 183 * Try to rename the backup back to the original name 184 * if the backup got created 185 */ 186 if (exists == TRUE) { 187 filestatus = MoveFile(buf, newname); 188 if (filestatus == 0) 189 errno = EACCES; 190 } 191 return (-1); 192 } 193 194 /* 195 * Delete the backup file if it got created 196 */ 197 if (exists == TRUE) 198 filestatus = DeleteFile(buf); 199 return (0); 200 } 201 202 isc_result_t 203 isc_file_getmodtime(const char *file, isc_time_t *time) { 204 int fh; 205 206 REQUIRE(file != NULL); 207 REQUIRE(time != NULL); 208 209 if ((fh = open(file, _O_RDONLY | _O_BINARY)) < 0) 210 return (isc__errno2result(errno)); 211 212 if (!GetFileTime((HANDLE) _get_osfhandle(fh), 213 NULL, 214 NULL, 215 &time->absolute)) 216 { 217 close(fh); 218 errno = EINVAL; 219 return (isc__errno2result(errno)); 220 } 221 close(fh); 222 return (ISC_R_SUCCESS); 223 } 224 225 isc_result_t 226 isc_file_settime(const char *file, isc_time_t *time) { 227 int fh; 228 229 REQUIRE(file != NULL && time != NULL); 230 231 if ((fh = open(file, _O_RDWR | _O_BINARY)) < 0) 232 return (isc__errno2result(errno)); 233 234 /* 235 * Set the date via the filedate system call and return. Failing 236 * this call implies the new file times are not supported by the 237 * underlying file system. 238 */ 239 if (!SetFileTime((HANDLE) _get_osfhandle(fh), 240 NULL, 241 &time->absolute, 242 &time->absolute)) 243 { 244 close(fh); 245 errno = EINVAL; 246 return (isc__errno2result(errno)); 247 } 248 249 close(fh); 250 return (ISC_R_SUCCESS); 251 252 } 253 254 #undef TEMPLATE 255 #define TEMPLATE "XXXXXXXXXX.tmp" /* 14 characters. */ 256 257 isc_result_t 258 isc_file_mktemplate(const char *path, char *buf, size_t buflen) { 259 return (isc_file_template(path, TEMPLATE, buf, buflen)); 260 } 261 262 isc_result_t 263 isc_file_template(const char *path, const char *templet, char *buf, 264 size_t buflen) { 265 char *s; 266 267 REQUIRE(path != NULL); 268 REQUIRE(templet != NULL); 269 REQUIRE(buf != NULL); 270 271 s = strrchr(templet, '\\'); 272 if (s != NULL) 273 templet = s + 1; 274 275 s = strrchr(path, '\\'); 276 277 if (s != NULL) { 278 if ((s - path + 1 + strlen(templet) + 1) > buflen) 279 return (ISC_R_NOSPACE); 280 281 strncpy(buf, path, s - path + 1); 282 buf[s - path + 1] = '\0'; 283 strcat(buf, templet); 284 } else { 285 if ((strlen(templet) + 1) > buflen) 286 return (ISC_R_NOSPACE); 287 288 strcpy(buf, templet); 289 } 290 291 return (ISC_R_SUCCESS); 292 } 293 294 isc_result_t 295 isc_file_renameunique(const char *file, char *templet) { 296 int fd = -1; 297 int res = 0; 298 isc_result_t result = ISC_R_SUCCESS; 299 300 REQUIRE(file != NULL); 301 REQUIRE(templet != NULL); 302 303 fd = mkstemp(templet); 304 if (fd == -1) 305 result = isc__errno2result(errno); 306 else 307 close(fd); 308 309 if (result == ISC_R_SUCCESS) { 310 res = isc_file_safemovefile(file, templet); 311 if (res != 0) { 312 result = isc__errno2result(errno); 313 (void)unlink(templet); 314 } 315 } 316 return (result); 317 } 318 319 isc_result_t 320 isc_file_openunique(char *templet, FILE **fp) { 321 int fd; 322 FILE *f; 323 isc_result_t result = ISC_R_SUCCESS; 324 325 REQUIRE(templet != NULL); 326 REQUIRE(fp != NULL && *fp == NULL); 327 328 /* 329 * Win32 does not have mkstemp. Using emulation above. 330 */ 331 fd = mkstemp(templet); 332 333 if (fd == -1) 334 result = isc__errno2result(errno); 335 if (result == ISC_R_SUCCESS) { 336 f = fdopen(fd, "w+"); 337 if (f == NULL) { 338 result = isc__errno2result(errno); 339 (void)remove(templet); 340 (void)close(fd); 341 } else 342 *fp = f; 343 } 344 345 return (result); 346 } 347 348 isc_result_t 349 isc_file_remove(const char *filename) { 350 int r; 351 352 REQUIRE(filename != NULL); 353 354 r = unlink(filename); 355 if (r == 0) 356 return (ISC_R_SUCCESS); 357 else 358 return (isc__errno2result(errno)); 359 } 360 361 isc_result_t 362 isc_file_rename(const char *oldname, const char *newname) { 363 int r; 364 365 REQUIRE(oldname != NULL); 366 REQUIRE(newname != NULL); 367 368 r = isc_file_safemovefile(oldname, newname); 369 if (r == 0) 370 return (ISC_R_SUCCESS); 371 else 372 return (isc__errno2result(errno)); 373 } 374 375 isc_boolean_t 376 isc_file_exists(const char *pathname) { 377 struct stat stats; 378 379 REQUIRE(pathname != NULL); 380 381 return (ISC_TF(file_stats(pathname, &stats) == ISC_R_SUCCESS)); 382 } 383 384 isc_boolean_t 385 isc_file_isabsolute(const char *filename) { 386 REQUIRE(filename != NULL); 387 /* 388 * Look for c:\path\... style, c:/path/... or \\computer\shar\path... 389 * the UNC style file specs 390 */ 391 if ((filename[0] == '\\') && (filename[1] == '\\')) 392 return (ISC_TRUE); 393 if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '\\') 394 return (ISC_TRUE); 395 if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '/') 396 return (ISC_TRUE); 397 return (ISC_FALSE); 398 } 399 400 isc_boolean_t 401 isc_file_iscurrentdir(const char *filename) { 402 REQUIRE(filename != NULL); 403 return (ISC_TF(filename[0] == '.' && filename[1] == '\0')); 404 } 405 406 isc_boolean_t 407 isc_file_ischdiridempotent(const char *filename) { 408 REQUIRE(filename != NULL); 409 410 if (isc_file_isabsolute(filename)) 411 return (ISC_TRUE); 412 if (filename[0] == '\\') 413 return (ISC_TRUE); 414 if (filename[0] == '/') 415 return (ISC_TRUE); 416 if (isc_file_iscurrentdir(filename)) 417 return (ISC_TRUE); 418 return (ISC_FALSE); 419 } 420 421 const char * 422 isc_file_basename(const char *filename) { 423 char *s; 424 425 REQUIRE(filename != NULL); 426 427 s = strrchr(filename, '\\'); 428 if (s == NULL) 429 return (filename); 430 return (s + 1); 431 } 432 433 isc_result_t 434 isc_file_progname(const char *filename, char *progname, size_t namelen) { 435 const char *s; 436 char *p; 437 size_t len; 438 439 REQUIRE(filename != NULL); 440 REQUIRE(progname != NULL); 441 442 /* 443 * Strip the path from the name 444 */ 445 s = isc_file_basename(filename); 446 if (s == NULL) { 447 return (ISC_R_NOSPACE); 448 } 449 450 /* 451 * Strip any and all suffixes 452 */ 453 p = strchr(s, '.'); 454 if (p == NULL) { 455 if (namelen <= strlen(s)) 456 return (ISC_R_NOSPACE); 457 458 strcpy(progname, s); 459 return (ISC_R_SUCCESS); 460 } 461 462 /* 463 * Copy the result to the buffer 464 */ 465 len = p - s; 466 if (len >= namelen) 467 return (ISC_R_NOSPACE); 468 469 strncpy(progname, s, len); 470 progname[len] = '\0'; 471 return (ISC_R_SUCCESS); 472 } 473 474 isc_result_t 475 isc_file_absolutepath(const char *filename, char *path, size_t pathlen) { 476 char *ptrname; 477 DWORD retval; 478 479 REQUIRE(filename != NULL); 480 REQUIRE(path != NULL); 481 482 retval = GetFullPathName(filename, pathlen, path, &ptrname); 483 484 /* Something went wrong in getting the path */ 485 if (retval == 0) 486 return (ISC_R_NOTFOUND); 487 /* Caller needs to provide a larger buffer to contain the string */ 488 if (retval >= pathlen) 489 return (ISC_R_NOSPACE); 490 return (ISC_R_SUCCESS); 491 } 492 493 isc_result_t 494 isc_file_truncate(const char *filename, isc_offset_t size) { 495 int fh; 496 497 REQUIRE(filename != NULL && size >= 0); 498 499 if ((fh = open(filename, _O_RDWR | _O_BINARY)) < 0) 500 return (isc__errno2result(errno)); 501 502 if(_chsize(fh, size) != 0) { 503 close(fh); 504 return (isc__errno2result(errno)); 505 } 506 close(fh); 507 508 return (ISC_R_SUCCESS); 509 } 510