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 /* try to make a directory. returns 0 on success, -1 on failure
    269  * (error code in errno) */
    270 APosixStatus
    271 path_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 
    283 static APosixStatus
    284 path_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) */
    330 APosixStatus
    331 path_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) */
    357 APosixStatus
    358 path_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 
    399 ABool
    400 path_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 
    433 APosixStatus
    434 path_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 
    450 APosixStatus
    451 path_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 
    502 APosixStatus
    503 path_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 
    522 void*
    523 path_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