1 #include <stdio.h>
2 #include <inttypes.h>
3 #include <time.h>
4 #include <string.h>
5 #include <malloc.h>
6 #include <pthread.h>
7 #include <assert.h>
9 #include "statcache.h"
10 #include "fusedav.h"
12 #include <ne_uri.h>
14 #define CACHE_SIZE 2049
15 #define CACHE_TIMEOUT 60
17 struct dir_entry {
18 struct dir_entry *next;
19 int is_dir;
20 char filename[];
21 };
23 struct cache_entry {
24 struct {
25 int valid;
26 uint32_t hash;
27 char *filename;
28 time_t dead;
29 struct stat st;
30 } stat_info;
32 struct {
33 int valid, filling, in_use, valid2;
34 uint32_t hash;
35 char *filename;
36 struct dir_entry *entries, *entries2;
37 time_t dead, dead2;
38 } dir_info;
39 };
41 static struct cache_entry *cache = NULL;
42 static pthread_mutex_t stat_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
43 static pthread_mutex_t dir_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
45 static uint32_t calc_hash(const char *s) {
46 uint32_t h = 0;
48 for (; *s; s++) {
49 h ^= * (uint8_t*) s;
50 h = (h << 8) | (h >> 24);
51 }
53 return h;
54 }
56 int stat_cache_get(const char *fn, struct stat *st) {
57 uint32_t h;
58 struct cache_entry *ce;
59 int r = -1;
61 if (debug)
62 fprintf(stderr, "CGET: %s\n", fn);
64 assert(cache);
66 h = calc_hash(fn);
67 ce = cache + (h % CACHE_SIZE);
69 pthread_mutex_lock(&stat_cache_mutex);
71 if (ce->stat_info.valid &&
72 ce->stat_info.filename &&
73 ce->stat_info.hash == h &&
74 !strcmp(ce->stat_info.filename, fn) &&
75 time(NULL) <= ce->stat_info.dead) {
77 *st = ce->stat_info.st;
78 r = 0;
79 }
81 pthread_mutex_unlock(&stat_cache_mutex);
83 return r;
84 }
86 void stat_cache_set(const char *fn, const struct stat*st) {
87 uint32_t h;
88 struct cache_entry *ce;
90 if (debug)
91 fprintf(stderr, "CSET: %s\n", fn);
92 assert(cache);
94 h = calc_hash(fn);
95 ce = cache + (h % CACHE_SIZE);
97 pthread_mutex_lock(&stat_cache_mutex);
99 if (!ce->stat_info.filename || ce->stat_info.hash != h || strcmp(ce->stat_info.filename, fn)) {
100 free(ce->stat_info.filename);
101 ce->stat_info.filename = strdup(fn);
102 ce->stat_info.hash = h;
103 }
105 ce->stat_info.st = *st;
106 ce->stat_info.dead = time(NULL)+CACHE_TIMEOUT;
107 ce->stat_info.valid = 1;
109 pthread_mutex_unlock(&stat_cache_mutex);
110 }
112 void stat_cache_invalidate(const char*fn) {
113 uint32_t h;
114 struct cache_entry *ce;
116 assert(cache);
118 h = calc_hash(fn);
119 ce = cache + (h % CACHE_SIZE);
121 pthread_mutex_lock(&stat_cache_mutex);
123 ce->stat_info.valid = 0;
124 free(ce->stat_info.filename);
125 ce->stat_info.filename = NULL;
127 pthread_mutex_unlock(&stat_cache_mutex);
128 }
130 static void free_dir_entries(struct dir_entry *de) {
132 while (de) {
133 struct dir_entry *next = de->next;
134 free(de);
135 de = next;
136 }
137 }
140 void dir_cache_begin(const char *fn) {
141 uint32_t h;
142 struct cache_entry *ce;
143 struct dir_entry *de = NULL, *de2 = NULL;
144 assert(cache);
146 h = calc_hash(fn);
147 ce = cache + (h % CACHE_SIZE);
149 pthread_mutex_lock(&dir_cache_mutex);
151 if (!ce->dir_info.filling) {
153 if (!ce->dir_info.filename || ce->dir_info.hash != h || strcmp(ce->dir_info.filename, fn)) {
154 free(ce->dir_info.filename);
155 ce->dir_info.filename = strdup(fn);
156 ce->dir_info.hash = h;
158 de = ce->dir_info.entries;
159 ce->dir_info.entries = NULL;
160 ce->dir_info.valid = 0;
161 }
163 de2 = ce->dir_info.entries2;
164 ce->dir_info.entries2 = NULL;
165 ce->dir_info.valid2 = 0;
166 ce->dir_info.filling = 1;
167 }
169 pthread_mutex_unlock(&dir_cache_mutex);
170 free_dir_entries(de);
171 free_dir_entries(de2);
172 }
174 void dir_cache_finish(const char *fn, int success) {
175 uint32_t h;
176 struct cache_entry *ce;
177 struct dir_entry *de = NULL;
178 assert(cache);
180 h = calc_hash(fn);
181 ce = cache + (h % CACHE_SIZE);
183 pthread_mutex_lock(&dir_cache_mutex);
185 if (ce->dir_info.filling &&
186 ce->dir_info.filename &&
187 ce->dir_info.hash == h &&
188 !strcmp(ce->dir_info.filename, fn)) {
190 assert(!ce->dir_info.valid2);
192 if (success) {
194 ce->dir_info.valid2 = 1;
195 ce->dir_info.filling = 0;
196 ce->dir_info.dead2 = time(NULL)+CACHE_TIMEOUT;
198 if (!ce->dir_info.in_use) {
199 de = ce->dir_info.entries;
200 ce->dir_info.entries = ce->dir_info.entries2;
201 ce->dir_info.entries2 = NULL;
202 ce->dir_info.dead = ce->dir_info.dead2;
203 ce->dir_info.valid2 = 0;
204 ce->dir_info.valid = 1;
205 }
207 } else {
208 ce->dir_info.filling = 0;
209 de = ce->dir_info.entries2;
210 ce->dir_info.entries2 = NULL;
211 }
212 }
214 pthread_mutex_unlock(&dir_cache_mutex);
215 free_dir_entries(de);
216 }
218 void dir_cache_add(const char *fn, const char *subdir, int is_dir) {
219 uint32_t h;
220 struct cache_entry *ce;
221 assert(cache);
223 h = calc_hash(fn);
224 ce = cache + (h % CACHE_SIZE);
226 pthread_mutex_lock(&dir_cache_mutex);
228 if (ce->dir_info.filling &&
229 ce->dir_info.filename &&
230 ce->dir_info.hash == h &&
231 !strcmp(ce->dir_info.filename, fn)) {
233 struct dir_entry *n;
235 assert(!ce->dir_info.valid2);
237 n = malloc(sizeof(struct dir_entry) + strlen(subdir) + 1);
238 assert(n);
240 strcpy(n->filename, subdir);
241 n->is_dir = is_dir;
243 n->next = ce->dir_info.entries2;
244 ce->dir_info.entries2 = n;
245 }
247 pthread_mutex_unlock(&dir_cache_mutex);
248 }
250 int dir_cache_enumerate(const char *fn, void (*f) (const char*fn, const char *subdir, int is_dir, void *user), void *user) {
251 uint32_t h;
252 struct cache_entry *ce;
253 struct dir_entry *de = NULL;
254 assert(cache && f);
255 int r = -1;
257 h = calc_hash(fn);
258 ce = cache + (h % CACHE_SIZE);
260 pthread_mutex_lock(&dir_cache_mutex);
262 if (ce->dir_info.valid &&
263 ce->dir_info.filename &&
264 ce->dir_info.hash == h &&
265 !strcmp(ce->dir_info.filename, fn) &&
266 time(NULL) <= ce->dir_info.dead) {
268 ce->dir_info.in_use = 1;
269 pthread_mutex_unlock(&dir_cache_mutex);
271 for (de = ce->dir_info.entries; de; de = de->next)
272 f(fn, de->filename, de->is_dir, user);
274 pthread_mutex_lock(&dir_cache_mutex);
275 ce->dir_info.in_use = 0;
277 if (ce->dir_info.valid2) {
278 de = ce->dir_info.entries;
279 ce->dir_info.entries = ce->dir_info.entries2;
280 ce->dir_info.entries2 = NULL;
281 ce->dir_info.dead = ce->dir_info.dead2;
282 ce->dir_info.valid2 = 0;
283 ce->dir_info.valid = 1;
284 }
286 r = 0;
287 }
289 pthread_mutex_unlock(&dir_cache_mutex);
290 free_dir_entries(de);
292 return r;
293 }
295 void dir_cache_invalidate(const char*fn) {
296 uint32_t h;
297 struct cache_entry *ce;
298 struct dir_entry *de = NULL;
299 assert(cache && fn);
301 h = calc_hash(fn);
302 ce = cache + (h % CACHE_SIZE);
303 pthread_mutex_lock(&dir_cache_mutex);
305 if (ce->dir_info.valid &&
306 ce->dir_info.filename &&
307 ce->dir_info.hash == h &&
308 !strcmp(ce->dir_info.filename, fn)) {
310 ce->dir_info.valid = 0;
311 de = ce->dir_info.entries;
312 ce->dir_info.entries = NULL;
313 }
315 pthread_mutex_unlock(&dir_cache_mutex);
316 free_dir_entries(de);
317 }
319 void dir_cache_invalidate_parent(const char *fn) {
320 char *p;
322 if ((p = ne_path_parent(fn))) {
323 int l = strlen(p);
325 if (strcmp(p, "/") && l) {
326 if (p[l-1] == '/')
327 p[l-1] = 0;
328 }
330 dir_cache_invalidate(p);
331 free(p);
332 } else
333 dir_cache_invalidate(fn);
334 }
336 void cache_free(void) {
337 uint32_t h;
338 struct cache_entry *ce;
340 if (!cache)
341 return;
343 for (h = 0, ce = cache; h < CACHE_SIZE; h++, ce++) {
344 free(ce->stat_info.filename);
345 free(ce->dir_info.filename);
346 free_dir_entries(ce->dir_info.entries);
347 free_dir_entries(ce->dir_info.entries2);
348 }
350 memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);
351 }
353 void cache_alloc(void) {
355 if (cache)
356 return;
358 cache = malloc(sizeof(struct cache_entry)*CACHE_SIZE);
359 memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);
360 }