Code

Inital commit
[fusedav.git] / src / statcache.c
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);
63     
64     assert(cache);
65     
66     h = calc_hash(fn);
67     ce = cache + (h % CACHE_SIZE);
69     pthread_mutex_lock(&stat_cache_mutex);
70     
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) {
76         
77         *st = ce->stat_info.st;
78         r = 0;
79     }
81     pthread_mutex_unlock(&stat_cache_mutex);
82     
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);
93     
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     }
104         
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);
112 void stat_cache_invalidate(const char*fn) {
113     uint32_t h;
114     struct cache_entry *ce;
116     assert(cache);
117     
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;
126     
127     pthread_mutex_unlock(&stat_cache_mutex);
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     }
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);
145     
146     h = calc_hash(fn);
147     ce = cache + (h % CACHE_SIZE);
148     
149     pthread_mutex_lock(&dir_cache_mutex);
151     if (!ce->dir_info.filling) {
152         
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     }
168     
169     pthread_mutex_unlock(&dir_cache_mutex);
170     free_dir_entries(de);
171     free_dir_entries(de2);
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);
179     
180     h = calc_hash(fn);
181     ce = cache + (h % CACHE_SIZE);
182     
183     pthread_mutex_lock(&dir_cache_mutex);
184     
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) {
193             
194             ce->dir_info.valid2 = 1;
195             ce->dir_info.filling = 0;
196             ce->dir_info.dead2 = time(NULL)+CACHE_TIMEOUT;
197             
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             }
206             
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);
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);
222     
223     h = calc_hash(fn);
224     ce = cache + (h % CACHE_SIZE);
225     
226     pthread_mutex_lock(&dir_cache_mutex);
227     
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;
242         
243         n->next = ce->dir_info.entries2;
244         ce->dir_info.entries2 = n;
245     }
247     pthread_mutex_unlock(&dir_cache_mutex);
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;
256     
257     h = calc_hash(fn);
258     ce = cache + (h % CACHE_SIZE);
259     
260     pthread_mutex_lock(&dir_cache_mutex);
261     
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     }
288     
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);
300     
301     h = calc_hash(fn);
302     ce = cache + (h % CACHE_SIZE);
303     pthread_mutex_lock(&dir_cache_mutex);
304     
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     }
314     
315     pthread_mutex_unlock(&dir_cache_mutex);
316     free_dir_entries(de);
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         }
329         
330         dir_cache_invalidate(p);
331         free(p);
332     } else
333         dir_cache_invalidate(fn);
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);
353 void cache_alloc(void) {
354     
355     if (cache)
356         return;
358     cache = malloc(sizeof(struct cache_entry)*CACHE_SIZE);
359     memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);