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