Code

3105a5b84c9f2620fd9439584df135551acc5a9e
[fusedav.git] / src / statcache.c
1 /* $Id$ */
3 /***
4   Copyright (c) 2004-2006 Lennart Poettering
6   Permission is hereby granted, free of charge, to any person
7   obtaining a copy of this software and associated documentation files
8   (the "Software"), to deal in the Software without restriction,
9   including without limitation the rights to use, copy, modify, merge,
10   publish, distribute, sublicense, and/or sell copies of the Software,
11   and to permit persons to whom the Software is furnished to do so,
12   subject to the following conditions:
14   The above copyright notice and this permission notice shall be
15   included in all copies or substantial portions of the Software.
17   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24   SOFTWARE.
25 ***/
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
31 #include <stdio.h>
32 #include <inttypes.h>
33 #include <time.h>
34 #include <string.h>
35 #include <malloc.h>
36 #include <pthread.h>
37 #include <assert.h>
39 #include "statcache.h"
40 #include "filecache.h"
41 #include "fusedav.h"
43 #include <ne_uri.h>
45 #define CACHE_SIZE 2049
46 #define CACHE_TIMEOUT 60
48 struct dir_entry {
49     struct dir_entry *next;
50     char filename[];
51 };
53 struct cache_entry {
54     struct {
55         int valid;
56         uint32_t hash;
57         char *filename;
58         time_t dead;
59         struct stat st;
60     } stat_info;
62     struct {
63         int valid, filling, in_use, valid2;
64         uint32_t hash;
65         char *filename;
66         struct dir_entry *entries, *entries2;
67         time_t dead, dead2;
68     } dir_info;
69 };
71 static struct cache_entry *cache = NULL;
72 static pthread_mutex_t stat_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
73 static pthread_mutex_t dir_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
75 static uint32_t calc_hash(const char *s) {
76     uint32_t h = 0;
78     for (; *s; s++) {
79         h ^= * (const uint8_t*) s;
80         h = (h << 8) | (h  >> 24);
81     }
83     return h;
84 }
86 int stat_cache_get(const char *fn, struct stat *st) {
87     uint32_t h;
88     struct cache_entry *ce;
89     int r = -1;
90     void *f;
92     if (debug)
93         fprintf(stderr, "CGET: %s\n", fn);
95     assert(cache);
97     h = calc_hash(fn);
98     ce = cache + (h % CACHE_SIZE);
100     pthread_mutex_lock(&stat_cache_mutex);
102     if (ce->stat_info.valid &&
103         ce->stat_info.filename &&
104         ce->stat_info.hash == h &&
105         !strcmp(ce->stat_info.filename, fn) &&
106         time(NULL) <= ce->stat_info.dead) {
108         *st = ce->stat_info.st;
110         if ((f = file_cache_get(fn))) {
111             st->st_size = file_cache_get_size(f);
112             file_cache_unref(f);
113         }
115         r = 0;
116     }
118     pthread_mutex_unlock(&stat_cache_mutex);
120     return r;
123 void stat_cache_set(const char *fn, const struct stat*st) {
124     uint32_t h;
125     struct cache_entry *ce;
127     if (debug)
128         fprintf(stderr, "CSET: %s\n", fn);
129     assert(cache);
131     h = calc_hash(fn);
132     ce = cache + (h % CACHE_SIZE);
134     pthread_mutex_lock(&stat_cache_mutex);
136     if (!ce->stat_info.filename || ce->stat_info.hash != h || strcmp(ce->stat_info.filename, fn)) {
137         free(ce->stat_info.filename);
138         ce->stat_info.filename = strdup(fn);
139         ce->stat_info.hash = h;
140     }
142     ce->stat_info.st = *st;
143     ce->stat_info.dead = time(NULL)+CACHE_TIMEOUT;
144     ce->stat_info.valid = 1;
146     pthread_mutex_unlock(&stat_cache_mutex);
149 void stat_cache_invalidate(const char*fn) {
150     uint32_t h;
151     struct cache_entry *ce;
153     assert(cache);
155     h = calc_hash(fn);
156     ce = cache + (h % CACHE_SIZE);
158     pthread_mutex_lock(&stat_cache_mutex);
160     ce->stat_info.valid = 0;
161     free(ce->stat_info.filename);
162     ce->stat_info.filename = NULL;
164     pthread_mutex_unlock(&stat_cache_mutex);
167 static void free_dir_entries(struct dir_entry *de) {
169     while (de) {
170         struct dir_entry *next = de->next;
171         free(de);
172         de = next;
173     }
177 void dir_cache_begin(const char *fn) {
178     uint32_t h;
179     struct cache_entry *ce;
180     struct dir_entry *de = NULL, *de2 = NULL;
181     assert(cache);
183     h = calc_hash(fn);
184     ce = cache + (h % CACHE_SIZE);
186     pthread_mutex_lock(&dir_cache_mutex);
188     if (!ce->dir_info.filling) {
190         if (!ce->dir_info.filename || ce->dir_info.hash != h || strcmp(ce->dir_info.filename, fn)) {
191             free(ce->dir_info.filename);
192             ce->dir_info.filename = strdup(fn);
193             ce->dir_info.hash = h;
195             de = ce->dir_info.entries;
196             ce->dir_info.entries = NULL;
197             ce->dir_info.valid = 0;
198         }
200         de2 = ce->dir_info.entries2;
201         ce->dir_info.entries2 = NULL;
202         ce->dir_info.valid2 = 0;
203         ce->dir_info.filling = 1;
204     }
206     pthread_mutex_unlock(&dir_cache_mutex);
207     free_dir_entries(de);
208     free_dir_entries(de2);
211 void dir_cache_finish(const char *fn, int success) {
212     uint32_t h;
213     struct cache_entry *ce;
214     struct dir_entry *de = NULL;
215     assert(cache);
217     h = calc_hash(fn);
218     ce = cache + (h % CACHE_SIZE);
220     pthread_mutex_lock(&dir_cache_mutex);
222     if (ce->dir_info.filling &&
223         ce->dir_info.filename &&
224         ce->dir_info.hash == h &&
225         !strcmp(ce->dir_info.filename, fn)) {
227         assert(!ce->dir_info.valid2);
229         if (success) {
231             ce->dir_info.valid2 = 1;
232             ce->dir_info.filling = 0;
233             ce->dir_info.dead2 = time(NULL)+CACHE_TIMEOUT;
235             if (!ce->dir_info.in_use) {
236                 de = ce->dir_info.entries;
237                 ce->dir_info.entries = ce->dir_info.entries2;
238                 ce->dir_info.entries2 = NULL;
239                 ce->dir_info.dead = ce->dir_info.dead2;
240                 ce->dir_info.valid2 = 0;
241                 ce->dir_info.valid = 1;
242             }
244         } else {
245             ce->dir_info.filling = 0;
246             de = ce->dir_info.entries2;
247             ce->dir_info.entries2 = NULL;
248         }
249     }
251     pthread_mutex_unlock(&dir_cache_mutex);
252     free_dir_entries(de);
255 void dir_cache_add(const char *fn, const char *subdir) {
256     uint32_t h;
257     struct cache_entry *ce;
258     assert(cache);
260     h = calc_hash(fn);
261     ce = cache + (h % CACHE_SIZE);
263     pthread_mutex_lock(&dir_cache_mutex);
265     if (ce->dir_info.filling &&
266         ce->dir_info.filename &&
267         ce->dir_info.hash == h &&
268         !strcmp(ce->dir_info.filename, fn)) {
270         struct dir_entry *n;
272         assert(!ce->dir_info.valid2);
274         n = malloc(sizeof(struct dir_entry) + strlen(subdir) + 1);
275         assert(n);
277         strcpy(n->filename, subdir);
279         n->next = ce->dir_info.entries2;
280         ce->dir_info.entries2 = n;
281     }
283     pthread_mutex_unlock(&dir_cache_mutex);
286 int dir_cache_enumerate(const char *fn, void (*f) (const char*fn, const char *subdir, void *user), void *user) {
287     uint32_t h;
288     struct cache_entry *ce;
289     struct dir_entry *de = NULL;
290     int r = -1;
292     assert(cache && f);
294     h = calc_hash(fn);
295     ce = cache + (h % CACHE_SIZE);
297     pthread_mutex_lock(&dir_cache_mutex);
299     if (ce->dir_info.valid &&
300         ce->dir_info.filename &&
301         ce->dir_info.hash == h &&
302         !strcmp(ce->dir_info.filename, fn) &&
303         time(NULL) <= ce->dir_info.dead) {
305         ce->dir_info.in_use = 1;
306         pthread_mutex_unlock(&dir_cache_mutex);
308         for (de = ce->dir_info.entries; de; de = de->next)
309             f(fn, de->filename, user);
311         pthread_mutex_lock(&dir_cache_mutex);
312         ce->dir_info.in_use = 0;
314         if (ce->dir_info.valid2) {
315             de = ce->dir_info.entries;
316             ce->dir_info.entries = ce->dir_info.entries2;
317             ce->dir_info.entries2 = NULL;
318             ce->dir_info.dead = ce->dir_info.dead2;
319             ce->dir_info.valid2 = 0;
320             ce->dir_info.valid = 1;
321         }
323         r = 0;
324     }
326     pthread_mutex_unlock(&dir_cache_mutex);
327     free_dir_entries(de);
329     return r;
332 void dir_cache_invalidate(const char*fn) {
333     uint32_t h;
334     struct cache_entry *ce;
335     struct dir_entry *de = NULL;
336     assert(cache && fn);
338     h = calc_hash(fn);
339     ce = cache + (h % CACHE_SIZE);
340     pthread_mutex_lock(&dir_cache_mutex);
342     if (ce->dir_info.valid &&
343         ce->dir_info.filename &&
344         ce->dir_info.hash == h &&
345         !strcmp(ce->dir_info.filename, fn)) {
347         ce->dir_info.valid = 0;
348         de = ce->dir_info.entries;
349         ce->dir_info.entries = NULL;
350     }
352     pthread_mutex_unlock(&dir_cache_mutex);
353     free_dir_entries(de);
356 void dir_cache_invalidate_parent(const char *fn) {
357     char *p;
359     if ((p = ne_path_parent(fn))) {
360         int l = strlen(p);
362         if (strcmp(p, "/") && l) {
363             if (p[l-1] == '/')
364                 p[l-1] = 0;
365         }
367         dir_cache_invalidate(p);
368         free(p);
369     } else
370         dir_cache_invalidate(fn);
373 void cache_free(void) {
374     uint32_t h;
375     struct cache_entry *ce;
377     if (!cache)
378         return;
380     for (h = 0, ce = cache; h < CACHE_SIZE; h++, ce++) {
381         free(ce->stat_info.filename);
382         free(ce->dir_info.filename);
383         free_dir_entries(ce->dir_info.entries);
384         free_dir_entries(ce->dir_info.entries2);
385     }
387     memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);
390 void cache_alloc(void) {
392     if (cache)
393         return;
395     cache = malloc(sizeof(struct cache_entry)*CACHE_SIZE);
396     assert(cache);
397     memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);