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