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 54static __inline__ int 55ispathsep(int c) 56{ 57#ifdef _WIN32 58 return (c == '/' || c == '\\'); 59#else 60 return (c == '/'); 61#endif 62} 63 64char* 65path_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 109static char* 110substring_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 119int 120path_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 189char* 190path_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 200char* 201path_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 218ABool 219path_exists( const char* path ) 220{ 221 int ret; 222 CHECKED(ret, access(path, F_OK)); 223 return (ret == 0) || (errno != ENOENT); 224} 225 226/* checks that a path points to a regular file */ 227ABool 228path_is_regular( const char* path ) 229{ 230 int ret; 231 struct stat st; 232 233 CHECKED(ret, stat(path, &st)); 234 if (ret < 0) 235 return 0; 236 237 return S_ISREG(st.st_mode); 238} 239 240 241/* checks that a path points to a directory */ 242ABool 243path_is_dir( const char* path ) 244{ 245 int ret; 246 struct stat st; 247 248 CHECKED(ret, stat(path, &st)); 249 if (ret < 0) 250 return 0; 251 252 return S_ISDIR(st.st_mode); 253} 254 255/* checks that one can read/write a given (regular) file */ 256ABool 257path_can_read( const char* path ) 258{ 259 int ret; 260 CHECKED(ret, access(path, R_OK)); 261 return (ret == 0); 262} 263 264ABool 265path_can_write( const char* path ) 266{ 267 int ret; 268 CHECKED(ret, access(path, R_OK)); 269 return (ret == 0); 270} 271 272ABool 273path_can_exec( const char* path ) 274{ 275 int ret; 276 CHECKED(ret, access(path, X_OK)); 277 return (ret == 0); 278} 279 280/* try to make a directory. returns 0 on success, -1 on failure 281 * (error code in errno) */ 282APosixStatus 283path_mkdir( const char* path, int mode ) 284{ 285#ifdef _WIN32 286 (void)mode; 287 return _mkdir(path); 288#else 289 int ret; 290 CHECKED(ret, mkdir(path, mode)); 291 return ret; 292#endif 293} 294 295static APosixStatus 296path_mkdir_recursive( char* path, unsigned len, int mode ) 297{ 298 char old_c; 299 int ret; 300 unsigned len2; 301 302 /* get rid of trailing separators */ 303 while (len > 0 && ispathsep(path[len-1])) 304 len -= 1; 305 306 if (len == 0) { 307 errno = ENOENT; 308 return -1; 309 } 310 311 /* check that the parent exists, 'len2' is the length of 312 * the parent part of the path */ 313 len2 = len-1; 314 while (len2 > 0 && !ispathsep(path[len2-1])) 315 len2 -= 1; 316 317 if (len2 > 0) { 318 old_c = path[len2]; 319 path[len2] = 0; 320 ret = 0; 321 if ( !path_exists(path) ) { 322 /* the parent doesn't exist, so try to create it */ 323 ret = path_mkdir_recursive( path, len2, mode ); 324 } 325 path[len2] = old_c; 326 327 if (ret < 0) 328 return ret; 329 } 330 331 /* at this point, we now the parent exists */ 332 old_c = path[len]; 333 path[len] = 0; 334 ret = path_mkdir( path, mode ); 335 path[len] = old_c; 336 337 return ret; 338} 339 340/* ensure that a given directory exists, create it if not, 341 0 on success, -1 on failure (error code in errno) */ 342APosixStatus 343path_mkdir_if_needed( const char* path, int mode ) 344{ 345 int ret = 0; 346 347 if (!path_exists(path)) { 348 ret = path_mkdir(path, mode); 349 350 if (ret < 0 && errno == ENOENT) { 351 char temp[MAX_PATH]; 352 unsigned len = (unsigned)strlen(path); 353 354 if (len > sizeof(temp)-1) { 355 errno = EINVAL; 356 return -1; 357 } 358 memcpy( temp, path, len ); 359 temp[len] = 0; 360 361 return path_mkdir_recursive(temp, len, mode); 362 } 363 } 364 return ret; 365} 366 367/* return the size of a given file in '*psize'. returns 0 on 368 * success, -1 on failure (error code in errno) */ 369APosixStatus 370path_get_size( const char* path, uint64_t *psize ) 371{ 372#ifdef _WIN32 373 /* avoid _stat64 which is only defined in MSVCRT.DLL, not CRTDLL.DLL */ 374 /* do not use OpenFile() because it has strange search behaviour that could */ 375 /* result in getting the size of a different file */ 376 LARGE_INTEGER size; 377 HANDLE file = CreateFile( /* lpFilename */ path, 378 /* dwDesiredAccess */ GENERIC_READ, 379 /* dwSharedMode */ FILE_SHARE_READ|FILE_SHARE_WRITE, 380 /* lpSecurityAttributes */ NULL, 381 /* dwCreationDisposition */ OPEN_EXISTING, 382 /* dwFlagsAndAttributes */ 0, 383 /* hTemplateFile */ NULL ); 384 if (file == INVALID_HANDLE_VALUE) { 385 /* ok, just to play fair */ 386 errno = ENOENT; 387 return -1; 388 } 389 if (!GetFileSizeEx(file, &size)) { 390 /* maybe we tried to get the size of a pipe or something like that ? */ 391 *psize = 0; 392 } 393 else { 394 *psize = (uint64_t) size.QuadPart; 395 } 396 CloseHandle(file); 397 return 0; 398#else 399 int ret; 400 struct stat st; 401 402 CHECKED(ret, stat(path, &st)); 403 if (ret == 0) { 404 *psize = (uint64_t) st.st_size; 405 } 406 return ret; 407#endif 408} 409 410 411ABool 412path_is_absolute( const char* path ) 413{ 414#ifdef _WIN32 415 if (path == NULL) 416 return 0; 417 418 if (path[0] == '/' || path[0] == '\\') 419 return 1; 420 421 /* 'C:' is always considered to be absolute 422 * even if used with a relative path like C:foo which 423 * is different from C:\foo 424 */ 425 if (path[0] != 0 && path[1] == ':') 426 return 1; 427 428 return 0; 429#else 430 return (path != NULL && path[0] == '/'); 431#endif 432} 433 434char* 435path_get_absolute( const char* path ) 436{ 437 if (path_is_absolute(path)) { 438 return ASTRDUP(path); 439 } 440 441#ifdef _WIN32 442 { 443 char* result; 444 int pathLen = strlen(path); 445 int currentLen = GetCurrentDirectory(0, NULL); 446 447 if (currentLen <= 0) { 448 /* Could not get size of working directory. something is 449 * really fishy here, return a simple copy */ 450 return ASTRDUP(path); 451 } 452 result = malloc(currentLen + pathLen + 2); 453 454 GetCurrentDirectory(currentLen+1, result); 455 if (currentLen == 0 || result[currentLen-1] != '\\') { 456 result[currentLen++] = '\\'; 457 } 458 memcpy(result + currentLen, path, pathLen+1); 459 460 return result; 461 } 462#else 463 { 464 int pathLen = strlen(path); 465 char currentDir[PATH_MAX]; 466 int currentLen; 467 char* result; 468 469 if (getcwd(currentDir, sizeof(currentDir)) == NULL) { 470 /* Could not get the current working directory. something is really 471 * fishy here, so don't do anything and return a copy */ 472 return ASTRDUP(path); 473 } 474 475 /* Make a new path with <current-path>/<path> */ 476 currentLen = strlen(currentDir); 477 result = malloc(currentLen + pathLen + 2); 478 479 memcpy(result, currentDir, currentLen); 480 if (currentLen == 0 || result[currentLen-1] != '/') { 481 result[currentLen++] = '/'; 482 } 483 memcpy(result + currentLen, path, pathLen+1); 484 485 return result; 486 } 487#endif 488} 489 490/** OTHER FILE UTILITIES 491 ** 492 ** path_empty_file() creates an empty file at a given path location. 493 ** if the file already exists, it is truncated without warning 494 ** 495 ** path_copy_file() copies one file into another. 496 ** 497 ** both functions return 0 on success, and -1 on error 498 **/ 499 500APosixStatus 501path_empty_file( const char* path ) 502{ 503#ifdef _WIN32 504 int fd = _creat( path, S_IWRITE ); 505#else 506 /* on Unix, only allow the owner to read/write, since the file * 507 * may contain some personal data we don't want to see exposed */ 508 int fd = creat(path, S_IRUSR | S_IWUSR); 509#endif 510 if (fd >= 0) { 511 close(fd); 512 return 0; 513 } 514 return -1; 515} 516 517APosixStatus 518path_copy_file( const char* dest, const char* source ) 519{ 520 int fd, fs, result = -1; 521 522 /* if the destination doesn't exist, create it */ 523 if ( access(source, F_OK) < 0 || 524 path_empty_file(dest) < 0) { 525 return -1; 526 } 527 528 if ( access(source, R_OK) < 0 ) { 529 D("%s: source file is un-readable: %s\n", 530 __FUNCTION__, source); 531 return -1; 532 } 533 534#ifdef _WIN32 535 fd = _open(dest, _O_RDWR | _O_BINARY); 536 fs = _open(source, _O_RDONLY | _O_BINARY); 537#else 538 fd = creat(dest, S_IRUSR | S_IWUSR); 539 fs = open(source, S_IREAD); 540#endif 541 if (fs >= 0 && fd >= 0) { 542 char buf[4096]; 543 ssize_t total = 0; 544 ssize_t n; 545 result = 0; /* success */ 546 while ((n = read(fs, buf, 4096)) > 0) { 547 if (write(fd, buf, n) != n) { 548 /* write failed. Make it return -1 so that an 549 * empty file be created. */ 550 D("Failed to copy '%s' to '%s': %s (%d)", 551 source, dest, strerror(errno), errno); 552 result = -1; 553 break; 554 } 555 total += n; 556 } 557 } 558 559 if (fs >= 0) { 560 close(fs); 561 } 562 if (fd >= 0) { 563 close(fd); 564 } 565 return result; 566} 567 568 569APosixStatus 570path_delete_file( const char* path ) 571{ 572#ifdef _WIN32 573 int ret = _unlink( path ); 574 if (ret == -1 && errno == EACCES) { 575 /* a first call to _unlink will fail if the file is set read-only */ 576 /* we can however try to change its mode first and call unlink */ 577 /* again... */ 578 ret = _chmod( path, _S_IREAD | _S_IWRITE ); 579 if (ret == 0) 580 ret = _unlink( path ); 581 } 582 return ret; 583#else 584 return unlink(path); 585#endif 586} 587 588 589void* 590path_load_file(const char *fn, size_t *pSize) 591{ 592 char* data; 593 int sz; 594 int fd; 595 596 if (pSize) 597 *pSize = 0; 598 599 data = NULL; 600 601 fd = open(fn, O_BINARY | O_RDONLY); 602 if(fd < 0) return NULL; 603 604 do { 605 sz = lseek(fd, 0, SEEK_END); 606 if(sz < 0) break; 607 608 if (pSize) 609 *pSize = (size_t) sz; 610 611 if (lseek(fd, 0, SEEK_SET) != 0) 612 break; 613 614 data = (char*) malloc(sz + 1); 615 if(data == NULL) break; 616 617 if (read(fd, data, sz) != sz) 618 break; 619 620 close(fd); 621 data[sz] = 0; 622 623 return data; 624 } while (0); 625 626 close(fd); 627 628 if(data != NULL) 629 free(data); 630 631 return NULL; 632} 633 634#ifdef _WIN32 635# define DIR_SEP ';' 636#else 637# define DIR_SEP ':' 638#endif 639 640char* 641path_search_exec( const char* filename ) 642{ 643 const char* sysPath = getenv("PATH"); 644 char temp[PATH_MAX]; 645 int count; 646 int slen; 647 const char* p; 648 649 /* If the file contains a directory separator, don't search */ 650#ifdef _WIN32 651 if (strchr(filename, '/') != NULL || strchr(filename, '\\') != NULL) { 652#else 653 if (strchr(filename, '/') != NULL) { 654#endif 655 if (path_exists(filename)) { 656 return strdup(filename); 657 } else { 658 return NULL; 659 } 660 } 661 662 /* If system path is empty, don't search */ 663 if (sysPath == NULL || sysPath[0] == '\0') { 664 return NULL; 665 } 666 667 /* Count the number of non-empty items in the system path 668 * Items are separated by DIR_SEP, and two successive separators 669 * correspond to an empty item that will be ignored. 670 * Also compute the required string storage length. */ 671 count = 0; 672 slen = 0; 673 p = sysPath; 674 675 while (*p) { 676 char* p2 = strchr(p, DIR_SEP); 677 int len; 678 if (p2 == NULL) { 679 len = strlen(p); 680 } else { 681 len = p2 - p; 682 } 683 684 do { 685 if (len <= 0) 686 break; 687 688 snprintf(temp, sizeof(temp), "%.*s/%s", len, p, filename); 689 690 if (path_exists(temp) && path_can_exec(temp)) { 691 return strdup(temp); 692 } 693 694 } while (0); 695 696 p += len; 697 if (*p == DIR_SEP) 698 p++; 699 } 700 701 /* Nothing, really */ 702 return NULL; 703} 704