Home | History | Annotate | Download | only in utils
      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