Code

add boilerplate licenses in source files
[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 "fusedav.h"
36 #include <ne_uri.h>
38 #define CACHE_SIZE 2049
39 #define CACHE_TIMEOUT 60
41 struct dir_entry {
42     struct dir_entry *next;
43     int is_dir;
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 ^= * (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;
85     if (debug)
86         fprintf(stderr, "CGET: %s\n", fn);
87     
88     assert(cache);
89     
90     h = calc_hash(fn);
91     ce = cache + (h % CACHE_SIZE);
93     pthread_mutex_lock(&stat_cache_mutex);
94     
95     if (ce->stat_info.valid &&
96         ce->stat_info.filename &&
97         ce->stat_info.hash == h &&
98         !strcmp(ce->stat_info.filename, fn) &&
99         time(NULL) <= ce->stat_info.dead) {
100         
101         *st = ce->stat_info.st;
102         r = 0;
103     }
105     pthread_mutex_unlock(&stat_cache_mutex);
106     
107     return r;
110 void stat_cache_set(const char *fn, const struct stat*st) {
111     uint32_t h;
112     struct cache_entry *ce;
114     if (debug)
115         fprintf(stderr, "CSET: %s\n", fn);
116     assert(cache);
117     
118     h = calc_hash(fn);
119     ce = cache + (h % CACHE_SIZE);
121     pthread_mutex_lock(&stat_cache_mutex);
123     if (!ce->stat_info.filename || ce->stat_info.hash != h || strcmp(ce->stat_info.filename, fn)) {
124         free(ce->stat_info.filename);
125         ce->stat_info.filename = strdup(fn);
126         ce->stat_info.hash = h;
127     }
128         
129     ce->stat_info.st = *st;
130     ce->stat_info.dead = time(NULL)+CACHE_TIMEOUT;
131     ce->stat_info.valid = 1;
133     pthread_mutex_unlock(&stat_cache_mutex);
136 void stat_cache_invalidate(const char*fn) {
137     uint32_t h;
138     struct cache_entry *ce;
140     assert(cache);
141     
142     h = calc_hash(fn);
143     ce = cache + (h % CACHE_SIZE);
145     pthread_mutex_lock(&stat_cache_mutex);
147     ce->stat_info.valid = 0;
148     free(ce->stat_info.filename);
149     ce->stat_info.filename = NULL;
150     
151     pthread_mutex_unlock(&stat_cache_mutex);
154 static void free_dir_entries(struct dir_entry *de) {
156     while (de) {
157         struct dir_entry *next = de->next;
158         free(de);
159         de = next;
160     }
164 void dir_cache_begin(const char *fn) {
165     uint32_t h;
166     struct cache_entry *ce;
167     struct dir_entry *de = NULL, *de2 = NULL;
168     assert(cache);
169     
170     h = calc_hash(fn);
171     ce = cache + (h % CACHE_SIZE);
172     
173     pthread_mutex_lock(&dir_cache_mutex);
175     if (!ce->dir_info.filling) {
176         
177         if (!ce->dir_info.filename || ce->dir_info.hash != h || strcmp(ce->dir_info.filename, fn)) {
178             free(ce->dir_info.filename);
179             ce->dir_info.filename = strdup(fn);
180             ce->dir_info.hash = h;
182             de = ce->dir_info.entries;
183             ce->dir_info.entries = NULL;
184             ce->dir_info.valid = 0;
185         }
187         de2 = ce->dir_info.entries2;
188         ce->dir_info.entries2 = NULL;
189         ce->dir_info.valid2 = 0;
190         ce->dir_info.filling = 1;
191     }
192     
193     pthread_mutex_unlock(&dir_cache_mutex);
194     free_dir_entries(de);
195     free_dir_entries(de2);
198 void dir_cache_finish(const char *fn, int success) {
199     uint32_t h;
200     struct cache_entry *ce;
201     struct dir_entry *de = NULL;
202     assert(cache);
203     
204     h = calc_hash(fn);
205     ce = cache + (h % CACHE_SIZE);
206     
207     pthread_mutex_lock(&dir_cache_mutex);
208     
209     if (ce->dir_info.filling &&
210         ce->dir_info.filename &&
211         ce->dir_info.hash == h &&
212         !strcmp(ce->dir_info.filename, fn)) {
214         assert(!ce->dir_info.valid2);
216         if (success) {
217             
218             ce->dir_info.valid2 = 1;
219             ce->dir_info.filling = 0;
220             ce->dir_info.dead2 = time(NULL)+CACHE_TIMEOUT;
221             
222             if (!ce->dir_info.in_use) {
223                 de = ce->dir_info.entries;
224                 ce->dir_info.entries = ce->dir_info.entries2;
225                 ce->dir_info.entries2 = NULL;
226                 ce->dir_info.dead = ce->dir_info.dead2;
227                 ce->dir_info.valid2 = 0;
228                 ce->dir_info.valid = 1;
229             }
230             
231         } else {
232             ce->dir_info.filling = 0;
233             de = ce->dir_info.entries2;
234             ce->dir_info.entries2 = NULL;
235         }
236     }
238     pthread_mutex_unlock(&dir_cache_mutex);
239     free_dir_entries(de);
242 void dir_cache_add(const char *fn, const char *subdir, int is_dir) {
243     uint32_t h;
244     struct cache_entry *ce;
245     assert(cache);
246     
247     h = calc_hash(fn);
248     ce = cache + (h % CACHE_SIZE);
249     
250     pthread_mutex_lock(&dir_cache_mutex);
251     
252     if (ce->dir_info.filling &&
253         ce->dir_info.filename &&
254         ce->dir_info.hash == h &&
255         !strcmp(ce->dir_info.filename, fn)) {
257         struct dir_entry *n;
259         assert(!ce->dir_info.valid2);
261         n = malloc(sizeof(struct dir_entry) + strlen(subdir) + 1);
262         assert(n);
264         strcpy(n->filename, subdir);
265         n->is_dir = is_dir;
266         
267         n->next = ce->dir_info.entries2;
268         ce->dir_info.entries2 = n;
269     }
271     pthread_mutex_unlock(&dir_cache_mutex);
274 int dir_cache_enumerate(const char *fn, void (*f) (const char*fn, const char *subdir, int is_dir, void *user), void *user) {
275     uint32_t h;
276     struct cache_entry *ce;
277     struct dir_entry *de = NULL;
278     assert(cache && f);
279     int r = -1;
280     
281     h = calc_hash(fn);
282     ce = cache + (h % CACHE_SIZE);
283     
284     pthread_mutex_lock(&dir_cache_mutex);
285     
286     if (ce->dir_info.valid &&
287         ce->dir_info.filename &&
288         ce->dir_info.hash == h &&
289         !strcmp(ce->dir_info.filename, fn) &&
290         time(NULL) <= ce->dir_info.dead) {
292         ce->dir_info.in_use = 1;
293         pthread_mutex_unlock(&dir_cache_mutex);
295         for (de = ce->dir_info.entries; de; de = de->next)
296             f(fn, de->filename, de->is_dir, user);
298         pthread_mutex_lock(&dir_cache_mutex);
299         ce->dir_info.in_use = 0;
301         if (ce->dir_info.valid2) {
302             de = ce->dir_info.entries;
303             ce->dir_info.entries = ce->dir_info.entries2;
304             ce->dir_info.entries2 = NULL;
305             ce->dir_info.dead = ce->dir_info.dead2;
306             ce->dir_info.valid2 = 0;
307             ce->dir_info.valid = 1;
308         }
310         r = 0;
311     }
312     
313     pthread_mutex_unlock(&dir_cache_mutex);
314     free_dir_entries(de);
316     return r;
317 }   
319 void dir_cache_invalidate(const char*fn) {
320     uint32_t h;
321     struct cache_entry *ce;
322     struct dir_entry *de = NULL;
323     assert(cache && fn);
324     
325     h = calc_hash(fn);
326     ce = cache + (h % CACHE_SIZE);
327     pthread_mutex_lock(&dir_cache_mutex);
328     
329     if (ce->dir_info.valid &&
330         ce->dir_info.filename &&
331         ce->dir_info.hash == h &&
332         !strcmp(ce->dir_info.filename, fn)) {
334         ce->dir_info.valid = 0;
335         de = ce->dir_info.entries;
336         ce->dir_info.entries = NULL;
337     }
338     
339     pthread_mutex_unlock(&dir_cache_mutex);
340     free_dir_entries(de);
343 void dir_cache_invalidate_parent(const char *fn) {
344     char *p;
346     if ((p = ne_path_parent(fn))) {
347         int l = strlen(p);
349         if (strcmp(p, "/") && l) {
350             if (p[l-1] == '/')
351                 p[l-1] = 0;
352         }
353         
354         dir_cache_invalidate(p);
355         free(p);
356     } else
357         dir_cache_invalidate(fn);
360 void cache_free(void) {
361     uint32_t h;
362     struct cache_entry *ce;
364     if (!cache)
365         return;
367     for (h = 0, ce = cache; h < CACHE_SIZE; h++, ce++) {
368         free(ce->stat_info.filename);
369         free(ce->dir_info.filename);
370         free_dir_entries(ce->dir_info.entries);
371         free_dir_entries(ce->dir_info.entries2);
372     }
374     memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);
377 void cache_alloc(void) {
378     
379     if (cache)
380         return;
382     cache = malloc(sizeof(struct cache_entry)*CACHE_SIZE);
383     memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);