Code

22737f7fc9de97103fd1a9c1f1975b61e5026465
[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     int is_dir;
45     char filename[];
46 };
48 struct cache_entry {
49     struct {
50         int valid;
51         uint32_t hash;
52         char *filename;
53         time_t dead;
54         struct stat st;
55     } stat_info;
57     struct {
58         int valid, filling, in_use, valid2;
59         uint32_t hash;
60         char *filename;
61         struct dir_entry *entries, *entries2;
62         time_t dead, dead2;
63     } dir_info;
64 };
66 static struct cache_entry *cache = NULL;
67 static pthread_mutex_t stat_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
68 static pthread_mutex_t dir_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
70 static uint32_t calc_hash(const char *s) {
71     uint32_t h = 0;
73     for (; *s; s++) {
74         h ^= * (const uint8_t*) s;
75         h = (h << 8) | (h  >> 24);
76     }
78     return h;
79 }
81 int stat_cache_get(const char *fn, struct stat *st) {
82     uint32_t h;
83     struct cache_entry *ce;
84     int r = -1;
85     void *f;
87     if (debug)
88         fprintf(stderr, "CGET: %s\n", fn);
89     
90     assert(cache);
91     
92     h = calc_hash(fn);
93     ce = cache + (h % CACHE_SIZE);
95     pthread_mutex_lock(&stat_cache_mutex);
96     
97     if (ce->stat_info.valid &&
98         ce->stat_info.filename &&
99         ce->stat_info.hash == h &&
100         !strcmp(ce->stat_info.filename, fn) &&
101         time(NULL) <= ce->stat_info.dead) {
102         
103         *st = ce->stat_info.st;
105         if ((f = file_cache_get(fn))) {
106             st->st_size = file_cache_get_size(f);
107             file_cache_unref(f);
108         }
110         r = 0;
111     }
113     pthread_mutex_unlock(&stat_cache_mutex);
114     
115     return r;
118 void stat_cache_set(const char *fn, const struct stat*st) {
119     uint32_t h;
120     struct cache_entry *ce;
122     if (debug)
123         fprintf(stderr, "CSET: %s\n", fn);
124     assert(cache);
125     
126     h = calc_hash(fn);
127     ce = cache + (h % CACHE_SIZE);
129     pthread_mutex_lock(&stat_cache_mutex);
131     if (!ce->stat_info.filename || ce->stat_info.hash != h || strcmp(ce->stat_info.filename, fn)) {
132         free(ce->stat_info.filename);
133         ce->stat_info.filename = strdup(fn);
134         ce->stat_info.hash = h;
135     }
136         
137     ce->stat_info.st = *st;
138     ce->stat_info.dead = time(NULL)+CACHE_TIMEOUT;
139     ce->stat_info.valid = 1;
141     pthread_mutex_unlock(&stat_cache_mutex);
144 void stat_cache_invalidate(const char*fn) {
145     uint32_t h;
146     struct cache_entry *ce;
148     assert(cache);
149     
150     h = calc_hash(fn);
151     ce = cache + (h % CACHE_SIZE);
153     pthread_mutex_lock(&stat_cache_mutex);
155     ce->stat_info.valid = 0;
156     free(ce->stat_info.filename);
157     ce->stat_info.filename = NULL;
158     
159     pthread_mutex_unlock(&stat_cache_mutex);
162 static void free_dir_entries(struct dir_entry *de) {
164     while (de) {
165         struct dir_entry *next = de->next;
166         free(de);
167         de = next;
168     }
172 void dir_cache_begin(const char *fn) {
173     uint32_t h;
174     struct cache_entry *ce;
175     struct dir_entry *de = NULL, *de2 = NULL;
176     assert(cache);
177     
178     h = calc_hash(fn);
179     ce = cache + (h % CACHE_SIZE);
180     
181     pthread_mutex_lock(&dir_cache_mutex);
183     if (!ce->dir_info.filling) {
184         
185         if (!ce->dir_info.filename || ce->dir_info.hash != h || strcmp(ce->dir_info.filename, fn)) {
186             free(ce->dir_info.filename);
187             ce->dir_info.filename = strdup(fn);
188             ce->dir_info.hash = h;
190             de = ce->dir_info.entries;
191             ce->dir_info.entries = NULL;
192             ce->dir_info.valid = 0;
193         }
195         de2 = ce->dir_info.entries2;
196         ce->dir_info.entries2 = NULL;
197         ce->dir_info.valid2 = 0;
198         ce->dir_info.filling = 1;
199     }
200     
201     pthread_mutex_unlock(&dir_cache_mutex);
202     free_dir_entries(de);
203     free_dir_entries(de2);
206 void dir_cache_finish(const char *fn, int success) {
207     uint32_t h;
208     struct cache_entry *ce;
209     struct dir_entry *de = NULL;
210     assert(cache);
211     
212     h = calc_hash(fn);
213     ce = cache + (h % CACHE_SIZE);
214     
215     pthread_mutex_lock(&dir_cache_mutex);
216     
217     if (ce->dir_info.filling &&
218         ce->dir_info.filename &&
219         ce->dir_info.hash == h &&
220         !strcmp(ce->dir_info.filename, fn)) {
222         assert(!ce->dir_info.valid2);
224         if (success) {
225             
226             ce->dir_info.valid2 = 1;
227             ce->dir_info.filling = 0;
228             ce->dir_info.dead2 = time(NULL)+CACHE_TIMEOUT;
229             
230             if (!ce->dir_info.in_use) {
231                 de = ce->dir_info.entries;
232                 ce->dir_info.entries = ce->dir_info.entries2;
233                 ce->dir_info.entries2 = NULL;
234                 ce->dir_info.dead = ce->dir_info.dead2;
235                 ce->dir_info.valid2 = 0;
236                 ce->dir_info.valid = 1;
237             }
238             
239         } else {
240             ce->dir_info.filling = 0;
241             de = ce->dir_info.entries2;
242             ce->dir_info.entries2 = NULL;
243         }
244     }
246     pthread_mutex_unlock(&dir_cache_mutex);
247     free_dir_entries(de);
250 void dir_cache_add(const char *fn, const char *subdir, int is_dir) {
251     uint32_t h;
252     struct cache_entry *ce;
253     assert(cache);
254     
255     h = calc_hash(fn);
256     ce = cache + (h % CACHE_SIZE);
257     
258     pthread_mutex_lock(&dir_cache_mutex);
259     
260     if (ce->dir_info.filling &&
261         ce->dir_info.filename &&
262         ce->dir_info.hash == h &&
263         !strcmp(ce->dir_info.filename, fn)) {
265         struct dir_entry *n;
267         assert(!ce->dir_info.valid2);
269         n = malloc(sizeof(struct dir_entry) + strlen(subdir) + 1);
270         assert(n);
272         strcpy(n->filename, subdir);
273         n->is_dir = is_dir;
274         
275         n->next = ce->dir_info.entries2;
276         ce->dir_info.entries2 = n;
277     }
279     pthread_mutex_unlock(&dir_cache_mutex);
282 int dir_cache_enumerate(const char *fn, void (*f) (const char*fn, const char *subdir, int is_dir, void *user), void *user) {
283     uint32_t h;
284     struct cache_entry *ce;
285     struct dir_entry *de = NULL;
286     int r = -1;
288     assert(cache && f);
289     
290     h = calc_hash(fn);
291     ce = cache + (h % CACHE_SIZE);
292     
293     pthread_mutex_lock(&dir_cache_mutex);
294     
295     if (ce->dir_info.valid &&
296         ce->dir_info.filename &&
297         ce->dir_info.hash == h &&
298         !strcmp(ce->dir_info.filename, fn) &&
299         time(NULL) <= ce->dir_info.dead) {
301         ce->dir_info.in_use = 1;
302         pthread_mutex_unlock(&dir_cache_mutex);
304         for (de = ce->dir_info.entries; de; de = de->next)
305             f(fn, de->filename, de->is_dir, user);
307         pthread_mutex_lock(&dir_cache_mutex);
308         ce->dir_info.in_use = 0;
310         if (ce->dir_info.valid2) {
311             de = ce->dir_info.entries;
312             ce->dir_info.entries = ce->dir_info.entries2;
313             ce->dir_info.entries2 = NULL;
314             ce->dir_info.dead = ce->dir_info.dead2;
315             ce->dir_info.valid2 = 0;
316             ce->dir_info.valid = 1;
317         }
319         r = 0;
320     }
321     
322     pthread_mutex_unlock(&dir_cache_mutex);
323     free_dir_entries(de);
325     return r;
326 }   
328 void dir_cache_invalidate(const char*fn) {
329     uint32_t h;
330     struct cache_entry *ce;
331     struct dir_entry *de = NULL;
332     assert(cache && fn);
333     
334     h = calc_hash(fn);
335     ce = cache + (h % CACHE_SIZE);
336     pthread_mutex_lock(&dir_cache_mutex);
337     
338     if (ce->dir_info.valid &&
339         ce->dir_info.filename &&
340         ce->dir_info.hash == h &&
341         !strcmp(ce->dir_info.filename, fn)) {
343         ce->dir_info.valid = 0;
344         de = ce->dir_info.entries;
345         ce->dir_info.entries = NULL;
346     }
347     
348     pthread_mutex_unlock(&dir_cache_mutex);
349     free_dir_entries(de);
352 void dir_cache_invalidate_parent(const char *fn) {
353     char *p;
355     if ((p = ne_path_parent(fn))) {
356         int l = strlen(p);
358         if (strcmp(p, "/") && l) {
359             if (p[l-1] == '/')
360                 p[l-1] = 0;
361         }
362         
363         dir_cache_invalidate(p);
364         free(p);
365     } else
366         dir_cache_invalidate(fn);
369 void cache_free(void) {
370     uint32_t h;
371     struct cache_entry *ce;
373     if (!cache)
374         return;
376     for (h = 0, ce = cache; h < CACHE_SIZE; h++, ce++) {
377         free(ce->stat_info.filename);
378         free(ce->dir_info.filename);
379         free_dir_entries(ce->dir_info.entries);
380         free_dir_entries(ce->dir_info.entries2);
381     }
383     memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);
386 void cache_alloc(void) {
387     
388     if (cache)
389         return;
391     cache = malloc(sizeof(struct cache_entry)*CACHE_SIZE);
392     assert(cache);
393     memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);