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 "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);
88 assert(cache);
90 h = calc_hash(fn);
91 ce = cache + (h % CACHE_SIZE);
93 pthread_mutex_lock(&stat_cache_mutex);
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) {
101 *st = ce->stat_info.st;
102 r = 0;
103 }
105 pthread_mutex_unlock(&stat_cache_mutex);
107 return r;
108 }
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);
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 }
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);
134 }
136 void stat_cache_invalidate(const char*fn) {
137 uint32_t h;
138 struct cache_entry *ce;
140 assert(cache);
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;
151 pthread_mutex_unlock(&stat_cache_mutex);
152 }
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 }
161 }
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);
170 h = calc_hash(fn);
171 ce = cache + (h % CACHE_SIZE);
173 pthread_mutex_lock(&dir_cache_mutex);
175 if (!ce->dir_info.filling) {
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 }
193 pthread_mutex_unlock(&dir_cache_mutex);
194 free_dir_entries(de);
195 free_dir_entries(de2);
196 }
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);
204 h = calc_hash(fn);
205 ce = cache + (h % CACHE_SIZE);
207 pthread_mutex_lock(&dir_cache_mutex);
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) {
218 ce->dir_info.valid2 = 1;
219 ce->dir_info.filling = 0;
220 ce->dir_info.dead2 = time(NULL)+CACHE_TIMEOUT;
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 }
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);
240 }
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);
247 h = calc_hash(fn);
248 ce = cache + (h % CACHE_SIZE);
250 pthread_mutex_lock(&dir_cache_mutex);
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;
267 n->next = ce->dir_info.entries2;
268 ce->dir_info.entries2 = n;
269 }
271 pthread_mutex_unlock(&dir_cache_mutex);
272 }
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;
281 h = calc_hash(fn);
282 ce = cache + (h % CACHE_SIZE);
284 pthread_mutex_lock(&dir_cache_mutex);
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 }
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);
325 h = calc_hash(fn);
326 ce = cache + (h % CACHE_SIZE);
327 pthread_mutex_lock(&dir_cache_mutex);
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 }
339 pthread_mutex_unlock(&dir_cache_mutex);
340 free_dir_entries(de);
341 }
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 }
354 dir_cache_invalidate(p);
355 free(p);
356 } else
357 dir_cache_invalidate(fn);
358 }
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);
375 }
377 void cache_alloc(void) {
379 if (cache)
380 return;
382 cache = malloc(sizeof(struct cache_entry)*CACHE_SIZE);
383 memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);
384 }