1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
diff --git a/apps/tagcache.c b/apps/tagcache.c
index 679d7cb..81e574a 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,6 +82,7 @@
 #include "settings.h"
 #include "dir.h"
 #include "structec.h"
+#include "debug.h"

 #ifndef __PCTOOL__
 #include "lang.h"
@@ -4232,6 +4238,47 @@ 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;
+    char target[MAX_PATH];
+    ssize_t len;
+    len = readlink(name, target, sizeof(target));
+    if (len < 0)
+        return false;
+    target[len] = '\0';
+
+    for(this = &roots_ll; this && this->next; this = this->next)
+    {
+        /* 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 (strstr(this->path, target))
+            return false;
+    }
+
+    this->next = malloc(sizeof(struct search_roots_ll));
+    this = this->next;
+    this->path = strdup(target);
+    this->next = NULL;
+    return true;
+}
+
+static void free_search_roots(struct search_roots_ll * start)
+{
+    if (start->next)
+    {
+        free_search_roots(start->next);
+        free(start->next);
+    }
+    free((void*)start->path);
+}
+#endif

 static bool check_dir(const char *dirname, int add_files)
 {
@@ -4246,7 +4293,7 @@ static bool check_dir(const char *dirname, int add_files)
         logf("tagcache: opendir(%s) failed", dirname);
         return false;
     }
-    
+    printf("scanning %s\n", dirname);
     /* check for a database.ignore and database.unignore */
     check_ignore(dirname, &ignore, &unignore);

@@ -4262,7 +4309,24 @@ static bool check_dir(const char *dirname, int add_files)
 #endif
     {
         struct dirent *entry;
+#ifdef APPLICATION
+        /* save some stack, it's safe because we don't use it after recursive calls */
+        static struct stat st;
+        if (lstat(dirname, &st))
+        {
+            success = false;
+            break;
+        }

+        if (S_ISLNK(st.st_mode))
+        {   /* don't follow symlinks.
+             * we already went through the target or we will do later so
+             * pretent success */
+            add_search_root(dirname);
+            success = true;
+            break;
+        }
+#endif
         entry = readdir(dir);

         if (entry == NULL)
@@ -4366,11 +4430,22 @@ void tagcache_build(const char *path)
     /* Scan for new files. */
     memset(&header, 0, sizeof(struct tagcache_header));
     write(cachefd, &header, sizeof(struct tagcache_header));
+    path = "/home/kugel/rbdev/rockbox-git/build-sdlapp";

-    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)
+        free_search_roots(roots_ll.next);
+#endif
     /* Write the header. */
     header.magic = TAGCACHE_MAGIC;
     header.datasize = data_size;