From 87c8be4a08b4864f0df588c80bc90586f5e512a0 Mon Sep 17 00:00:00 2001 From: Thomas Martitz Date: Thu, 23 Dec 2010 19:02:18 +0000 Subject: [PATCH] RaaA: Improve tagcache search to make the database built. First, it add the ability to tagcache to walk through multiple search roots. Second, it adds symlinks targets to the search roots if they're are not inside any of the current search roots, otherwise the symlink is ignored (unless it's a file). The default search root is still /, so no search root will be actually added. But the tagcache now isn't trapped by recursive symlinks anymore and successfully builds, and it's prepared for a future music directory setting. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28884 a1c6a512-1295-4272-9138-f99709370657 --- apps/tagcache.c | 130 +++++++++++++++++--- firmware/include/dir.h | 1 + firmware/target/hosted/android/fs-android.c | 35 ++++-- uisimulator/common/io.c | 38 ++++-- 4 files changed, 165 insertions(+), 39 deletions(-) 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; }