diff --git a/apps/metadata/mod.c b/apps/metadata/mod.c
index dbedc6e..d7c5d87 100644
--- a/apps/metadata/mod.c
+++ b/apps/metadata/mod.c
@@ -19,47 +19,53 @@
  *
  ****************************************************************************/
 #include <stdio.h>
-#include <string.h>
 #include <stdlib.h>
 #include <ctype.h>
 #include <inttypes.h>

 #include "system.h"
+#include "string-extra.h"
 #include "metadata.h"
 #include "metadata_common.h"
 #include "metadata_parsers.h"
 #include "rbunicode.h"

+#define MAGIC_OFFSET (0x438)
 bool get_mod_metadata(int fd, struct mp3entry* id3)
-{    
+{
     /* Use the trackname part of the id3 structure as a temporary buffer */
-    unsigned char buf[1084];
+    unsigned char buf[0x34C];
     int read_bytes;
     char *p;


     if ((lseek(fd, 0, SEEK_SET) < 0) 
-         || ((read_bytes = read(fd, buf, sizeof(buf))) < 1084))
+         || ((read_bytes = read(fd, buf, sizeof(buf))) < (ssize_t)sizeof(buf)))
+    {
+        return false;
+    }
+
+    /* is it a .mod file ?
+     * if we don't find the magic, we need some \0 bytes or
+     * the code below will fail */
+    if (memcmp(&buf[MAGIC_OFFSET], "M.K.", 4) &&
+        memcmp(&buf[MAGIC_OFFSET], "M!K!", 4) &&
+        memchr(buf, '\0', sizeof(buf)) == NULL)
     {
         return false;
     }
-    
-    /* We don't do file format checking here
-     * There can be .mod files without any signatures out there */

     p = id3->id3v2buf;
-    
+
     /* Copy Title */
-    strcpy(p, &buf[0x00]);
+    strlcpy(p, &buf[0x00], sizeof(id3->id3v2buf));
     id3->title = p;
-    p += strlen(p)+1;

     id3->bitrate = filesize(fd)/1024; /* size in kb */
     id3->frequency = 44100;
     id3->length = 120*1000;
     id3->vbr = false;
     id3->filesize = filesize(fd);
-        
     return true;
 }

diff --git a/apps/tagcache.c b/apps/tagcache.c
index 3565d8e..4588dbb 100644
--- a/apps/tagcache.c
+++ b/apps/tagcache.c
@@ -60,6 +60,11 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
+#ifdef APPLICATION
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h> /* lstat() */
+#endif
 #include "config.h"
 #include "ata_idle_notify.h"
 #include "thread.h"
@@ -77,7 +82,9 @@
 #include "dir.h"
 #include "filefuncs.h"
 #include "structec.h"
+#include "debug.h"

+#define logf(...) do { debugf(__VA_ARGS__); debugf("\n"); } while(0)
 #ifndef __PCTOOL__
 #include "lang.h"
 #include "eeprom_settings.h"
@@ -4189,6 +4196,71 @@ 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
+static bool add_search_root(const char *name)
+{
+    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;
+
+    if (!strcmp("/", abs_target))
+        printf("link to /: %s (%s)\n", name, target);
+    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
+         *
+         * don't bother if / is in the search roots */
+        printf("Checking %s against %s\n", abs_target, this->path);
+        if (!strncmp(this->path, abs_target, root_len))
+            goto fail;
+    }
+
+    if (prev)
+    {
+        prev->next = malloc(sizeof(struct search_roots_ll));
+        this = prev->next;
+        this->path = strdup(abs_target);
+        printf("Added: %s\n", abs_target);
+        this->next = NULL;
+        return true;
+    }
+fail:
+    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);
+    }
+    ret += strlen(start->path)+1;
+    free((void*)start->path);
+    return ret;
+}
+#endif

 static bool check_dir(const char *dirname, int add_files)
 {
@@ -4203,7 +4275,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 +4289,44 @@ 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);
-        
+        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);
+        {
+#ifdef APPLICATION
+            if (info.attribute & ATTR_LINK)
+            {   /* don't follow symlinks to dirs */
+                if (!strcmp("/", curpath))
+                {
+                    printf("grr %s;%s (entry: %s)\n", curpath, dirname, entry->d_name);
+                }
+                add_search_root(curpath);
+            }
+            else
+#endif
+                check_dir(curpath, add_files);
+        }
         else if (add_files)
         {
             tc_stat.curentry = curpath;
@@ -4320,10 +4404,20 @@ 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);
+    }
+#ifdef APPLICATION
+    if (roots_ll.next)
+        printf("freed %d bytes\n", free_search_roots(roots_ll.next));
+#endif
     /* Write the header. */
     header.magic = TAGCACHE_MAGIC;
     header.datasize = data_size;
diff --git a/firmware/export/system.h b/firmware/export/system.h
index 4a5dcf0..d7f5c95 100644
--- a/firmware/export/system.h
+++ b/firmware/export/system.h
@@ -22,6 +22,7 @@
 #ifndef __SYSTEM_H__
 #define __SYSTEM_H__

+#include "config.h"
 #include "cpu.h"
 #include "stdbool.h"
 #include "kernel.h"
@@ -237,6 +238,7 @@ enum {
 #endif

 #ifdef NEED_GENERIC_BYTESWAPS
+#undef swap16
 static inline uint16_t swap16(uint16_t value)
     /*
       result[15..8] = value[ 7..0];
@@ -245,7 +247,7 @@ static inline uint16_t swap16(uint16_t value)
 {
     return (value >> 8) | (value << 8);
 }
-
+#undef swap32
 static inline uint32_t swap32(uint32_t value)
     /*
       result[31..24] = value[ 7.. 0];
diff --git a/firmware/include/dir.h b/firmware/include/dir.h
index 3a582c3..4f19931 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 1967198..c6d22a4 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/firmware/target/hosted/sdl/button-sdl.c b/firmware/target/hosted/sdl/button-sdl.c
index 3321a01..41fc281 100644
--- a/firmware/target/hosted/sdl/button-sdl.c
+++ b/firmware/target/hosted/sdl/button-sdl.c
@@ -250,7 +250,10 @@ void gui_message_loop(void)
     do {
         /* wait for the next event */
         while(SDL_WaitEvent(&event) == 0)
+        {
             printf("SDL_WaitEvent() error\n");
+            exit(EXIT_FAILURE);
+        }

         sim_enter_irq_handler();
         quit = event_handler(&event);
diff --git a/uisimulator/common/io.c b/uisimulator/common/io.c
index fe7bad4..56abf4b 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 <sys/statvfs.h>
@@ -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.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;
 }