* deal properly with signals
[fusedav.git] / src / statcache.c
blob22737f7fc9de97103fd1a9c1f1975b61e5026465
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.
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.
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[];
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;
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);
78 return h;
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);
90 assert(cache);
92 h = calc_hash(fn);
93 ce = cache + (h % CACHE_SIZE);
95 pthread_mutex_lock(&stat_cache_mutex);
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) {
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);
110 r = 0;
113 pthread_mutex_unlock(&stat_cache_mutex);
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);
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;
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);
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;
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;
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);
178 h = calc_hash(fn);
179 ce = cache + (h % CACHE_SIZE);
181 pthread_mutex_lock(&dir_cache_mutex);
183 if (!ce->dir_info.filling) {
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;
195 de2 = ce->dir_info.entries2;
196 ce->dir_info.entries2 = NULL;
197 ce->dir_info.valid2 = 0;
198 ce->dir_info.filling = 1;
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);
212 h = calc_hash(fn);
213 ce = cache + (h % CACHE_SIZE);
215 pthread_mutex_lock(&dir_cache_mutex);
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) {
226 ce->dir_info.valid2 = 1;
227 ce->dir_info.filling = 0;
228 ce->dir_info.dead2 = time(NULL)+CACHE_TIMEOUT;
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;
239 } else {
240 ce->dir_info.filling = 0;
241 de = ce->dir_info.entries2;
242 ce->dir_info.entries2 = NULL;
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);
255 h = calc_hash(fn);
256 ce = cache + (h % CACHE_SIZE);
258 pthread_mutex_lock(&dir_cache_mutex);
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;
275 n->next = ce->dir_info.entries2;
276 ce->dir_info.entries2 = n;
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);
290 h = calc_hash(fn);
291 ce = cache + (h % CACHE_SIZE);
293 pthread_mutex_lock(&dir_cache_mutex);
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;
319 r = 0;
322 pthread_mutex_unlock(&dir_cache_mutex);
323 free_dir_entries(de);
325 return r;
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);
334 h = calc_hash(fn);
335 ce = cache + (h % CACHE_SIZE);
336 pthread_mutex_lock(&dir_cache_mutex);
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;
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;
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);
383 memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);
386 void cache_alloc(void) {
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);