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