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
47static __inline__ int
48ispathsep(int  c)
49{
50#ifdef _WIN32
51    return (c == '/' || c == '\\');
52#else
53    return (c == '/');
54#endif
55}
56
57char*
58path_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
102static char*
103substring_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
112int
113path_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
182char*
183path_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
193char*
194path_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
211ABool
212path_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 */
222ABool
223path_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 */
238ABool
239path_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 */
253ABool
254path_can_read( const char*  path )
255{
256    if (!path)
257        return 0;
258
259    return HANDLE_EINTR(access(path, R_OK)) == 0;
260}
261
262ABool
263path_can_write( const char*  path )
264{
265    if (!path)
266        return 0;
267
268    return HANDLE_EINTR(access(path, W_OK)) == 0;
269}
270
271ABool
272path_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) */
282APosixStatus
283path_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
293static APosixStatus
294path_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) */
340APosixStatus
341path_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) */
367APosixStatus
368path_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
407ABool
408path_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
430char*
431path_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
496APosixStatus
497path_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
513APosixStatus
514path_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
565APosixStatus
566path_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
585void*
586path_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
636char*
637path_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