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;
}