diff --git a/apps/tagcache.c b/apps/tagcache.c index 3565d8e5d4..0831bab32d 100644 --- a/apps/tagcache.c +++ b/apps/tagcache.c @@ -60,6 +60,9 @@ #include #include #include +#ifdef APPLICATION +#include /* readlink() */ +#endif #include "config.h" #include "ata_idle_notify.h" #include "thread.h" @@ -77,6 +80,7 @@ #include "dir.h" #include "filefuncs.h" #include "structec.h" +#include "debug.h" #ifndef __PCTOOL__ #include "lang.h" @@ -4189,6 +4193,88 @@ static void __attribute__ ((noinline)) check_ignore(const char *dirname, *unignore = file_exists(newpath); } +static struct search_roots_ll { + const char *path; + struct search_roots_ll * next; +} roots_ll; + +#ifdef APPLICATION +/* + * This adds a path to the search roots, possibly during traveling through + * the filesystem. It only adds if the path is not inside an already existing + * search root. + * + * Returns true if it added the path to the search roots + * + * Windows 2000 and greater supports symlinks, but they don't provide + * realpath() or readlink(), and symlinks are rarely used on them so + * ignore this for windows for now + **/ +static bool add_search_root(const char *name) +{ + (void)name; +#ifndef WIN32 + struct search_roots_ll *this, *prev = NULL; + char target[MAX_PATH]; + char _abs_target[MAX_PATH]; + char * abs_target; + ssize_t len; + + len = readlink(name, target, sizeof(target)); + if (len < 0) + return false; + + target[len] = '\0'; + /* realpath(target, NULL) doesn't work on android ... */ + abs_target = realpath(target, _abs_target); + if (abs_target == NULL) + return false; + + for(this = &roots_ll; this; prev = this, this = this->next) + { + size_t root_len = strlen(this->path); + /* check if the link target is inside of an existing search root + * don't add if target is inside, we'll scan it later */ + if (!strncmp(this->path, abs_target, root_len)) + return false; + } + + if (prev) + { + size_t len = strlen(abs_target) + 1; /* count \0 */ + this = malloc(sizeof(struct search_roots_ll) + len ); + if (!this || len > MAX_PATH) + { + logf("Error at adding a search root: %s", this ? "path too long":"OOM"); + free(this); + prev->next = NULL; + } + this->path = ((char*)this) + sizeof(struct search_roots_ll); + strcpy((char*)this->path, abs_target); /* ok to cast const away here */ + this->next = NULL; + prev->next = this; + logf("Added %s to the search roots\n", abs_target); + return true; + } +#endif + return false; +} + +static int free_search_roots(struct search_roots_ll * start) +{ + int ret = 0; + if (start->next) + { + ret += free_search_roots(start->next); + ret += sizeof(struct search_roots_ll); + free(start->next); + } + return ret; +} +#else /* native, simulator */ +#define add_search_root(a) do {} while(0) +#define free_search_roots(a) do {} while(0) +#endif static bool check_dir(const char *dirname, int add_files) { @@ -4203,7 +4289,6 @@ static bool check_dir(const char *dirname, int add_files) logf("tagcache: opendir(%s) failed", dirname); return false; } - /* check for a database.ignore and database.unignore */ check_ignore(dirname, &ignore, &unignore); @@ -4218,31 +4303,35 @@ static bool check_dir(const char *dirname, int add_files) while (!check_event_queue()) #endif { - struct dirent *entry; - - entry = readdir(dir); - + struct dirent *entry = readdir(dir); if (entry == NULL) { success = true; - break ; + break; } - struct dirinfo info = dir_get_info(dir, entry); - if (!strcmp((char *)entry->d_name, ".") || !strcmp((char *)entry->d_name, "..")) continue; + struct dirinfo info = dir_get_info(dir, entry); + yield(); len = strlen(curpath); - snprintf(&curpath[len], sizeof(curpath) - len, "/%s", - entry->d_name); - + /* don't add an extra / for curpath == / */ + if (len <= 1) len = 0; + snprintf(&curpath[len], sizeof(curpath) - len, "/%s", entry->d_name); + processed_dir_count++; if (info.attribute & ATTR_DIRECTORY) - check_dir(curpath, add_files); + { /* don't follow symlinks to dirs, but try to add it as a search root + * this makes able to avoid looping in recursive symlinks */ + if (info.attribute & ATTR_LINK) + add_search_root(curpath); + else + check_dir(curpath, add_files); + } else if (add_files) { tc_stat.curentry = curpath; @@ -4320,10 +4409,19 @@ void tagcache_build(const char *path) memset(&header, 0, sizeof(struct tagcache_header)); write(cachefd, &header, sizeof(struct tagcache_header)); - if (strcmp("/", path) != 0) - strcpy(curpath, path); - ret = check_dir(path, true); - + ret = true; + roots_ll.path = path; + roots_ll.next = NULL; + struct search_roots_ll * this; + /* check_dir might add new roots */ + for(this = &roots_ll; this; this = this->next) + { + strcpy(curpath, this->path); + ret = ret && check_dir(this->path, true); + } + if (roots_ll.next) + free_search_roots(roots_ll.next); + /* Write the header. */ header.magic = TAGCACHE_MAGIC; header.datasize = data_size; diff --git a/firmware/include/dir.h b/firmware/include/dir.h index 3a582c3865..4f1993143c 100644 --- a/firmware/include/dir.h +++ b/firmware/include/dir.h @@ -48,6 +48,7 @@ #define ATTR_DIRECTORY 0x10 #define ATTR_ARCHIVE 0x20 #define ATTR_VOLUME 0x40 /* this is a volume, not a real directory */ +#define ATTR_LINK 0x80 #ifdef HAVE_DIRCACHE # include "dircache.h" diff --git a/firmware/target/hosted/android/fs-android.c b/firmware/target/hosted/android/fs-android.c index 1967198d3d..c6d22a477e 100644 --- a/firmware/target/hosted/android/fs-android.c +++ b/firmware/target/hosted/android/fs-android.c @@ -105,26 +105,37 @@ struct dirinfo dir_get_info(struct DIR* _parent, struct dirent *dir) { struct __dir *parent = (struct __dir*)_parent; struct stat s; - struct tm *tm; + struct tm *tm = NULL; struct dirinfo ret; char path[MAX_PATH]; snprintf(path, sizeof(path), "%s/%s", parent->path, dir->d_name); - stat(path, &s); memset(&ret, 0, sizeof(ret)); - if (S_ISDIR(s.st_mode)) + if (!stat(path, &s)) { - ret.attribute = ATTR_DIRECTORY; + if (S_ISDIR(s.st_mode)) + { + ret.attribute = ATTR_DIRECTORY; + } + ret.size = s.st_size; + tm = localtime(&(s.st_mtime)); + } + + if (!lstat(path, &s) && S_ISLNK(s.st_mode)) + { + ret.attribute |= ATTR_LINK; + } + + if (tm) + { + ret.wrtdate = ((tm->tm_year - 80) << 9) | + ((tm->tm_mon + 1) << 5) | + tm->tm_mday; + ret.wrttime = (tm->tm_hour << 11) | + (tm->tm_min << 5) | + (tm->tm_sec >> 1); } - ret.size = s.st_size; - tm = localtime(&(s.st_mtime)); - ret.wrtdate = ((tm->tm_year - 80) << 9) | - ((tm->tm_mon + 1) << 5) | - tm->tm_mday; - ret.wrttime = (tm->tm_hour << 11) | - (tm->tm_min << 5) | - (tm->tm_sec >> 1); return ret; } diff --git a/uisimulator/common/io.c b/uisimulator/common/io.c index fe7bad438f..56abf4b6ad 100644 --- a/uisimulator/common/io.c +++ b/uisimulator/common/io.c @@ -28,6 +28,7 @@ #include "config.h" #define HAVE_STATVFS (!defined(WIN32)) +#define HAVE_LSTAT (!defined(WIN32)) #if HAVE_STATVFS #include @@ -314,7 +315,7 @@ struct sim_dirent *sim_readdir(MYDIR *dir) static struct sim_dirent secret; STAT_T s; DIRENT_T *x11 = READDIR(dir->dir); - struct tm* tm; + struct tm tm; if(!x11) return (struct sim_dirent *)0; @@ -324,20 +325,35 @@ struct sim_dirent *sim_readdir(MYDIR *dir) /* build file name */ snprintf(buffer, sizeof(buffer), "%s/%s", get_sim_pathname(dir->name), secret.d_name); - STAT(buffer, &s); /* get info */ + if (STAT(buffer, &s)) /* get info */ + return NULL; #define ATTR_DIRECTORY 0x10 - secret.info.attribute = S_ISDIR(s.st_mode)?ATTR_DIRECTORY:0; - secret.info.size = s.st_size; + secret.info.attribute = 0; + + if (S_ISDIR(s.st_mode)) + secret.info.attribute = ATTR_DIRECTORY; + + secret.info.size = s.st_size; + + if (localtime_r(&(s.st_mtime), &tm) == NULL) + return NULL; + secret.info.wrtdate = ((tm.tm_year - 80) << 9) | + ((tm.tm_mon + 1) << 5) | + tm.tm_mday; + secret.info.wrttime = (tm.tm_hour << 11) | + (tm.tm_min << 5) | + (tm.tm_sec >> 1); + +#ifdef HAVE_LSTAT +#define ATTR_LINK 0x80 + if (!lstat(buffer, &s) && S_ISLNK(s.st_mode)) + { + secret.info.attribute |= ATTR_LINK; + } +#endif - tm = localtime(&(s.st_mtime)); - secret.info.wrtdate = ((tm->tm_year - 80) << 9) | - ((tm->tm_mon + 1) << 5) | - tm->tm_mday; - secret.info.wrttime = (tm->tm_hour << 11) | - (tm->tm_min << 5) | - (tm->tm_sec >> 1); return &secret; }