• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
54 static __inline__ int
ispathsep(int c)55 ispathsep(int  c)
56 {
57 #ifdef _WIN32
58     return (c == '/' || c == '\\');
59 #else
60     return (c == '/');
61 #endif
62 }
63 
64 char*
path_parent(const char * path,int levels)65 path_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 
109 static char*
substring_dup(const char * start,const char * end)110 substring_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 
119 int
path_split(const char * path,char ** pdirname,char ** pbasename)120 path_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 
189 char*
path_basename(const char * path)190 path_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 
200 char*
path_dirname(const char * path)201 path_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 
218 ABool
path_exists(const char * path)219 path_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 */
227 ABool
path_is_regular(const char * path)228 path_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 */
242 ABool
path_is_dir(const char * path)243 path_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 */
256 ABool
path_can_read(const char * path)257 path_can_read( const char*  path )
258 {
259     int  ret;
260     CHECKED(ret, access(path, R_OK));
261     return (ret == 0);
262 }
263 
264 ABool
path_can_write(const char * path)265 path_can_write( const char*  path )
266 {
267     int  ret;
268     CHECKED(ret, access(path, R_OK));
269     return (ret == 0);
270 }
271 
272 ABool
path_can_exec(const char * path)273 path_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) */
282 APosixStatus
path_mkdir(const char * path,int mode)283 path_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 
295 static APosixStatus
path_mkdir_recursive(char * path,unsigned len,int mode)296 path_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) */
342 APosixStatus
path_mkdir_if_needed(const char * path,int mode)343 path_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) */
369 APosixStatus
path_get_size(const char * path,uint64_t * psize)370 path_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 
411 ABool
path_is_absolute(const char * path)412 path_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 
434 char*
path_get_absolute(const char * path)435 path_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 
500 APosixStatus
path_empty_file(const char * path)501 path_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 
517 APosixStatus
path_copy_file(const char * dest,const char * source)518 path_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 
569 APosixStatus
path_delete_file(const char * path)570 path_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 
589 void*
path_load_file(const char * fn,size_t * pSize)590 path_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 
640 char*
path_search_exec(const char * filename)641 path_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