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    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 */
229ABool
230path_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 */
246ABool
247path_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 */
262ABool
263path_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
272ABool
273path_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
282ABool
283path_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) */
294APosixStatus
295path_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
307static APosixStatus
308path_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) */
354APosixStatus
355path_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) */
381APosixStatus
382path_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
423ABool
424path_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
446char*
447path_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
512APosixStatus
513path_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
529APosixStatus
530path_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
581APosixStatus
582path_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
601void*
602path_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
652char*
653path_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