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 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 ^= * (const 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;
84 void *f;
86 if (debug)
87 fprintf(stderr, "CGET: %s\n", fn);
89 assert(cache);
91 h = calc_hash(fn);
92 ce = cache + (h % CACHE_SIZE);
94 pthread_mutex_lock(&stat_cache_mutex);
96 if (ce->stat_info.valid &&
97 ce->stat_info.filename &&
98 ce->stat_info.hash == h &&
99 !strcmp(ce->stat_info.filename, fn) &&
100 time(NULL) <= ce->stat_info.dead) {
102 *st = ce->stat_info.st;
104 if ((f = file_cache_get(fn))) {
105 st->st_size = file_cache_get_size(f);
106 file_cache_unref(f);
107 }
109 r = 0;
110 }
112 pthread_mutex_unlock(&stat_cache_mutex);
114 return r;
115 }
117 void stat_cache_set(const char *fn, const struct stat*st) {
118 uint32_t h;
119 struct cache_entry *ce;
121 if (debug)
122 fprintf(stderr, "CSET: %s\n", fn);
123 assert(cache);
125 h = calc_hash(fn);
126 ce = cache + (h % CACHE_SIZE);
128 pthread_mutex_lock(&stat_cache_mutex);
130 if (!ce->stat_info.filename || ce->stat_info.hash != h || strcmp(ce->stat_info.filename, fn)) {
131 free(ce->stat_info.filename);
132 ce->stat_info.filename = strdup(fn);
133 ce->stat_info.hash = h;
134 }
136 ce->stat_info.st = *st;
137 ce->stat_info.dead = time(NULL)+CACHE_TIMEOUT;
138 ce->stat_info.valid = 1;
140 pthread_mutex_unlock(&stat_cache_mutex);
141 }
143 void stat_cache_invalidate(const char*fn) {
144 uint32_t h;
145 struct cache_entry *ce;
147 assert(cache);
149 h = calc_hash(fn);
150 ce = cache + (h % CACHE_SIZE);
152 pthread_mutex_lock(&stat_cache_mutex);
154 ce->stat_info.valid = 0;
155 free(ce->stat_info.filename);
156 ce->stat_info.filename = NULL;
158 pthread_mutex_unlock(&stat_cache_mutex);
159 }
161 static void free_dir_entries(struct dir_entry *de) {
163 while (de) {
164 struct dir_entry *next = de->next;
165 free(de);
166 de = next;
167 }
168 }
171 void dir_cache_begin(const char *fn) {
172 uint32_t h;
173 struct cache_entry *ce;
174 struct dir_entry *de = NULL, *de2 = NULL;
175 assert(cache);
177 h = calc_hash(fn);
178 ce = cache + (h % CACHE_SIZE);
180 pthread_mutex_lock(&dir_cache_mutex);
182 if (!ce->dir_info.filling) {
184 if (!ce->dir_info.filename || ce->dir_info.hash != h || strcmp(ce->dir_info.filename, fn)) {
185 free(ce->dir_info.filename);
186 ce->dir_info.filename = strdup(fn);
187 ce->dir_info.hash = h;
189 de = ce->dir_info.entries;
190 ce->dir_info.entries = NULL;
191 ce->dir_info.valid = 0;
192 }
194 de2 = ce->dir_info.entries2;
195 ce->dir_info.entries2 = NULL;
196 ce->dir_info.valid2 = 0;
197 ce->dir_info.filling = 1;
198 }
200 pthread_mutex_unlock(&dir_cache_mutex);
201 free_dir_entries(de);
202 free_dir_entries(de2);
203 }
205 void dir_cache_finish(const char *fn, int success) {
206 uint32_t h;
207 struct cache_entry *ce;
208 struct dir_entry *de = NULL;
209 assert(cache);
211 h = calc_hash(fn);
212 ce = cache + (h % CACHE_SIZE);
214 pthread_mutex_lock(&dir_cache_mutex);
216 if (ce->dir_info.filling &&
217 ce->dir_info.filename &&
218 ce->dir_info.hash == h &&
219 !strcmp(ce->dir_info.filename, fn)) {
221 assert(!ce->dir_info.valid2);
223 if (success) {
225 ce->dir_info.valid2 = 1;
226 ce->dir_info.filling = 0;
227 ce->dir_info.dead2 = time(NULL)+CACHE_TIMEOUT;
229 if (!ce->dir_info.in_use) {
230 de = ce->dir_info.entries;
231 ce->dir_info.entries = ce->dir_info.entries2;
232 ce->dir_info.entries2 = NULL;
233 ce->dir_info.dead = ce->dir_info.dead2;
234 ce->dir_info.valid2 = 0;
235 ce->dir_info.valid = 1;
236 }
238 } else {
239 ce->dir_info.filling = 0;
240 de = ce->dir_info.entries2;
241 ce->dir_info.entries2 = NULL;
242 }
243 }
245 pthread_mutex_unlock(&dir_cache_mutex);
246 free_dir_entries(de);
247 }
249 void dir_cache_add(const char *fn, const char *subdir) {
250 uint32_t h;
251 struct cache_entry *ce;
252 assert(cache);
254 h = calc_hash(fn);
255 ce = cache + (h % CACHE_SIZE);
257 pthread_mutex_lock(&dir_cache_mutex);
259 if (ce->dir_info.filling &&
260 ce->dir_info.filename &&
261 ce->dir_info.hash == h &&
262 !strcmp(ce->dir_info.filename, fn)) {
264 struct dir_entry *n;
266 assert(!ce->dir_info.valid2);
268 n = malloc(sizeof(struct dir_entry) + strlen(subdir) + 1);
269 assert(n);
271 strcpy(n->filename, subdir);
273 n->next = ce->dir_info.entries2;
274 ce->dir_info.entries2 = n;
275 }
277 pthread_mutex_unlock(&dir_cache_mutex);
278 }
280 int dir_cache_enumerate(const char *fn, void (*f) (const char*fn, const char *subdir, void *user), void *user) {
281 uint32_t h;
282 struct cache_entry *ce;
283 struct dir_entry *de = NULL;
284 int r = -1;
286 assert(cache && f);
288 h = calc_hash(fn);
289 ce = cache + (h % CACHE_SIZE);
291 pthread_mutex_lock(&dir_cache_mutex);
293 if (ce->dir_info.valid &&
294 ce->dir_info.filename &&
295 ce->dir_info.hash == h &&
296 !strcmp(ce->dir_info.filename, fn) &&
297 time(NULL) <= ce->dir_info.dead) {
299 ce->dir_info.in_use = 1;
300 pthread_mutex_unlock(&dir_cache_mutex);
302 for (de = ce->dir_info.entries; de; de = de->next)
303 f(fn, de->filename, user);
305 pthread_mutex_lock(&dir_cache_mutex);
306 ce->dir_info.in_use = 0;
308 if (ce->dir_info.valid2) {
309 de = ce->dir_info.entries;
310 ce->dir_info.entries = ce->dir_info.entries2;
311 ce->dir_info.entries2 = NULL;
312 ce->dir_info.dead = ce->dir_info.dead2;
313 ce->dir_info.valid2 = 0;
314 ce->dir_info.valid = 1;
315 }
317 r = 0;
318 }
320 pthread_mutex_unlock(&dir_cache_mutex);
321 free_dir_entries(de);
323 return r;
324 }
326 void dir_cache_invalidate(const char*fn) {
327 uint32_t h;
328 struct cache_entry *ce;
329 struct dir_entry *de = NULL;
330 assert(cache && fn);
332 h = calc_hash(fn);
333 ce = cache + (h % CACHE_SIZE);
334 pthread_mutex_lock(&dir_cache_mutex);
336 if (ce->dir_info.valid &&
337 ce->dir_info.filename &&
338 ce->dir_info.hash == h &&
339 !strcmp(ce->dir_info.filename, fn)) {
341 ce->dir_info.valid = 0;
342 de = ce->dir_info.entries;
343 ce->dir_info.entries = NULL;
344 }
346 pthread_mutex_unlock(&dir_cache_mutex);
347 free_dir_entries(de);
348 }
350 void dir_cache_invalidate_parent(const char *fn) {
351 char *p;
353 if ((p = ne_path_parent(fn))) {
354 int l = strlen(p);
356 if (strcmp(p, "/") && l) {
357 if (p[l-1] == '/')
358 p[l-1] = 0;
359 }
361 dir_cache_invalidate(p);
362 free(p);
363 } else
364 dir_cache_invalidate(fn);
365 }
367 void cache_free(void) {
368 uint32_t h;
369 struct cache_entry *ce;
371 if (!cache)
372 return;
374 for (h = 0, ce = cache; h < CACHE_SIZE; h++, ce++) {
375 free(ce->stat_info.filename);
376 free(ce->dir_info.filename);
377 free_dir_entries(ce->dir_info.entries);
378 free_dir_entries(ce->dir_info.entries2);
379 }
381 memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);
382 }
384 void cache_alloc(void) {
386 if (cache)
387 return;
389 cache = malloc(sizeof(struct cache_entry)*CACHE_SIZE);
390 assert(cache);
391 memset(cache, 0, sizeof(struct cache_entry)*CACHE_SIZE);
392 }