git: remove $Id$ svn cruft
[fusedav.git] / src / statcache.c
blob28f3e275199d561d2274bcb5e79e0a6b6693f826
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[];
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;
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);
81 return h;
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);
113 r = 0;
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;
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;
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;
198 de2 = ce->dir_info.entries2;
199 ce->dir_info.entries2 = NULL;
200 ce->dir_info.valid2 = 0;
201 ce->dir_info.filling = 1;
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;
242 } else {
243 ce->dir_info.filling = 0;
244 de = ce->dir_info.entries2;
245 ce->dir_info.entries2 = NULL;
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;
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;
321 r = 0;
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;
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;
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);
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);