1 /* Copyright (C) 2007-2009 The Android Open Source Project 2 ** 3 ** This software is licensed under the terms of the GNU General Public 4 ** License version 2, as published by the Free Software Foundation, and 5 ** may be copied, distributed, and modified under those terms. 6 ** 7 ** This program is distributed in the hope that it will be useful, 8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of 9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 ** GNU General Public License for more details. 11 */ 12 #include "android/utils/path.h" 13 14 #include <stdio.h> 15 #include <string.h> 16 #include <stdlib.h> 17 #include <errno.h> 18 #include <fcntl.h> 19 20 #ifdef _WIN32 21 #include <process.h> 22 #include <shlobj.h> 23 #include <tlhelp32.h> 24 #include <io.h> 25 #include <sys/types.h> 26 #include <sys/stat.h> 27 #include <stdint.h> 28 #include <limits.h> 29 #include <winbase.h> 30 #else 31 #include <unistd.h> 32 #include <sys/stat.h> 33 #include <time.h> 34 #include <signal.h> 35 #endif 36 37 #include "android/utils/debug.h" 38 #define D(...) VERBOSE_PRINT(init,__VA_ARGS__) 39 40 #ifndef CHECKED 41 # ifdef _WIN32 42 # define CHECKED(ret, call) (ret) = (call) 43 # else 44 # define CHECKED(ret, call) do { (ret) = (call); } while ((ret) < 0 && errno == EINTR) 45 # endif 46 #endif 47 48 /** PATH HANDLING ROUTINES 49 ** 50 ** path_parent() can be used to return the n-level parent of a given directory 51 ** this understands . and .. when encountered in the input path 52 **/ 53 54 static __inline__ int 55 ispathsep(int c) 56 { 57 #ifdef _WIN32 58 return (c == '/' || c == '\\'); 59 #else 60 return (c == '/'); 61 #endif 62 } 63 64 char* 65 path_parent( const char* path, int levels ) 66 { 67 const char* end = path + strlen(path); 68 char* result; 69 70 while (levels > 0) { 71 const char* base; 72 73 /* trim any trailing path separator */ 74 while (end > path && ispathsep(end[-1])) 75 end--; 76 77 base = end; 78 while (base > path && !ispathsep(base[-1])) 79 base--; 80 81 if (base <= path) { 82 if (end == base+1 && base[0] == '.' && levels == 1) 83 return strdup(".."); 84 /* we can't go that far */ 85 return NULL; 86 } 87 88 if (end == base+1 && base[0] == '.') 89 goto Next; 90 91 if (end == base+2 && base[0] == '.' && base[1] == '.') { 92 levels += 1; 93 goto Next; 94 } 95 96 levels -= 1; 97 98 Next: 99 end = base - 1; 100 } 101 result = malloc( end-path+1 ); 102 if (result != NULL) { 103 memcpy( result, path, end-path ); 104 result[end-path] = 0; 105 } 106 return result; 107 } 108 109 static char* 110 substring_dup( const char* start, const char* end ) 111 { 112 int len = end - start; 113 char* result = android_alloc(len+1); 114 memcpy(result, start, len); 115 result[len] = 0; 116 return result; 117 } 118 119 int 120 path_split( const char* path, char* *pdirname, char* *pbasename ) 121 { 122 const char* end = path + strlen(path); 123 const char* last; 124 char* basename; 125 126 /* prepare for errors */ 127 if (pdirname) 128 *pdirname = NULL; 129 if (pbasename) 130 *pbasename = NULL; 131 132 /* handle empty path case */ 133 if (end == path) { 134 return -1; 135 } 136 137 /* strip trailing path separators */ 138 while (end > path && ispathsep(end[-1])) 139 end -= 1; 140 141 /* handle "/" and degenerate cases like "////" */ 142 if (end == path) { 143 return -1; 144 } 145 146 /* find last separator */ 147 last = end; 148 while (last > path && !ispathsep(last[-1])) 149 last -= 1; 150 151 /* handle cases where there is no path separator */ 152 if (last == path) { 153 if (pdirname) 154 *pdirname = ASTRDUP("."); 155 if (pbasename) 156 *pbasename = substring_dup(path,end); 157 return 0; 158 } 159 160 /* handle "/foo" */ 161 if (last == path+1) { 162 if (pdirname) 163 *pdirname = ASTRDUP("/"); 164 if (pbasename) 165 *pbasename = substring_dup(path+1,end); 166 return 0; 167 } 168 169 /* compute basename */ 170 basename = substring_dup(last,end); 171 if (strcmp(basename, ".") == 0 || strcmp(basename, "..") == 0) { 172 AFREE(basename); 173 return -1; 174 } 175 176 if (pbasename) 177 *pbasename = basename; 178 else { 179 AFREE(basename); 180 } 181 182 /* compute dirname */ 183 if (pdirname != NULL) 184 *pdirname = substring_dup(path,last-1); 185 186 return 0; 187 } 188 189 char* 190 path_basename( const char* path ) 191 { 192 char* basename; 193 194 if (path_split(path, NULL, &basename) < 0) 195 return NULL; 196 197 return basename; 198 } 199 200 char* 201 path_dirname( const char* path ) 202 { 203 char* dirname; 204 205 if (path_split(path, &dirname, NULL) < 0) 206 return NULL; 207 208 return dirname; 209 } 210 211 212 213 214 215 /** MISC FILE AND DIRECTORY HANDLING 216 **/ 217 218 ABool 219 path_exists( const char* path ) 220 { 221 int ret; 222 if (path == NULL) 223 return 0; 224 CHECKED(ret, access(path, F_OK)); 225 return (ret == 0) || (errno != ENOENT); 226 } 227 228 /* checks that a path points to a regular file */ 229 ABool 230 path_is_regular( const char* path ) 231 { 232 int ret; 233 struct stat st; 234 235 if (path == NULL) 236 return 0; 237 CHECKED(ret, stat(path, &st)); 238 if (ret < 0) 239 return 0; 240 241 return S_ISREG(st.st_mode); 242 } 243 244 245 /* checks that a path points to a directory */ 246 ABool 247 path_is_dir( const char* path ) 248 { 249 int ret; 250 struct stat st; 251 252 if (path == NULL) 253 return 0; 254 CHECKED(ret, stat(path, &st)); 255 if (ret < 0) 256 return 0; 257 258 return S_ISDIR(st.st_mode); 259 } 260 261 /* checks that one can read/write a given (regular) file */ 262 ABool 263 path_can_read( const char* path ) 264 { 265 int ret; 266 if (path == NULL) 267 return 0; 268 CHECKED(ret, access(path, R_OK)); 269 return (ret == 0); 270 } 271 272 ABool 273 path_can_write( const char* path ) 274 { 275 int ret; 276 if (path == NULL) 277 return 0; 278 CHECKED(ret, access(path, R_OK)); 279 return (ret == 0); 280 } 281 282 ABool 283 path_can_exec( const char* path ) 284 { 285 int ret; 286 if (path == NULL) 287 return 0; 288 CHECKED(ret, access(path, X_OK)); 289 return (ret == 0); 290 } 291 292 /* try to make a directory. returns 0 on success, -1 on failure 293 * (error code in errno) */ 294 APosixStatus 295 path_mkdir( const char* path, int mode ) 296 { 297 #ifdef _WIN32 298 (void)mode; 299 return _mkdir(path); 300 #else 301 int ret; 302 CHECKED(ret, mkdir(path, mode)); 303 return ret; 304 #endif 305 } 306 307 static APosixStatus 308 path_mkdir_recursive( char* path, unsigned len, int mode ) 309 { 310 char old_c; 311 int ret; 312 unsigned len2; 313 314 /* get rid of trailing separators */ 315 while (len > 0 && ispathsep(path[len-1])) 316 len -= 1; 317 318 if (len == 0) { 319 errno = ENOENT; 320 return -1; 321 } 322 323 /* check that the parent exists, 'len2' is the length of 324 * the parent part of the path */ 325 len2 = len-1; 326 while (len2 > 0 && !ispathsep(path[len2-1])) 327 len2 -= 1; 328 329 if (len2 > 0) { 330 old_c = path[len2]; 331 path[len2] = 0; 332 ret = 0; 333 if ( !path_exists(path) ) { 334 /* the parent doesn't exist, so try to create it */ 335 ret = path_mkdir_recursive( path, len2, mode ); 336 } 337 path[len2] = old_c; 338 339 if (ret < 0) 340 return ret; 341 } 342 343 /* at this point, we now the parent exists */ 344 old_c = path[len]; 345 path[len] = 0; 346 ret = path_mkdir( path, mode ); 347 path[len] = old_c; 348 349 return ret; 350 } 351 352 /* ensure that a given directory exists, create it if not, 353 0 on success, -1 on failure (error code in errno) */ 354 APosixStatus 355 path_mkdir_if_needed( const char* path, int mode ) 356 { 357 int ret = 0; 358 359 if (!path_exists(path)) { 360 ret = path_mkdir(path, mode); 361 362 if (ret < 0 && errno == ENOENT) { 363 char temp[MAX_PATH]; 364 unsigned len = (unsigned)strlen(path); 365 366 if (len > sizeof(temp)-1) { 367 errno = EINVAL; 368 return -1; 369 } 370 memcpy( temp, path, len ); 371 temp[len] = 0; 372 373 return path_mkdir_recursive(temp, len, mode); 374 } 375 } 376 return ret; 377 } 378 379 /* return the size of a given file in '*psize'. returns 0 on 380 * success, -1 on failure (error code in errno) */ 381 APosixStatus 382 path_get_size( const char* path, uint64_t *psize ) 383 { 384 #ifdef _WIN32 385 /* avoid _stat64 which is only defined in MSVCRT.DLL, not CRTDLL.DLL */ 386 /* do not use OpenFile() because it has strange search behaviour that could */ 387 /* result in getting the size of a different file */ 388 LARGE_INTEGER size; 389 HANDLE file = CreateFile( /* lpFilename */ path, 390 /* dwDesiredAccess */ GENERIC_READ, 391 /* dwSharedMode */ FILE_SHARE_READ|FILE_SHARE_WRITE, 392 /* lpSecurityAttributes */ NULL, 393 /* dwCreationDisposition */ OPEN_EXISTING, 394 /* dwFlagsAndAttributes */ 0, 395 /* hTemplateFile */ NULL ); 396 if (file == INVALID_HANDLE_VALUE) { 397 /* ok, just to play fair */ 398 errno = ENOENT; 399 return -1; 400 } 401 if (!GetFileSizeEx(file, &size)) { 402 /* maybe we tried to get the size of a pipe or something like that ? */ 403 *psize = 0; 404 } 405 else { 406 *psize = (uint64_t) size.QuadPart; 407 } 408 CloseHandle(file); 409 return 0; 410 #else 411 int ret; 412 struct stat st; 413 414 CHECKED(ret, stat(path, &st)); 415 if (ret == 0) { 416 *psize = (uint64_t) st.st_size; 417 } 418 return ret; 419 #endif 420 } 421 422 423 ABool 424 path_is_absolute( const char* path ) 425 { 426 #ifdef _WIN32 427 if (path == NULL) 428 return 0; 429 430 if (path[0] == '/' || path[0] == '\\') 431 return 1; 432 433 /* 'C:' is always considered to be absolute 434 * even if used with a relative path like C:foo which 435 * is different from C:\foo 436 */ 437 if (path[0] != 0 && path[1] == ':') 438 return 1; 439 440 return 0; 441 #else 442 return (path != NULL && path[0] == '/'); 443 #endif 444 } 445 446 char* 447 path_get_absolute( const char* path ) 448 { 449 if (path_is_absolute(path)) { 450 return ASTRDUP(path); 451 } 452 453 #ifdef _WIN32 454 { 455 char* result; 456 int pathLen = strlen(path); 457 int currentLen = GetCurrentDirectory(0, NULL); 458 459 if (currentLen <= 0) { 460 /* Could not get size of working directory. something is 461 * really fishy here, return a simple copy */ 462 return ASTRDUP(path); 463 } 464 result = malloc(currentLen + pathLen + 2); 465 466 GetCurrentDirectory(currentLen+1, result); 467 if (currentLen == 0 || result[currentLen-1] != '\\') { 468 result[currentLen++] = '\\'; 469 } 470 memcpy(result + currentLen, path, pathLen+1); 471 472 return result; 473 } 474 #else 475 { 476 int pathLen = strlen(path); 477 char currentDir[PATH_MAX]; 478 int currentLen; 479 char* result; 480 481 if (getcwd(currentDir, sizeof(currentDir)) == NULL) { 482 /* Could not get the current working directory. something is really 483 * fishy here, so don't do anything and return a copy */ 484 return ASTRDUP(path); 485 } 486 487 /* Make a new path with <current-path>/<path> */ 488 currentLen = strlen(currentDir); 489 result = malloc(currentLen + pathLen + 2); 490 491 memcpy(result, currentDir, currentLen); 492 if (currentLen == 0 || result[currentLen-1] != '/') { 493 result[currentLen++] = '/'; 494 } 495 memcpy(result + currentLen, path, pathLen+1); 496 497 return result; 498 } 499 #endif 500 } 501 502 /** OTHER FILE UTILITIES 503 ** 504 ** path_empty_file() creates an empty file at a given path location. 505 ** if the file already exists, it is truncated without warning 506 ** 507 ** path_copy_file() copies one file into another. 508 ** 509 ** both functions return 0 on success, and -1 on error 510 **/ 511 512 APosixStatus 513 path_empty_file( const char* path ) 514 { 515 #ifdef _WIN32 516 int fd = _creat( path, S_IWRITE ); 517 #else 518 /* on Unix, only allow the owner to read/write, since the file * 519 * may contain some personal data we don't want to see exposed */ 520 int fd = creat(path, S_IRUSR | S_IWUSR); 521 #endif 522 if (fd >= 0) { 523 close(fd); 524 return 0; 525 } 526 return -1; 527 } 528 529 APosixStatus 530 path_copy_file( const char* dest, const char* source ) 531 { 532 int fd, fs, result = -1; 533 534 /* if the destination doesn't exist, create it */ 535 if ( access(source, F_OK) < 0 || 536 path_empty_file(dest) < 0) { 537 return -1; 538 } 539 540 if ( access(source, R_OK) < 0 ) { 541 D("%s: source file is un-readable: %s\n", 542 __FUNCTION__, source); 543 return -1; 544 } 545 546 #ifdef _WIN32 547 fd = _open(dest, _O_RDWR | _O_BINARY); 548 fs = _open(source, _O_RDONLY | _O_BINARY); 549 #else 550 fd = creat(dest, S_IRUSR | S_IWUSR); 551 fs = open(source, S_IREAD); 552 #endif 553 if (fs >= 0 && fd >= 0) { 554 char buf[4096]; 555 ssize_t total = 0; 556 ssize_t n; 557 result = 0; /* success */ 558 while ((n = read(fs, buf, 4096)) > 0) { 559 if (write(fd, buf, n) != n) { 560 /* write failed. Make it return -1 so that an 561 * empty file be created. */ 562 D("Failed to copy '%s' to '%s': %s (%d)", 563 source, dest, strerror(errno), errno); 564 result = -1; 565 break; 566 } 567 total += n; 568 } 569 } 570 571 if (fs >= 0) { 572 close(fs); 573 } 574 if (fd >= 0) { 575 close(fd); 576 } 577 return result; 578 } 579 580 581 APosixStatus 582 path_delete_file( const char* path ) 583 { 584 #ifdef _WIN32 585 int ret = _unlink( path ); 586 if (ret == -1 && errno == EACCES) { 587 /* a first call to _unlink will fail if the file is set read-only */ 588 /* we can however try to change its mode first and call unlink */ 589 /* again... */ 590 ret = _chmod( path, _S_IREAD | _S_IWRITE ); 591 if (ret == 0) 592 ret = _unlink( path ); 593 } 594 return ret; 595 #else 596 return unlink(path); 597 #endif 598 } 599 600 601 void* 602 path_load_file(const char *fn, size_t *pSize) 603 { 604 char* data; 605 int sz; 606 int fd; 607 608 if (pSize) 609 *pSize = 0; 610 611 data = NULL; 612 613 fd = open(fn, O_BINARY | O_RDONLY); 614 if(fd < 0) return NULL; 615 616 do { 617 sz = lseek(fd, 0, SEEK_END); 618 if(sz < 0) break; 619 620 if (pSize) 621 *pSize = (size_t) sz; 622 623 if (lseek(fd, 0, SEEK_SET) != 0) 624 break; 625 626 data = (char*) malloc(sz + 1); 627 if(data == NULL) break; 628 629 if (read(fd, data, sz) != sz) 630 break; 631 632 close(fd); 633 data[sz] = 0; 634 635 return data; 636 } while (0); 637 638 close(fd); 639 640 if(data != NULL) 641 free(data); 642 643 return NULL; 644 } 645 646 #ifdef _WIN32 647 # define DIR_SEP ';' 648 #else 649 # define DIR_SEP ':' 650 #endif 651 652 char* 653 path_search_exec( const char* filename ) 654 { 655 const char* sysPath = getenv("PATH"); 656 char temp[PATH_MAX]; 657 int count; 658 int slen; 659 const char* p; 660 661 /* If the file contains a directory separator, don't search */ 662 #ifdef _WIN32 663 if (strchr(filename, '/') != NULL || strchr(filename, '\\') != NULL) { 664 #else 665 if (strchr(filename, '/') != NULL) { 666 #endif 667 if (path_exists(filename)) { 668 return strdup(filename); 669 } else { 670 return NULL; 671 } 672 } 673 674 /* If system path is empty, don't search */ 675 if (sysPath == NULL || sysPath[0] == '\0') { 676 return NULL; 677 } 678 679 /* Count the number of non-empty items in the system path 680 * Items are separated by DIR_SEP, and two successive separators 681 * correspond to an empty item that will be ignored. 682 * Also compute the required string storage length. */ 683 count = 0; 684 slen = 0; 685 p = sysPath; 686 687 while (*p) { 688 char* p2 = strchr(p, DIR_SEP); 689 int len; 690 if (p2 == NULL) { 691 len = strlen(p); 692 } else { 693 len = p2 - p; 694 } 695 696 do { 697 if (len <= 0) 698 break; 699 700 snprintf(temp, sizeof(temp), "%.*s/%s", len, p, filename); 701 702 if (path_exists(temp) && path_can_exec(temp)) { 703 return strdup(temp); 704 } 705 706 } while (0); 707 708 p += len; 709 if (*p == DIR_SEP) 710 p++; 711 } 712 713 /* Nothing, really */ 714 return NULL; 715 } 716