path.c revision ee2298a313b6e425d6ff0324be6a313b1cd9a399
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#define  D(...)  ((void)0)
38
39#ifndef CHECKED
40#  ifdef _WIN32
41#    define   CHECKED(ret, call)    (ret) = (call)
42#  else
43#    define   CHECKED(ret, call)    do { (ret) = (call); } while ((ret) < 0 && errno == EINTR)
44#  endif
45#endif
46
47/** PATH HANDLING ROUTINES
48 **
49 **  path_parent() can be used to return the n-level parent of a given directory
50 **  this understands . and .. when encountered in the input path
51 **/
52
53static __inline__ int
54ispathsep(int  c)
55{
56#ifdef _WIN32
57    return (c == '/' || c == '\\');
58#else
59    return (c == '/');
60#endif
61}
62
63char*
64path_parent( const char*  path, int  levels )
65{
66    const char*  end = path + strlen(path);
67    char*        result;
68
69    while (levels > 0) {
70        const char*  base;
71
72        /* trim any trailing path separator */
73        while (end > path && ispathsep(end[-1]))
74            end--;
75
76        base = end;
77        while (base > path && !ispathsep(base[-1]))
78            base--;
79
80        if (base <= path) /* we can't go that far */
81            return NULL;
82
83        if (end == base+1 && base[0] == '.')
84            goto Next;
85
86        if (end == base+2 && base[0] == '.' && base[1] == '.') {
87            levels += 1;
88            goto Next;
89        }
90
91        levels -= 1;
92
93    Next:
94        end = base - 1;
95    }
96    result = malloc( end-path+1 );
97    if (result != NULL) {
98        memcpy( result, path, end-path );
99        result[end-path] = 0;
100    }
101    return result;
102}
103
104static char*
105substring_dup( const char*  start, const char*  end )
106{
107    int    len    = end - start;
108    char*  result = android_alloc(len+1);
109    memcpy(result, start, len);
110    result[len] = 0;
111    return result;
112}
113
114int
115path_split( const char*  path, char* *pdirname, char* *pbasename )
116{
117    const char*  end = path + strlen(path);
118    const char*  last;
119    char*        basename;
120
121    /* prepare for errors */
122    if (pdirname)
123        *pdirname = NULL;
124    if (pbasename)
125        *pbasename = NULL;
126
127    /* handle empty path case */
128    if (end == path) {
129        return -1;
130    }
131
132    /* strip trailing path separators */
133    while (end > path && ispathsep(end[-1]))
134        end -= 1;
135
136    /* handle "/" and degenerate cases like "////" */
137    if (end == path) {
138        return -1;
139    }
140
141    /* find last separator */
142    last = end;
143    while (last > path && !ispathsep(last[-1]))
144        last -= 1;
145
146    /* handle cases where there is no path separator */
147    if (last == path) {
148        if (pdirname)
149            *pdirname  = ASTRDUP(".");
150        if (pbasename)
151            *pbasename = substring_dup(path,end);
152        return 0;
153    }
154
155    /* handle "/foo" */
156    if (last == path+1) {
157        if (pdirname)
158            *pdirname  = ASTRDUP("/");
159        if (pbasename)
160            *pbasename = substring_dup(path+1,end);
161        return 0;
162    }
163
164    /* compute basename */
165    basename = substring_dup(last,end);
166    if (strcmp(basename, ".") == 0 || strcmp(basename, "..") == 0) {
167        AFREE(basename);
168        return -1;
169    }
170
171    if (pbasename)
172        *pbasename = basename;
173    else {
174        AFREE(basename);
175    }
176
177    /* compute dirname */
178    if (pdirname != NULL)
179        *pdirname = substring_dup(path,last-1);
180
181    return 0;
182}
183
184char*
185path_basename( const char*  path )
186{
187    char*  basename;
188
189    if (path_split(path, NULL, &basename) < 0)
190        return NULL;
191
192    return basename;
193}
194
195char*
196path_dirname( const char*  path )
197{
198    char*  dirname;
199
200    if (path_split(path, &dirname, NULL) < 0)
201        return NULL;
202
203    return dirname;
204}
205
206
207
208
209
210/** MISC FILE AND DIRECTORY HANDLING
211 **/
212
213ABool
214path_exists( const char*  path )
215{
216    int  ret;
217    CHECKED(ret, 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    int          ret;
226    struct stat  st;
227
228    CHECKED(ret, stat(path, &st));
229    if (ret < 0)
230        return 0;
231
232    return S_ISREG(st.st_mode);
233}
234
235
236/* checks that a path points to a directory */
237ABool
238path_is_dir( const char*  path )
239{
240    int          ret;
241    struct stat  st;
242
243    CHECKED(ret, stat(path, &st));
244    if (ret < 0)
245        return 0;
246
247    return S_ISDIR(st.st_mode);
248}
249
250/* checks that one can read/write a given (regular) file */
251ABool
252path_can_read( const char*  path )
253{
254    int  ret;
255    CHECKED(ret, access(path, R_OK));
256    return (ret == 0);
257}
258
259ABool
260path_can_write( const char*  path )
261{
262    int  ret;
263    CHECKED(ret, access(path, R_OK));
264    return (ret == 0);
265}
266
267/* try to make a directory. returns 0 on success, -1 on failure
268 * (error code in errno) */
269APosixStatus
270path_mkdir( const char*  path, int  mode )
271{
272#ifdef _WIN32
273    (void)mode;
274    return _mkdir(path);
275#else
276    int  ret;
277    CHECKED(ret, mkdir(path, mode));
278    return ret;
279#endif
280}
281
282static APosixStatus
283path_mkdir_recursive( char*  path, unsigned  len, int  mode )
284{
285    char      old_c;
286    int       ret;
287    unsigned  len2;
288
289    /* get rid of trailing separators */
290    while (len > 0 && ispathsep(path[len-1]))
291        len -= 1;
292
293    if (len == 0) {
294        errno = ENOENT;
295        return -1;
296    }
297
298    /* check that the parent exists, 'len2' is the length of
299     * the parent part of the path */
300    len2 = len-1;
301    while (len2 > 0 && !ispathsep(path[len2-1]))
302        len2 -= 1;
303
304    if (len2 > 0) {
305        old_c      = path[len2];
306        path[len2] = 0;
307        ret        = 0;
308        if ( !path_exists(path) ) {
309            /* the parent doesn't exist, so try to create it */
310            ret = path_mkdir_recursive( path, len2, mode );
311        }
312        path[len2] = old_c;
313
314        if (ret < 0)
315            return ret;
316    }
317
318    /* at this point, we now the parent exists */
319    old_c     = path[len];
320    path[len] = 0;
321    ret       = path_mkdir( path, mode );
322    path[len] = old_c;
323
324    return ret;
325}
326
327/* ensure that a given directory exists, create it if not,
328   0 on success, -1 on failure (error code in errno) */
329APosixStatus
330path_mkdir_if_needed( const char*  path, int  mode )
331{
332    int  ret = 0;
333
334    if (!path_exists(path)) {
335        ret = path_mkdir(path, mode);
336
337        if (ret < 0 && errno == ENOENT) {
338            char      temp[MAX_PATH];
339            unsigned  len = (unsigned)strlen(path);
340
341            if (len > sizeof(temp)-1) {
342                errno = EINVAL;
343                return -1;
344            }
345            memcpy( temp, path, len );
346            temp[len] = 0;
347
348            return path_mkdir_recursive(temp, len, mode);
349        }
350    }
351    return ret;
352}
353
354/* return the size of a given file in '*psize'. returns 0 on
355 * success, -1 on failure (error code in errno) */
356APosixStatus
357path_get_size( const char*  path, uint64_t  *psize )
358{
359#ifdef _WIN32
360    /* avoid _stat64 which is only defined in MSVCRT.DLL, not CRTDLL.DLL */
361    /* do not use OpenFile() because it has strange search behaviour that could */
362    /* result in getting the size of a different file */
363    LARGE_INTEGER  size;
364    HANDLE  file = CreateFile( /* lpFilename */        path,
365                               /* dwDesiredAccess */   GENERIC_READ,
366                               /* dwSharedMode */     FILE_SHARE_READ|FILE_SHARE_WRITE,
367                               /* lpSecurityAttributes */  NULL,
368                               /* dwCreationDisposition */ OPEN_EXISTING,
369                               /* dwFlagsAndAttributes */  0,
370                               /* hTemplateFile */      NULL );
371    if (file == INVALID_HANDLE_VALUE) {
372        /* ok, just to play fair */
373        errno = ENOENT;
374        return -1;
375    }
376    if (!GetFileSizeEx(file, &size)) {
377        /* maybe we tried to get the size of a pipe or something like that ? */
378        *psize = 0;
379    }
380    else {
381        *psize = (uint64_t) size.QuadPart;
382    }
383    CloseHandle(file);
384    return 0;
385#else
386    int    ret;
387    struct stat  st;
388
389    CHECKED(ret, stat(path, &st));
390    if (ret == 0) {
391        *psize = (uint64_t) st.st_size;
392    }
393    return ret;
394#endif
395}
396
397
398ABool
399path_is_absolute( const char*  path )
400{
401#ifdef _WIN32
402    if (path == NULL)
403        return 0;
404
405    if (path[0] == '/' || path[0] == '\\')
406        return 1;
407
408    /* 'C:' is always considered to be absolute
409     * even if used with a relative path like C:foo which
410     * is different from C:\foo
411     */
412    if (path[0] != 0 && path[1] == ':')
413        return 1;
414
415    return 0;
416#else
417    return (path != NULL && path[0] == '/');
418#endif
419}
420
421
422/** OTHER FILE UTILITIES
423 **
424 **  path_empty_file() creates an empty file at a given path location.
425 **  if the file already exists, it is truncated without warning
426 **
427 **  path_copy_file() copies one file into another.
428 **
429 **  both functions return 0 on success, and -1 on error
430 **/
431
432APosixStatus
433path_empty_file( const char*  path )
434{
435#ifdef _WIN32
436    int  fd = _creat( path, S_IWRITE );
437#else
438    /* on Unix, only allow the owner to read/write, since the file *
439     * may contain some personal data we don't want to see exposed */
440    int  fd = creat(path, S_IRUSR | S_IWUSR);
441#endif
442    if (fd >= 0) {
443        close(fd);
444        return 0;
445    }
446    return -1;
447}
448
449APosixStatus
450path_copy_file( const char*  dest, const char*  source )
451{
452    int  fd, fs, result = -1;
453
454    /* if the destination doesn't exist, create it */
455    if ( access(source, F_OK)  < 0 ||
456         path_empty_file(dest) < 0) {
457        return -1;
458    }
459
460#ifdef _WIN32
461    fd = _open(dest, _O_RDWR | _O_BINARY);
462    fs = _open(source, _O_RDONLY |  _O_BINARY);
463#else
464    fd = creat(dest, S_IRUSR | S_IWUSR);
465    fs = open(source, S_IREAD);
466#endif
467    if (fs >= 0 && fd >= 0) {
468        char buf[4096];
469        ssize_t total = 0;
470        ssize_t n;
471        result = 0; /* success */
472        while ((n = read(fs, buf, 4096)) > 0) {
473            if (write(fd, buf, n) != n) {
474                /* write failed. Make it return -1 so that an
475                 * empty file be created. */
476                D("Failed to copy '%s' to '%s': %s (%d)",
477                       source, dest, strerror(errno), errno);
478                result = -1;
479                break;
480            }
481            total += n;
482        }
483    }
484
485    if (fs >= 0) {
486        close(fs);
487    }
488    if (fd >= 0) {
489        close(fd);
490    }
491    return result;
492}
493
494
495APosixStatus
496path_delete_file( const char*  path )
497{
498#ifdef _WIN32
499    int  ret = _unlink( path );
500    if (ret == -1 && errno == EACCES) {
501        /* a first call to _unlink will fail if the file is set read-only */
502        /* we can however try to change its mode first and call unlink    */
503        /* again...                                                       */
504        ret = _chmod( path, _S_IREAD | _S_IWRITE );
505        if (ret == 0)
506            ret = _unlink( path );
507    }
508    return ret;
509#else
510    return  unlink(path);
511#endif
512}
513
514
515void*
516path_load_file(const char *fn, size_t  *pSize)
517{
518    char*  data;
519    int    sz;
520    int    fd;
521
522    if (pSize)
523        *pSize = 0;
524
525    data   = NULL;
526
527    fd = open(fn, O_BINARY | O_RDONLY);
528    if(fd < 0) return NULL;
529
530    do {
531        sz = lseek(fd, 0, SEEK_END);
532        if(sz < 0) break;
533
534        if (pSize)
535            *pSize = (size_t) sz;
536
537        if (lseek(fd, 0, SEEK_SET) != 0)
538            break;
539
540        data = (char*) malloc(sz + 1);
541        if(data == NULL) break;
542
543        if (read(fd, data, sz) != sz)
544            break;
545
546        close(fd);
547        data[sz] = 0;
548
549        return data;
550    } while (0);
551
552    close(fd);
553
554    if(data != NULL)
555        free(data);
556
557    return NULL;
558}
559
560