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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index fad559f..a06ac7a 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -130,6 +130,23 @@ static inline struct dircache_entry* get_entry(int id)
     return &dircache_root[id];
 }
 
+/* flag to make sure buffer doesn't move due to other allocs.
+ * this is set to true completely during dircache build */
+static volatile bool dont_move = false;
+static int dircache_handle;
+static int move_callback(int handle, void* current, void* new)
+{
+    /* TODO */
+    (void)handle;(void)current;
+    dircache_root = new;
+    return dont_move ? BUFLIB_CB_CANNOT_MOVE : BUFLIB_CB_OK;
+}
+
+static struct buflib_callbacks ops = {
+    .move_callback = move_callback,
+    .shrink_callback = NULL,
+};
+
 #ifdef HAVE_EEPROM_SETTINGS
 /**
  * Open the dircache file to save a snapshot on disk
@@ -573,7 +590,10 @@ int dircache_load(void)
     }
     
     allocated_size = maindata.size + DIRCACHE_RESERVE;
-    dircache_root = buffer_alloc(allocated_size);
+    dircache_handle = core_alloc_ex("dircache", allocated_size, &ops);
+    /* block movement during upcoming I/O */
+    dont_move = true;
+    dircache_root = core_get_data(dircache_handle);
     /* needs to be struct-size aligned so that the pointer arithmetic below works */
     ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry));
     entry_count = maindata.entry_count;
@@ -639,6 +659,7 @@ int dircache_load(void)
     logf("Done, %ld KiB used", dircache_size / 1024);
     dircache_initialized = true;
     memset(fd_bindings, 0, sizeof(fd_bindings));
+    dont_move = false;
 
     return 0;
 }
@@ -659,6 +680,7 @@ int dircache_save(void)
         return -1;
 
     logf("Saving directory cache");
+    dont_move = true;
     fd = open_dircache_file(O_WRONLY | O_CREAT | O_TRUNC, 0666);
 
     maindata.magic = DIRCACHE_MAGIC;
@@ -697,7 +719,7 @@ int dircache_save(void)
         return -4;
     }
 
-    
+    dont_move = false;
     return 0;
 }
 #endif /* HAVE_EEPROM_SETTINGS */
@@ -719,6 +741,7 @@ static int dircache_do_rebuild(void)
     /* reset dircache and alloc root entry */
     entry_count = 0;
     root_entry = allocate_entry();
+    dont_move = true;
 
 #ifdef HAVE_MULTIVOLUME
     append_position = root_entry;
@@ -739,6 +762,7 @@ static int dircache_do_rebuild(void)
                 cpu_boost(false);
                 dircache_size = 0;
                 dircache_initializing = false;
+                dont_move = false;
                 return -2;
             }
             cpu_boost(false);
@@ -764,7 +788,8 @@ static int dircache_do_rebuild(void)
         if (allocated_size - dircache_size < DIRCACHE_RESERVE)
             reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size);
     }
-    
+
+    dont_move = false;
     return 1;
 }
 
@@ -789,7 +814,8 @@ static void dircache_thread(void)
 #endif
             case DIRCACHE_BUILD:
                 thread_enabled = true;
-                dircache_do_rebuild();
+                if (dircache_do_rebuild() < 0)
+                    core_free(dircache_handle);
                 thread_enabled = false;
                 break ;
                 
@@ -817,18 +843,6 @@ static void generate_dot_d_names(void)
     strcpy(dotdot, "..");
 }
 
-static int move_callback(int handle, void* current, void* new)
-{
-    /* TODO */
-    (void)handle;(void)current;(void)new;
-    return BUFLIB_CB_OK;
-}
-
-static struct buflib_callbacks ops = {
-    .move_callback = move_callback,
-    .shrink_callback = NULL,
-};
-
 /**
  * Start scanning the disk to build the dircache.
  * Either transparent or non-transparent build method is used.
@@ -859,10 +873,9 @@ int dircache_build(int last_size)
 
     if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT )
     {
-        int handle;
         allocated_size = last_size + DIRCACHE_RESERVE;
-        handle = core_alloc_ex("dircache", allocated_size, &ops);
-        dircache_root = core_get_data(handle);
+        dircache_handle = core_alloc_ex("dircache", allocated_size, &ops);
+        dircache_root = core_get_data(dircache_handle);
         ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry));
         d_names_start = d_names_end = ((char*)dircache_root)+allocated_size-1;
         dircache_size = 0;
@@ -880,8 +893,8 @@ int dircache_build(int last_size)
      * after generation the buffer will be compacted with DIRCACHE_RESERVE
      * free bytes inbetween */
     size_t got_size;
-    int handle = core_alloc_maximum("dircache", &got_size, &ops);
-    dircache_root = core_get_data(handle);
+    dircache_handle = core_alloc_maximum("dircache", &got_size, &ops);
+    dircache_root = core_get_data(dircache_handle);
     ALIGN_BUFFER(dircache_root, got_size, sizeof(struct dircache_entry));
     d_names_start = d_names_end = (char*)dircache_root + got_size - 1;
     dircache_size = 0;
@@ -919,11 +932,11 @@ int dircache_build(int last_size)
     allocated_size = (d_names_end - (char*)dircache_root);
     reserve_used = 0;
 
-    core_shrink(handle, dircache_root, allocated_size);
+    core_shrink(dircache_handle, dircache_root, allocated_size);
     return res;
 fail:
     dircache_disable();
-    core_free(handle);
+    core_free(dircache_handle);
     return res;
 }
 
@@ -938,7 +951,9 @@ void* dircache_steal_buffer(size_t *size)
         *size = 0;
         return NULL;
     }
-    
+
+    /* since we give up the buffer (without freeing), it must not move anymore */
+    dont_move = true;
     *size = dircache_size + (DIRCACHE_RESERVE-reserve_used);
     
     return dircache_root;