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) /* 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
105static char*
106substring_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
115int
116path_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
185char*
186path_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
196char*
197path_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
214ABool
215path_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 */
223ABool
224path_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 */
238ABool
239path_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 */
252ABool
253path_can_read( const char*  path )
254{
255    int  ret;
256    CHECKED(ret, access(path, R_OK));
257    return (ret == 0);
258}
259
260ABool
261path_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) */
270APosixStatus
271path_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
283static APosixStatus
284path_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) */
330APosixStatus
331path_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) */
357APosixStatus
358path_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
399ABool
400path_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
433APosixStatus
434path_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
450APosixStatus
451path_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
502APosixStatus
503path_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
522void*
523path_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