Code

* make lock_timeout configurable
[fusedav.git] / src / statcache.c
1 /* $Id$ */
3 /***
4   This file is part of fusedav.
6   fusedav is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10   
11   fusedav is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
14   License for more details.
15   
16   You should have received a copy of the GNU General Public License
17   along with fusedav; if not, write to the Free Software Foundation,
18   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 ***/
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <stdio.h>
26 #include <inttypes.h>
27 #include <time.h>
28 #include <string.h>
29 #include <malloc.h>
30 #include <pthread.h>
31 #include <assert.h>
33 #include "statcache.h"
34 #include "filecache.h"
35 #include "fusedav.h"
37 #include <ne_uri.h>
39 #define CACHE_SIZE 2049
40 #define CACHE_TIMEOUT 60
42 struct dir_entry {
43     struct dir_entry *next;
44     char filename[];
45 };
47 struct cache_entry {
48     struct {
49         int valid;
50         uint32_t hash;
51         char *filename;
52         time_t dead;
53         struct stat st;
54     } stat_info;
56     struct {
57         int valid, filling, in_use, valid2;
58         uint32_t hash;
59         char *filename;
60         struct dir_entry *entries, *entries2;
61         time_t dead, dead2;
62     } dir_info;
63 };
65 static struct cache_entry *cache = NULL;
66 static pthread_mutex_t stat_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
67 static pthread_mutex_t dir_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
69 static uint32_t calc_hash(const char *s) {
70     uint32_t h = 0;
72     for (; *s; s++) {
73         h ^= * (const uint8_t*) s;
74         h = (h << 8) | (h  >> 24);
75     }
77     return h;
78 }
80 int stat_cache_get(const char *fn, struct stat *st) {
81     uint32_t h;
82     struct cache_entry *ce;
83     int r = -1;
84     void *f;
86     if (debug)
87         fprintf(stderr, "CGET: %s\n", fn);
88     
89     assert(cache);
90     
91     h = calc_hash(fn);
92     ce = cache + (h % CACHE_SIZE);
94     pthread_mutex_lock(&stat_cache_mutex);
95     
96     if (ce->stat_info.valid &&
97         ce->stat_info.filename &&
98         ce->stat_info.hash == h &&
99         !strcmp(ce->stat_info.filename, fn) &&
100         time(NULL) <= ce->stat_info.dead) {
101         
102         *st = ce->stat_info.st;
104         if ((f = file_cache_get(fn))) {
105             st->st_size = file_cache_get_size(f);
106             file_cache_unref(f);
107         }
109         r = 0;
110     }
112     pthread_mutex_unlock(&stat_cache_mutex);
113     
114     return r;
117 void stat_cache_set(const char *fn, const struct stat*st) {
118     uint32_t h;
119     struct cache_entry *ce;
121     if (debug)
122         fprintf(stderr, "CSET: %s\n", fn);
123     assert(cache);
124     
125     h = calc_hash(fn);
126     ce = cache + (h % CACHE_SIZE);
128     pthread_mutex_lock(&stat_cache_mutex);
130     if (!ce->stat_info.filename || ce->stat_info.hash != h || strcmp(ce->stat_info.filename, fn)) {
131         free(ce->stat_info.filename);
132         ce->stat_info.filename = strdup(fn);
133         ce->stat_info.hash = h;
134     }
135         
136     ce->stat_info.st = *st;
137     ce->stat_info.dead = time(NULL)+CACHE_TIMEOUT;
138     ce->stat_info.valid = 1;
140     pthread_mutex_unlock(&stat_cache_mutex);
143 void stat_cache_invalidate(const char*fn) {
144     uint32_t h;
145     struct cache_entry *ce;
147     assert(cache);
148     
149     h = calc_hash(fn);
150     ce = cache + (h % CACHE_SIZE);
152     pthread_mutex_lock(&stat_cache_mutex);
154     ce->stat_info.valid = 0;
155     free(ce->stat_info.filename);
156     ce->stat_info.filename = NULL;
157     
158     pthread_mutex_unlock(&stat_cache_mutex);
161 static void free_dir_entries(struct dir_entry *de) {
163     while (de) {
164         struct dir_entry *next = de->next;
165         free(de);
166         de = next;
167     }
171 void dir_cache_begin(const char *fn) {
172     uint32_t h;
173     struct cache_entry *ce;
174     struct dir_entry *de = NULL, *de2 = NULL;
175     assert(cache);
176     
177     h = calc_hash(fn);
178     ce = cache + (h % CACHE_SIZE);
179     
180     pthread_mutex_lock(&dir_cache_mutex);
182     if (!ce->dir_info.filling) {
183         
184         if (!ce->dir_info.filename || ce->dir_info.hash != h || strcmp(ce->dir_info.filename, fn)) {
185             free(ce->dir_info.filename);
186             ce->dir_info.filename = strdup(fn);
187             ce->dir_info.hash = h;
189             de = ce->dir_info.entries;
190             ce->dir_info.entries = NULL;
191             ce->dir_info.valid = 0;
192         }
194         de2 = ce->dir_info.entries2;
195         ce->dir_info.entries2 = NULL;
196         ce->dir_info.valid2 = 0;
197         ce->dir_info.filling = 1;
198     }
199     
200     pthread_mutex_unlock(&dir_cache_mutex);
201     free_dir_entries(de);
202     free_dir_entries(de2);
205 void dir_cache_finish(const char *fn, int success) {
206     uint32_t h;
207     struct cache_entry *ce;
208     struct dir_entry *de = NULL;
209     assert(cache);
210     
211     h = calc_hash(fn);
212     ce = cache + (h % CACHE_SIZE);
213     
214     pthread_mutex_lock(&dir_cache_mutex);
215     
216     if (ce->dir_info.filling &&
217         ce->dir_info.filename &&
218         ce->dir_info.hash == h &&
219         !strcmp(ce->dir_info.filename, fn)) {
221         assert(!ce->dir_info.valid2);
223         if (success) {
224             
225             ce->dir_info.valid2 = 1;
226             ce->dir_info.filling = 0;
227             ce->dir_info.dead2 = time(NULL)+CACHE_TIMEOUT;
228             
229             if (!ce->dir_info.in_use) {
230                 de = ce->dir_info.entries;
231                 ce->dir_info.entries = ce->dir_info.entries2;
232                 ce->dir_info.entries2 = NULL;
233                 ce->dir_info.dead = ce->dir_info.dead2;
234                 ce->dir_info.valid2 = 0;
235                 ce->dir_info.valid = 1;
236             }
237             
238         } else {
239             ce->dir_info.filling = 0;
240             de = ce->dir_info.entries2;
241             ce->dir_info.entries2 = NULL;
242         }
243     }
245     pthread_mutex_unlock(&dir_cache_mutex);
246     free_dir_entries(de);
249 void dir_cache_add(const char *fn, const char *subdir) {
250     uint32_t h;
251     struct cache_entry *ce;
252     assert(cache);
253     
254     h = calc_hash(fn);
255     ce = cache + (h % CACHE_SIZE);
256     
257     pthread_mutex_lock(&dir_cache_mutex);
258     
259     if (ce->dir_info.filling &&
260         ce->dir_info.filename &&
261         ce->dir_info.hash == h &&
262         !strcmp(ce->dir_info.filename, fn)) {
264         struct dir_entry *n;
266         assert(!ce->dir_info.valid2);
268         n = malloc(sizeof(struct dir_entry) + strlen(subdir) + 1);
269         assert(n);
271         strcpy(n->filename, subdir);
272         
273         n->next = ce->dir_info.entries2;
274         ce->dir_info.entries2 = n;
275     }
277     pthread_mutex_unlock(&dir_cache_mutex);
280 int dir_cache_enumerate(const char *fn, void (*f) (const char*fn, const char *subdir, void *user), void *user) {
281     uint32_t h;
282     struct cache_entry *ce;
283     struct dir_entry *de = NULL;
284     int r = -1;
286     assert(cache && f);
287     
288     h = calc_hash(fn);
289     ce = cache + (h % CACHE_SIZE);
290     
291     pthread_mutex_lock(&dir_cache_mutex);
292     
293     if (ce->dir_info.valid &&
294         ce->dir_info.filename &&
295         ce->dir_info.hash == h &&
296         !strcmp(ce->dir_info.filename, fn) &&
297         time(NULL) <= ce->dir_info.dead) {
299         ce->dir_info.in_use = 1;
300         pthread_mutex_unlock(&dir_cache_mutex);
302         for (de = ce->dir_info.entries; de; de = de->next)
303             f(fn, de->filename, user);
305         pthread_mutex_lock(&dir_cache_mutex);
306         ce->dir_info.in_use = 0;
308         if (ce->dir_info.valid2) {
309             de = ce->dir_info.entries;
310             ce->dir_info.entries = ce->dir_info.entries2;
311             ce->dir_info.entries2 = NULL;
312             ce->dir_info.dead = ce->dir_info.dead2;
313             ce->dir_info.valid2 = 0;
314             ce->dir_info.valid = 1;
315         }
317         r = 0;
318     }
319     
320     pthread_mutex_unlock(&dir_cache_mutex);
321     free_dir_entries(de);
323     return r;
324 }   
326 void dir_cache_invalidate(const char*fn) {
327     uint32_t h;
328     struct cache_entry *ce;
329     struct dir_entry *de = NULL;
330     assert(cache && fn);
331     
332     h = calc_hash(fn);
333     ce = cache + (h % CACHE_SIZE);
334     pthread_mutex_lock(&dir_cache_mutex);
335     
336     if (ce->dir_info.valid &&
337         ce->dir_info.filename &&
338         ce->dir_info.hash == h &&
339         !strcmp(ce->dir_info.filename, fn)) {
341         ce->dir_info.valid = 0;
342         de = ce->dir_info.entries;
343         ce->dir_info.entries = NULL;
344     }
345     
346     pthread_mutex_unlock(&dir_cache_mutex);
347     free_dir_entries(de);
350 void dir_cache_invalidate_parent(const char *fn) {
351     char *p;
353     if ((p = ne_path_parent(fn))) {
354         int l = strlen(p);
356         if (strcmp(p, "/") && l) {
357             if (p[l-1] == '/')
358                 p[l-1] = 0;
359         }
360         
361         dir_cache_invalidate(p);
362         free(p);
363     } else
364         dir_cache_invalidate(fn);
367 void cache_free(void) {
368     uint32_t h;
369     struct cache_entry *ce;
371     if (!cache)
372         return;
374     for (h = 0, ce = cache; h < CACHE_SIZE; h++, ce++) {
375         free(ce->stat_info.filename);
376         free(ce->dir_info.filename);
377         free_dir_entries(ce->dir_info.entries);
378         free_dir_entries(ce->dir_info.entries2);
379     }
381     memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);
384 void cache_alloc(void) {
385     
386     if (cache)
387         return;
389     cache = malloc(sizeof(struct cache_entry)*CACHE_SIZE);
390     assert(cache);
391     memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);