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/debug.h"
     13 #include "android/utils/eintr_wrapper.h"
     14 #include "android/utils/path.h"
     15 
     16 #include <stdio.h>
     17 #include <string.h>
     18 #include <stdlib.h>
     19 #include <errno.h>
     20 #include <fcntl.h>
     21 
     22 #ifdef _WIN32
     23 #include <process.h>
     24 #include <shlobj.h>
     25 #include <tlhelp32.h>
     26 #include <io.h>
     27 #include <sys/types.h>
     28 #include <sys/stat.h>
     29 #include <stdint.h>
     30 #include <limits.h>
     31 #include <winbase.h>
     32 #else
     33 #include <unistd.h>
     34 #include <sys/stat.h>
     35 #include <time.h>
     36 #include <signal.h>
     37 #endif
     38 
     39 #define  D(...)  VERBOSE_PRINT(init,__VA_ARGS__)
     40 
     41 /** PATH HANDLING ROUTINES
     42  **
     43  **  path_parent() can be used to return the n-level parent of a given directory
     44  **  this understands . and .. when encountered in the input path
     45  **/
     46 
     47 static __inline__ int
     48 ispathsep(int  c)
     49 {
     50 #ifdef _WIN32
     51     return (c == '/' || c == '\\');
     52 #else
     53     return (c == '/');
     54 #endif
     55 }
     56 
     57 char*
     58 path_parent( const char*  path, int  levels )
     59 {
     60     const char*  end = path + strlen(path);
     61     char*        result;
     62 
     63     while (levels > 0) {
     64         const char*  base;
     65 
     66         /* trim any trailing path separator */
     67         while (end > path && ispathsep(end[-1]))
     68             end--;
     69 
     70         base = end;
     71         while (base > path && !ispathsep(base[-1]))
     72             base--;
     73 
     74         if (base <= path) {
     75             if (end == base+1 && base[0] == '.' && levels == 1)
     76                 return strdup("..");
     77           /* we can't go that far */
     78             return NULL;
     79         }
     80 
     81         if (end == base+1 && base[0] == '.')
     82             goto Next;
     83 
     84         if (end == base+2 && base[0] == '.' && base[1] == '.') {
     85             levels += 1;
     86             goto Next;
     87         }
     88 
     89         levels -= 1;
     90 
     91     Next:
     92         end = base - 1;
     93     }
     94     result = malloc( end-path+1 );
     95     if (result != NULL) {
     96         memcpy( result, path, end-path );
     97         result[end-path] = 0;
     98     }
     99     return result;
    100 }
    101 
    102 static char*
    103 substring_dup( const char*  start, const char*  end )
    104 {
    105     int    len    = end - start;
    106     char*  result = android_alloc(len+1);
    107     memcpy(result, start, len);
    108     result[len] = 0;
    109     return result;
    110 }
    111 
    112 int
    113 path_split( const char*  path, char* *pdirname, char* *pbasename )
    114 {
    115     const char*  end = path + strlen(path);
    116     const char*  last;
    117     char*        basename;
    118 
    119     /* prepare for errors */
    120     if (pdirname)
    121         *pdirname = NULL;
    122     if (pbasename)
    123         *pbasename = NULL;
    124 
    125     /* handle empty path case */
    126     if (end == path) {
    127         return -1;
    128     }
    129 
    130     /* strip trailing path separators */
    131     while (end > path && ispathsep(end[-1]))
    132         end -= 1;
    133 
    134     /* handle "/" and degenerate cases like "////" */
    135     if (end == path) {
    136         return -1;
    137     }
    138 
    139     /* find last separator */
    140     last = end;
    141     while (last > path && !ispathsep(last[-1]))
    142         last -= 1;
    143 
    144     /* handle cases where there is no path separator */
    145     if (last == path) {
    146         if (pdirname)
    147             *pdirname  = ASTRDUP(".");
    148         if (pbasename)
    149             *pbasename = substring_dup(path,end);
    150         return 0;
    151     }
    152 
    153     /* handle "/foo" */
    154     if (last == path+1) {
    155         if (pdirname)
    156             *pdirname  = ASTRDUP("/");
    157         if (pbasename)
    158             *pbasename = substring_dup(path+1,end);
    159         return 0;
    160     }
    161 
    162     /* compute basename */
    163     basename = substring_dup(last,end);
    164     if (strcmp(basename, ".") == 0 || strcmp(basename, "..") == 0) {
    165         AFREE(basename);
    166         return -1;
    167     }
    168 
    169     if (pbasename)
    170         *pbasename = basename;
    171     else {
    172         AFREE(basename);
    173     }
    174 
    175     /* compute dirname */
    176     if (pdirname != NULL)
    177         *pdirname = substring_dup(path,last-1);
    178 
    179     return 0;
    180 }
    181 
    182 char*
    183 path_basename( const char*  path )
    184 {
    185     char*  basename;
    186 
    187     if (path_split(path, NULL, &basename) < 0)
    188         return NULL;
    189 
    190     return basename;
    191 }
    192 
    193 char*
    194 path_dirname( const char*  path )
    195 {
    196     char*  dirname;
    197 
    198     if (path_split(path, &dirname, NULL) < 0)
    199         return NULL;
    200 
    201     return dirname;
    202 }
    203 
    204 
    205 
    206 
    207 
    208 /** MISC FILE AND DIRECTORY HANDLING
    209  **/
    210 
    211 ABool
    212 path_exists( const char*  path )
    213 {
    214     if (!path)
    215         return 0;
    216 
    217     int ret = HANDLE_EINTR(access(path, F_OK));
    218     return (ret == 0) || (errno != ENOENT);
    219 }
    220 
    221 /* checks that a path points to a regular file */
    222 ABool
    223 path_is_regular( const char*  path )
    224 {
    225     if (path == NULL)
    226         return 0;
    227 
    228     struct stat  st;
    229     int ret = HANDLE_EINTR(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     if (!path)
    242         return 0;
    243 
    244     struct stat  st;
    245     int ret = HANDLE_EINTR(stat(path, &st));
    246     if (ret < 0)
    247         return 0;
    248 
    249     return S_ISDIR(st.st_mode);
    250 }
    251 
    252 /* checks that one can read/write a given (regular) file */
    253 ABool
    254 path_can_read( const char*  path )
    255 {
    256     if (!path)
    257         return 0;
    258 
    259     return HANDLE_EINTR(access(path, R_OK)) == 0;
    260 }
    261 
    262 ABool
    263 path_can_write( const char*  path )
    264 {
    265     if (!path)
    266         return 0;
    267 
    268     return HANDLE_EINTR(access(path, W_OK)) == 0;
    269 }
    270 
    271 ABool
    272 path_can_exec( const char* path )
    273 {
    274     if (!path)
    275         return 0;
    276 
    277     return HANDLE_EINTR(access(path, X_OK)) == 0;
    278 }
    279 
    280 /* try to make a directory. returns 0 on success, -1 on failure
    281  * (error code in errno) */
    282 APosixStatus
    283 path_mkdir( const char*  path, int  mode )
    284 {
    285 #ifdef _WIN32
    286     (void)mode;
    287     return mkdir(path);
    288 #else
    289     return HANDLE_EINTR(mkdir(path, mode));
    290 #endif
    291 }
    292 
    293 static APosixStatus
    294 path_mkdir_recursive( char*  path, unsigned  len, int  mode )
    295 {
    296     char      old_c;
    297     int       ret;
    298     unsigned  len2;
    299 
    300     /* get rid of trailing separators */
    301     while (len > 0 && ispathsep(path[len-1]))
    302         len -= 1;
    303 
    304     if (len == 0) {
    305         errno = ENOENT;
    306         return -1;
    307     }
    308 
    309     /* check that the parent exists, 'len2' is the length of
    310      * the parent part of the path */
    311     len2 = len-1;
    312     while (len2 > 0 && !ispathsep(path[len2-1]))
    313         len2 -= 1;
    314 
    315     if (len2 > 0) {
    316         old_c      = path[len2];
    317         path[len2] = 0;
    318         ret        = 0;
    319         if ( !path_exists(path) ) {
    320             /* the parent doesn't exist, so try to create it */
    321             ret = path_mkdir_recursive( path, len2, mode );
    322         }
    323         path[len2] = old_c;
    324 
    325         if (ret < 0)
    326             return ret;
    327     }
    328 
    329     /* at this point, we now the parent exists */
    330     old_c     = path[len];
    331     path[len] = 0;
    332     ret       = path_mkdir( path, mode );
    333     path[len] = old_c;
    334 
    335     return ret;
    336 }
    337 
    338 /* ensure that a given directory exists, create it if not,
    339    0 on success, -1 on failure (error code in errno) */
    340 APosixStatus
    341 path_mkdir_if_needed( const char*  path, int  mode )
    342 {
    343     int  ret = 0;
    344 
    345     if (!path_exists(path)) {
    346         ret = path_mkdir(path, mode);
    347 
    348         if (ret < 0 && errno == ENOENT) {
    349             char      temp[MAX_PATH];
    350             unsigned  len = (unsigned)strlen(path);
    351 
    352             if (len > sizeof(temp)-1) {
    353                 errno = EINVAL;
    354                 return -1;
    355             }
    356             memcpy( temp, path, len );
    357             temp[len] = 0;
    358 
    359             return path_mkdir_recursive(temp, len, mode);
    360         }
    361     }
    362     return ret;
    363 }
    364 
    365 /* return the size of a given file in '*psize'. returns 0 on
    366  * success, -1 on failure (error code in errno) */
    367 APosixStatus
    368 path_get_size( const char*  path, uint64_t  *psize )
    369 {
    370 #ifdef _WIN32
    371     /* avoid _stat64 which is only defined in MSVCRT.DLL, not CRTDLL.DLL */
    372     /* do not use OpenFile() because it has strange search behaviour that could */
    373     /* result in getting the size of a different file */
    374     LARGE_INTEGER  size;
    375     HANDLE  file = CreateFile( /* lpFilename */        path,
    376                                /* dwDesiredAccess */   GENERIC_READ,
    377                                /* dwSharedMode */     FILE_SHARE_READ|FILE_SHARE_WRITE,
    378                                /* lpSecurityAttributes */  NULL,
    379                                /* dwCreationDisposition */ OPEN_EXISTING,
    380                                /* dwFlagsAndAttributes */  0,
    381                                /* hTemplateFile */      NULL );
    382     if (file == INVALID_HANDLE_VALUE) {
    383         /* ok, just to play fair */
    384         errno = ENOENT;
    385         return -1;
    386     }
    387     if (!GetFileSizeEx(file, &size)) {
    388         /* maybe we tried to get the size of a pipe or something like that ? */
    389         *psize = 0;
    390     }
    391     else {
    392         *psize = (uint64_t) size.QuadPart;
    393     }
    394     CloseHandle(file);
    395     return 0;
    396 #else
    397     struct stat  st;
    398     int ret = HANDLE_EINTR(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     const char* p;
    642 
    643     /* If the file contains a directory separator, don't search */
    644 #ifdef _WIN32
    645     if (strchr(filename, '/') != NULL || strchr(filename, '\\') != NULL) {
    646 #else
    647     if (strchr(filename, '/') != NULL) {
    648 #endif
    649         if (path_exists(filename)) {
    650             return strdup(filename);
    651         } else {
    652             return NULL;
    653         }
    654     }
    655 
    656     /* If system path is empty, don't search */
    657     if (sysPath == NULL || sysPath[0] == '\0') {
    658         return NULL;
    659     }
    660 
    661     /* Count the number of non-empty items in the system path
    662      * Items are separated by DIR_SEP, and two successive separators
    663      * correspond to an empty item that will be ignored.
    664      * Also compute the required string storage length. */
    665     p = sysPath;
    666 
    667     while (*p) {
    668         char* p2 = strchr(p, DIR_SEP);
    669         int   len;
    670         if (p2 == NULL) {
    671             len = strlen(p);
    672         } else {
    673             len = p2 - p;
    674         }
    675 
    676         do {
    677             if (len <= 0)
    678                 break;
    679 
    680             snprintf(temp, sizeof(temp), "%.*s/%s", len, p, filename);
    681 
    682             if (path_exists(temp) && path_can_exec(temp)) {
    683                 return strdup(temp);
    684             }
    685 
    686         } while (0);
    687 
    688         p += len;
    689         if (*p == DIR_SEP)
    690             p++;
    691     }
    692 
    693     /* Nothing, really */
    694     return NULL;
    695 }
    696