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 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 */
229 ABool
path_is_regular(const char * path)230 path_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 */
246 ABool
path_is_dir(const char * path)247 path_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 */
262 ABool
path_can_read(const char * path)263 path_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
272 ABool
path_can_write(const char * path)273 path_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
282 ABool
path_can_exec(const char * path)283 path_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) */
294 APosixStatus
path_mkdir(const char * path,int mode)295 path_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
307 static APosixStatus
path_mkdir_recursive(char * path,unsigned len,int mode)308 path_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) */
354 APosixStatus
path_mkdir_if_needed(const char * path,int mode)355 path_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) */
381 APosixStatus
path_get_size(const char * path,uint64_t * psize)382 path_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
423 ABool
path_is_absolute(const char * path)424 path_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
446 char*
path_get_absolute(const char * path)447 path_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
512 APosixStatus
path_empty_file(const char * path)513 path_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
529 APosixStatus
path_copy_file(const char * dest,const char * source)530 path_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
581 APosixStatus
path_delete_file(const char * path)582 path_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
601 void*
path_load_file(const char * fn,size_t * pSize)602 path_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
652 char*
path_search_exec(const char * filename)653 path_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