Code

git: remove $Id$ svn cruft
[fusedav.git] / src / statcache.c
1 /***
2   Copyright (c) 2004-2006 Lennart Poettering
4   Permission is hereby granted, free of charge, to any person
5   obtaining a copy of this software and associated documentation files
6   (the "Software"), to deal in the Software without restriction,
7   including without limitation the rights to use, copy, modify, merge,
8   publish, distribute, sublicense, and/or sell copies of the Software,
9   and to permit persons to whom the Software is furnished to do so,
10   subject to the following conditions:
12   The above copyright notice and this permission notice shall be
13   included in all copies or substantial portions of the Software.
15   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22   SOFTWARE.
23 ***/
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
29 #include <stdio.h>
30 #include <inttypes.h>
31 #include <time.h>
32 #include <string.h>
33 #include <malloc.h>
34 #include <pthread.h>
35 #include <assert.h>
37 #include "statcache.h"
38 #include "filecache.h"
39 #include "fusedav.h"
41 #include <ne_uri.h>
43 #define CACHE_SIZE 2049
44 #define CACHE_TIMEOUT 60
46 struct dir_entry {
47     struct dir_entry *next;
48     char filename[];
49 };
51 struct cache_entry {
52     struct {
53         int valid;
54         uint32_t hash;
55         char *filename;
56         time_t dead;
57         struct stat st;
58     } stat_info;
60     struct {
61         int valid, filling, in_use, valid2;
62         uint32_t hash;
63         char *filename;
64         struct dir_entry *entries, *entries2;
65         time_t dead, dead2;
66     } dir_info;
67 };
69 static struct cache_entry *cache = NULL;
70 static pthread_mutex_t stat_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
71 static pthread_mutex_t dir_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
73 static uint32_t calc_hash(const char *s) {
74     uint32_t h = 0;
76     for (; *s; s++) {
77         h ^= * (const uint8_t*) s;
78         h = (h << 8) | (h  >> 24);
79     }
81     return h;
82 }
84 int stat_cache_get(const char *fn, struct stat *st) {
85     uint32_t h;
86     struct cache_entry *ce;
87     int r = -1;
88     void *f;
90     if (debug)
91         fprintf(stderr, "CGET: %s\n", fn);
93     assert(cache);
95     h = calc_hash(fn);
96     ce = cache + (h % CACHE_SIZE);
98     pthread_mutex_lock(&stat_cache_mutex);
100     if (ce->stat_info.valid &&
101         ce->stat_info.filename &&
102         ce->stat_info.hash == h &&
103         !strcmp(ce->stat_info.filename, fn) &&
104         time(NULL) <= ce->stat_info.dead) {
106         *st = ce->stat_info.st;
108         if ((f = file_cache_get(fn))) {
109             st->st_size = file_cache_get_size(f);
110             file_cache_unref(f);
111         }
113         r = 0;
114     }
116     pthread_mutex_unlock(&stat_cache_mutex);
118     return r;
121 void stat_cache_set(const char *fn, const struct stat*st) {
122     uint32_t h;
123     struct cache_entry *ce;
125     if (debug)
126         fprintf(stderr, "CSET: %s\n", fn);
127     assert(cache);
129     h = calc_hash(fn);
130     ce = cache + (h % CACHE_SIZE);
132     pthread_mutex_lock(&stat_cache_mutex);
134     if (!ce->stat_info.filename || ce->stat_info.hash != h || strcmp(ce->stat_info.filename, fn)) {
135         free(ce->stat_info.filename);
136         ce->stat_info.filename = strdup(fn);
137         ce->stat_info.hash = h;
138     }
140     ce->stat_info.st = *st;
141     ce->stat_info.dead = time(NULL)+CACHE_TIMEOUT;
142     ce->stat_info.valid = 1;
144     pthread_mutex_unlock(&stat_cache_mutex);
147 void stat_cache_invalidate(const char*fn) {
148     uint32_t h;
149     struct cache_entry *ce;
151     assert(cache);
153     h = calc_hash(fn);
154     ce = cache + (h % CACHE_SIZE);
156     pthread_mutex_lock(&stat_cache_mutex);
158     ce->stat_info.valid = 0;
159     free(ce->stat_info.filename);
160     ce->stat_info.filename = NULL;
162     pthread_mutex_unlock(&stat_cache_mutex);
165 static void free_dir_entries(struct dir_entry *de) {
167     while (de) {
168         struct dir_entry *next = de->next;
169         free(de);
170         de = next;
171     }
175 void dir_cache_begin(const char *fn) {
176     uint32_t h;
177     struct cache_entry *ce;
178     struct dir_entry *de = NULL, *de2 = NULL;
179     assert(cache);
181     h = calc_hash(fn);
182     ce = cache + (h % CACHE_SIZE);
184     pthread_mutex_lock(&dir_cache_mutex);
186     if (!ce->dir_info.filling) {
188         if (!ce->dir_info.filename || ce->dir_info.hash != h || strcmp(ce->dir_info.filename, fn)) {
189             free(ce->dir_info.filename);
190             ce->dir_info.filename = strdup(fn);
191             ce->dir_info.hash = h;
193             de = ce->dir_info.entries;
194             ce->dir_info.entries = NULL;
195             ce->dir_info.valid = 0;
196         }
198         de2 = ce->dir_info.entries2;
199         ce->dir_info.entries2 = NULL;
200         ce->dir_info.valid2 = 0;
201         ce->dir_info.filling = 1;
202     }
204     pthread_mutex_unlock(&dir_cache_mutex);
205     free_dir_entries(de);
206     free_dir_entries(de2);
209 void dir_cache_finish(const char *fn, int success) {
210     uint32_t h;
211     struct cache_entry *ce;
212     struct dir_entry *de = NULL;
213     assert(cache);
215     h = calc_hash(fn);
216     ce = cache + (h % CACHE_SIZE);
218     pthread_mutex_lock(&dir_cache_mutex);
220     if (ce->dir_info.filling &&
221         ce->dir_info.filename &&
222         ce->dir_info.hash == h &&
223         !strcmp(ce->dir_info.filename, fn)) {
225         assert(!ce->dir_info.valid2);
227         if (success) {
229             ce->dir_info.valid2 = 1;
230             ce->dir_info.filling = 0;
231             ce->dir_info.dead2 = time(NULL)+CACHE_TIMEOUT;
233             if (!ce->dir_info.in_use) {
234                 de = ce->dir_info.entries;
235                 ce->dir_info.entries = ce->dir_info.entries2;
236                 ce->dir_info.entries2 = NULL;
237                 ce->dir_info.dead = ce->dir_info.dead2;
238                 ce->dir_info.valid2 = 0;
239                 ce->dir_info.valid = 1;
240             }
242         } else {
243             ce->dir_info.filling = 0;
244             de = ce->dir_info.entries2;
245             ce->dir_info.entries2 = NULL;
246         }
247     }
249     pthread_mutex_unlock(&dir_cache_mutex);
250     free_dir_entries(de);
253 void dir_cache_add(const char *fn, const char *subdir) {
254     uint32_t h;
255     struct cache_entry *ce;
256     assert(cache);
258     h = calc_hash(fn);
259     ce = cache + (h % CACHE_SIZE);
261     pthread_mutex_lock(&dir_cache_mutex);
263     if (ce->dir_info.filling &&
264         ce->dir_info.filename &&
265         ce->dir_info.hash == h &&
266         !strcmp(ce->dir_info.filename, fn)) {
268         struct dir_entry *n;
270         assert(!ce->dir_info.valid2);
272         n = malloc(sizeof(struct dir_entry) + strlen(subdir) + 1);
273         assert(n);
275         strcpy(n->filename, subdir);
277         n->next = ce->dir_info.entries2;
278         ce->dir_info.entries2 = n;
279     }
281     pthread_mutex_unlock(&dir_cache_mutex);
284 int dir_cache_enumerate(const char *fn, void (*f) (const char*fn, const char *subdir, void *user), void *user) {
285     uint32_t h;
286     struct cache_entry *ce;
287     struct dir_entry *de = NULL;
288     int r = -1;
290     assert(cache && f);
292     h = calc_hash(fn);
293     ce = cache + (h % CACHE_SIZE);
295     pthread_mutex_lock(&dir_cache_mutex);
297     if (ce->dir_info.valid &&
298         ce->dir_info.filename &&
299         ce->dir_info.hash == h &&
300         !strcmp(ce->dir_info.filename, fn) &&
301         time(NULL) <= ce->dir_info.dead) {
303         ce->dir_info.in_use = 1;
304         pthread_mutex_unlock(&dir_cache_mutex);
306         for (de = ce->dir_info.entries; de; de = de->next)
307             f(fn, de->filename, user);
309         pthread_mutex_lock(&dir_cache_mutex);
310         ce->dir_info.in_use = 0;
312         if (ce->dir_info.valid2) {
313             de = ce->dir_info.entries;
314             ce->dir_info.entries = ce->dir_info.entries2;
315             ce->dir_info.entries2 = NULL;
316             ce->dir_info.dead = ce->dir_info.dead2;
317             ce->dir_info.valid2 = 0;
318             ce->dir_info.valid = 1;
319         }
321         r = 0;
322     }
324     pthread_mutex_unlock(&dir_cache_mutex);
325     free_dir_entries(de);
327     return r;
330 void dir_cache_invalidate(const char*fn) {
331     uint32_t h;
332     struct cache_entry *ce;
333     struct dir_entry *de = NULL;
334     assert(cache && fn);
336     h = calc_hash(fn);
337     ce = cache + (h % CACHE_SIZE);
338     pthread_mutex_lock(&dir_cache_mutex);
340     if (ce->dir_info.valid &&
341         ce->dir_info.filename &&
342         ce->dir_info.hash == h &&
343         !strcmp(ce->dir_info.filename, fn)) {
345         ce->dir_info.valid = 0;
346         de = ce->dir_info.entries;
347         ce->dir_info.entries = NULL;
348     }
350     pthread_mutex_unlock(&dir_cache_mutex);
351     free_dir_entries(de);
354 void dir_cache_invalidate_parent(const char *fn) {
355     char *p;
357     if ((p = ne_path_parent(fn))) {
358         int l = strlen(p);
360         if (strcmp(p, "/") && l) {
361             if (p[l-1] == '/')
362                 p[l-1] = 0;
363         }
365         dir_cache_invalidate(p);
366         free(p);
367     } else
368         dir_cache_invalidate(fn);
371 void cache_free(void) {
372     uint32_t h;
373     struct cache_entry *ce;
375     if (!cache)
376         return;
378     for (h = 0, ce = cache; h < CACHE_SIZE; h++, ce++) {
379         free(ce->stat_info.filename);
380         free(ce->dir_info.filename);
381         free_dir_entries(ce->dir_info.entries);
382         free_dir_entries(ce->dir_info.entries2);
383     }
385     memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);
388 void cache_alloc(void) {
390     if (cache)
391         return;
393     cache = malloc(sizeof(struct cache_entry)*CACHE_SIZE);
394     assert(cache);
395     memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);