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) /* we can't go that far */ 82 return NULL; 83 84 if (end == base+1 && base[0] == '.') 85 goto Next; 86 87 if (end == base+2 && base[0] == '.' && base[1] == '.') { 88 levels += 1; 89 goto Next; 90 } 91 92 levels -= 1; 93 94 Next: 95 end = base - 1; 96 } 97 result = malloc( end-path+1 ); 98 if (result != NULL) { 99 memcpy( result, path, end-path ); 100 result[end-path] = 0; 101 } 102 return result; 103} 104 105static char* 106substring_dup( const char* start, const char* end ) 107{ 108 int len = end - start; 109 char* result = android_alloc(len+1); 110 memcpy(result, start, len); 111 result[len] = 0; 112 return result; 113} 114 115int 116path_split( const char* path, char* *pdirname, char* *pbasename ) 117{ 118 const char* end = path + strlen(path); 119 const char* last; 120 char* basename; 121 122 /* prepare for errors */ 123 if (pdirname) 124 *pdirname = NULL; 125 if (pbasename) 126 *pbasename = NULL; 127 128 /* handle empty path case */ 129 if (end == path) { 130 return -1; 131 } 132 133 /* strip trailing path separators */ 134 while (end > path && ispathsep(end[-1])) 135 end -= 1; 136 137 /* handle "/" and degenerate cases like "////" */ 138 if (end == path) { 139 return -1; 140 } 141 142 /* find last separator */ 143 last = end; 144 while (last > path && !ispathsep(last[-1])) 145 last -= 1; 146 147 /* handle cases where there is no path separator */ 148 if (last == path) { 149 if (pdirname) 150 *pdirname = ASTRDUP("."); 151 if (pbasename) 152 *pbasename = substring_dup(path,end); 153 return 0; 154 } 155 156 /* handle "/foo" */ 157 if (last == path+1) { 158 if (pdirname) 159 *pdirname = ASTRDUP("/"); 160 if (pbasename) 161 *pbasename = substring_dup(path+1,end); 162 return 0; 163 } 164 165 /* compute basename */ 166 basename = substring_dup(last,end); 167 if (strcmp(basename, ".") == 0 || strcmp(basename, "..") == 0) { 168 AFREE(basename); 169 return -1; 170 } 171 172 if (pbasename) 173 *pbasename = basename; 174 else { 175 AFREE(basename); 176 } 177 178 /* compute dirname */ 179 if (pdirname != NULL) 180 *pdirname = substring_dup(path,last-1); 181 182 return 0; 183} 184 185char* 186path_basename( const char* path ) 187{ 188 char* basename; 189 190 if (path_split(path, NULL, &basename) < 0) 191 return NULL; 192 193 return basename; 194} 195 196char* 197path_dirname( const char* path ) 198{ 199 char* dirname; 200 201 if (path_split(path, &dirname, NULL) < 0) 202 return NULL; 203 204 return dirname; 205} 206 207 208 209 210 211/** MISC FILE AND DIRECTORY HANDLING 212 **/ 213 214ABool 215path_exists( const char* path ) 216{ 217 int ret; 218 CHECKED(ret, access(path, F_OK)); 219 return (ret == 0) || (errno != ENOENT); 220} 221 222/* checks that a path points to a regular file */ 223ABool 224path_is_regular( const char* path ) 225{ 226 int ret; 227 struct stat st; 228 229 CHECKED(ret, stat(path, &st)); 230 if (ret < 0) 231 return 0; 232 233 return S_ISREG(st.st_mode); 234} 235 236 237/* checks that a path points to a directory */ 238ABool 239path_is_dir( const char* path ) 240{ 241 int ret; 242 struct stat st; 243 244 CHECKED(ret, stat(path, &st)); 245 if (ret < 0) 246 return 0; 247 248 return S_ISDIR(st.st_mode); 249} 250 251/* checks that one can read/write a given (regular) file */ 252ABool 253path_can_read( const char* path ) 254{ 255 int ret; 256 CHECKED(ret, access(path, R_OK)); 257 return (ret == 0); 258} 259 260ABool 261path_can_write( const char* path ) 262{ 263 int ret; 264 CHECKED(ret, access(path, R_OK)); 265 return (ret == 0); 266} 267 268/* try to make a directory. returns 0 on success, -1 on failure 269 * (error code in errno) */ 270APosixStatus 271path_mkdir( const char* path, int mode ) 272{ 273#ifdef _WIN32 274 (void)mode; 275 return _mkdir(path); 276#else 277 int ret; 278 CHECKED(ret, mkdir(path, mode)); 279 return ret; 280#endif 281} 282 283static APosixStatus 284path_mkdir_recursive( char* path, unsigned len, int mode ) 285{ 286 char old_c; 287 int ret; 288 unsigned len2; 289 290 /* get rid of trailing separators */ 291 while (len > 0 && ispathsep(path[len-1])) 292 len -= 1; 293 294 if (len == 0) { 295 errno = ENOENT; 296 return -1; 297 } 298 299 /* check that the parent exists, 'len2' is the length of 300 * the parent part of the path */ 301 len2 = len-1; 302 while (len2 > 0 && !ispathsep(path[len2-1])) 303 len2 -= 1; 304 305 if (len2 > 0) { 306 old_c = path[len2]; 307 path[len2] = 0; 308 ret = 0; 309 if ( !path_exists(path) ) { 310 /* the parent doesn't exist, so try to create it */ 311 ret = path_mkdir_recursive( path, len2, mode ); 312 } 313 path[len2] = old_c; 314 315 if (ret < 0) 316 return ret; 317 } 318 319 /* at this point, we now the parent exists */ 320 old_c = path[len]; 321 path[len] = 0; 322 ret = path_mkdir( path, mode ); 323 path[len] = old_c; 324 325 return ret; 326} 327 328/* ensure that a given directory exists, create it if not, 329 0 on success, -1 on failure (error code in errno) */ 330APosixStatus 331path_mkdir_if_needed( const char* path, int mode ) 332{ 333 int ret = 0; 334 335 if (!path_exists(path)) { 336 ret = path_mkdir(path, mode); 337 338 if (ret < 0 && errno == ENOENT) { 339 char temp[MAX_PATH]; 340 unsigned len = (unsigned)strlen(path); 341 342 if (len > sizeof(temp)-1) { 343 errno = EINVAL; 344 return -1; 345 } 346 memcpy( temp, path, len ); 347 temp[len] = 0; 348 349 return path_mkdir_recursive(temp, len, mode); 350 } 351 } 352 return ret; 353} 354 355/* return the size of a given file in '*psize'. returns 0 on 356 * success, -1 on failure (error code in errno) */ 357APosixStatus 358path_get_size( const char* path, uint64_t *psize ) 359{ 360#ifdef _WIN32 361 /* avoid _stat64 which is only defined in MSVCRT.DLL, not CRTDLL.DLL */ 362 /* do not use OpenFile() because it has strange search behaviour that could */ 363 /* result in getting the size of a different file */ 364 LARGE_INTEGER size; 365 HANDLE file = CreateFile( /* lpFilename */ path, 366 /* dwDesiredAccess */ GENERIC_READ, 367 /* dwSharedMode */ FILE_SHARE_READ|FILE_SHARE_WRITE, 368 /* lpSecurityAttributes */ NULL, 369 /* dwCreationDisposition */ OPEN_EXISTING, 370 /* dwFlagsAndAttributes */ 0, 371 /* hTemplateFile */ NULL ); 372 if (file == INVALID_HANDLE_VALUE) { 373 /* ok, just to play fair */ 374 errno = ENOENT; 375 return -1; 376 } 377 if (!GetFileSizeEx(file, &size)) { 378 /* maybe we tried to get the size of a pipe or something like that ? */ 379 *psize = 0; 380 } 381 else { 382 *psize = (uint64_t) size.QuadPart; 383 } 384 CloseHandle(file); 385 return 0; 386#else 387 int ret; 388 struct stat st; 389 390 CHECKED(ret, stat(path, &st)); 391 if (ret == 0) { 392 *psize = (uint64_t) st.st_size; 393 } 394 return ret; 395#endif 396} 397 398 399ABool 400path_is_absolute( const char* path ) 401{ 402#ifdef _WIN32 403 if (path == NULL) 404 return 0; 405 406 if (path[0] == '/' || path[0] == '\\') 407 return 1; 408 409 /* 'C:' is always considered to be absolute 410 * even if used with a relative path like C:foo which 411 * is different from C:\foo 412 */ 413 if (path[0] != 0 && path[1] == ':') 414 return 1; 415 416 return 0; 417#else 418 return (path != NULL && path[0] == '/'); 419#endif 420} 421 422 423/** OTHER FILE UTILITIES 424 ** 425 ** path_empty_file() creates an empty file at a given path location. 426 ** if the file already exists, it is truncated without warning 427 ** 428 ** path_copy_file() copies one file into another. 429 ** 430 ** both functions return 0 on success, and -1 on error 431 **/ 432 433APosixStatus 434path_empty_file( const char* path ) 435{ 436#ifdef _WIN32 437 int fd = _creat( path, S_IWRITE ); 438#else 439 /* on Unix, only allow the owner to read/write, since the file * 440 * may contain some personal data we don't want to see exposed */ 441 int fd = creat(path, S_IRUSR | S_IWUSR); 442#endif 443 if (fd >= 0) { 444 close(fd); 445 return 0; 446 } 447 return -1; 448} 449 450APosixStatus 451path_copy_file( const char* dest, const char* source ) 452{ 453 int fd, fs, result = -1; 454 455 /* if the destination doesn't exist, create it */ 456 if ( access(source, F_OK) < 0 || 457 path_empty_file(dest) < 0) { 458 return -1; 459 } 460 461 if ( access(source, R_OK) < 0 ) { 462 D("%s: source file is un-readable: %s\n", 463 __FUNCTION__, source); 464 return -1; 465 } 466 467#ifdef _WIN32 468 fd = _open(dest, _O_RDWR | _O_BINARY); 469 fs = _open(source, _O_RDONLY | _O_BINARY); 470#else 471 fd = creat(dest, S_IRUSR | S_IWUSR); 472 fs = open(source, S_IREAD); 473#endif 474 if (fs >= 0 && fd >= 0) { 475 char buf[4096]; 476 ssize_t total = 0; 477 ssize_t n; 478 result = 0; /* success */ 479 while ((n = read(fs, buf, 4096)) > 0) { 480 if (write(fd, buf, n) != n) { 481 /* write failed. Make it return -1 so that an 482 * empty file be created. */ 483 D("Failed to copy '%s' to '%s': %s (%d)", 484 source, dest, strerror(errno), errno); 485 result = -1; 486 break; 487 } 488 total += n; 489 } 490 } 491 492 if (fs >= 0) { 493 close(fs); 494 } 495 if (fd >= 0) { 496 close(fd); 497 } 498 return result; 499} 500 501 502APosixStatus 503path_delete_file( const char* path ) 504{ 505#ifdef _WIN32 506 int ret = _unlink( path ); 507 if (ret == -1 && errno == EACCES) { 508 /* a first call to _unlink will fail if the file is set read-only */ 509 /* we can however try to change its mode first and call unlink */ 510 /* again... */ 511 ret = _chmod( path, _S_IREAD | _S_IWRITE ); 512 if (ret == 0) 513 ret = _unlink( path ); 514 } 515 return ret; 516#else 517 return unlink(path); 518#endif 519} 520 521 522void* 523path_load_file(const char *fn, size_t *pSize) 524{ 525 char* data; 526 int sz; 527 int fd; 528 529 if (pSize) 530 *pSize = 0; 531 532 data = NULL; 533 534 fd = open(fn, O_BINARY | O_RDONLY); 535 if(fd < 0) return NULL; 536 537 do { 538 sz = lseek(fd, 0, SEEK_END); 539 if(sz < 0) break; 540 541 if (pSize) 542 *pSize = (size_t) sz; 543 544 if (lseek(fd, 0, SEEK_SET) != 0) 545 break; 546 547 data = (char*) malloc(sz + 1); 548 if(data == NULL) break; 549 550 if (read(fd, data, sz) != sz) 551 break; 552 553 close(fd); 554 data[sz] = 0; 555 556 return data; 557 } while (0); 558 559 close(fd); 560 561 if(data != NULL) 562 free(data); 563 564 return NULL; 565} 566 567