fixnuta buga v remove_last (nesnizovala grow ptr, ktery tak ubiral znaky a nemenil...
[httpfs.git] / cache.c
blob581adc1c776f2a6eb09eef23bebf6a1543c90cd9
1 /*
2 Caching file system proxy
3 Copyright (C) 2004 Miklos Szeredi <miklos@szeredi.hu>
5 This program can be distributed under the terms of the GNU GPL.
6 See the file COPYING.
7 */
9 #include "cache.h"
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <errno.h>
14 #include <glib.h>
15 #include <pthread.h>
17 #define DEFAULT_CACHE_TIMEOUT 20
18 #define MAX_CACHE_SIZE 10000
19 #define MIN_CACHE_CLEAN_INTERVAL 5
20 #define CACHE_CLEAN_INTERVAL 60
22 struct cache {
23 int on;
24 unsigned stat_timeout;
25 unsigned dir_timeout;
26 unsigned link_timeout;
27 struct fuse_cache_operations *next_oper;
28 GHashTable *table;
29 pthread_mutex_t lock;
30 time_t last_cleaned;
33 static struct cache cache;
35 struct node {
36 struct stat stat;
37 time_t stat_valid;
38 char **dir;
39 time_t dir_valid;
40 char *link;
41 time_t link_valid;
42 time_t valid;
43 int weak;
46 struct fuse_cache_dirhandle {
47 const char *path;
48 fuse_dirh_t h;
49 fuse_dirfil_t filler;
50 GPtrArray *dir;
53 static void free_node(gpointer node_)
55 struct node *node = (struct node *) node_;
56 g_strfreev(node->dir);
57 g_free(node);
60 static int cache_clean_entry(void *key_, struct node *node, time_t *now)
62 (void) key_;
63 if (*now > node->valid)
64 return TRUE;
65 else
66 return FALSE;
69 static void cache_clean(void)
71 time_t now = time(NULL);
72 if (now > cache.last_cleaned + MIN_CACHE_CLEAN_INTERVAL &&
73 (g_hash_table_size(cache.table) > MAX_CACHE_SIZE ||
74 now > cache.last_cleaned + CACHE_CLEAN_INTERVAL)) {
75 g_hash_table_foreach_remove(cache.table,
76 (GHRFunc) cache_clean_entry, &now);
77 cache.last_cleaned = now;
81 static struct node *cache_lookup(const char *path)
83 return (struct node *) g_hash_table_lookup(cache.table, path);
86 static void cache_purge(const char *path)
88 g_hash_table_remove(cache.table, path);
91 static void cache_purge_parent(const char *path)
93 const char *s = strrchr(path, '/');
94 if (s) {
95 if (s == path)
96 g_hash_table_remove(cache.table, "/");
97 else {
98 char *parent = g_strndup(path, s - path);
99 cache_purge(parent);
100 g_free(parent);
105 void cache_invalidate_weak(const char *path)
107 struct node *n;
109 pthread_mutex_lock(&cache.lock);
110 n = (struct node *) g_hash_table_lookup(cache.table, path);
111 if (n && n->weak) {
112 g_hash_table_remove(cache.table, path);
114 pthread_mutex_unlock(&cache.lock);
118 void cache_invalidate(const char *path)
120 pthread_mutex_lock(&cache.lock);
121 cache_purge(path);
122 pthread_mutex_unlock(&cache.lock);
125 static void cache_invalidate_dir(const char *path)
127 pthread_mutex_lock(&cache.lock);
128 cache_purge(path);
129 cache_purge_parent(path);
130 pthread_mutex_unlock(&cache.lock);
133 static int cache_del_children(const char *key, void *val_, const char *path)
135 (void) val_;
136 if (strncmp(key, path, strlen(path)) == 0)
137 return TRUE;
138 else
139 return FALSE;
142 static void cache_do_rename(const char *from, const char *to)
144 pthread_mutex_lock(&cache.lock);
145 g_hash_table_foreach_remove(cache.table, (GHRFunc) cache_del_children,
146 (char *) from);
147 cache_purge(from);
148 cache_purge(to);
149 cache_purge_parent(from);
150 cache_purge_parent(to);
151 pthread_mutex_unlock(&cache.lock);
154 static struct node *cache_get(const char *path)
156 struct node *node = cache_lookup(path);
157 if (node == NULL) {
158 char *pathcopy = g_strdup(path);
159 node = g_new0(struct node, 1);
160 g_hash_table_insert(cache.table, pathcopy, node);
162 return node;
165 void cache_add_attr(const char *path, const struct stat *stbuf, int weak)
167 struct node *node;
168 time_t now;
170 pthread_mutex_lock(&cache.lock);
171 node = cache_get(path);
172 now = time(NULL);
173 node->stat = *stbuf;
174 node->weak = weak;
175 node->stat_valid = time(NULL) + cache.stat_timeout;
176 if (node->stat_valid > node->valid)
177 node->valid = node->stat_valid;
178 cache_clean();
179 pthread_mutex_unlock(&cache.lock);
182 static void cache_add_dir(const char *path, char **dir)
184 struct node *node;
185 time_t now;
187 pthread_mutex_lock(&cache.lock);
188 node = cache_get(path);
189 now = time(NULL);
190 g_strfreev(node->dir);
191 node->dir = dir;
192 node->dir_valid = time(NULL) + cache.dir_timeout;
193 if (node->dir_valid > node->valid)
194 node->valid = node->dir_valid;
195 cache_clean();
196 pthread_mutex_unlock(&cache.lock);
199 static size_t my_strnlen(const char *s, size_t maxsize)
201 const char *p;
202 for (p = s; maxsize && *p; maxsize--, p++);
203 return p - s;
206 static void cache_add_link(const char *path, const char *link, size_t size)
208 struct node *node;
209 time_t now;
211 pthread_mutex_lock(&cache.lock);
212 node = cache_get(path);
213 now = time(NULL);
214 g_free(node->link);
215 node->link = g_strndup(link, my_strnlen(link, size-1));
216 node->link_valid = time(NULL) + cache.link_timeout;
217 if (node->link_valid > node->valid)
218 node->valid = node->link_valid;
219 cache_clean();
220 pthread_mutex_unlock(&cache.lock);
223 static int cache_get_attr(const char *path, struct stat *stbuf)
225 struct node *node;
226 int err = -EAGAIN;
227 pthread_mutex_lock(&cache.lock);
228 node = cache_lookup(path);
229 if (node != NULL) {
230 time_t now = time(NULL);
231 if (node->stat_valid - now >= 0) {
232 *stbuf = node->stat;
233 err = 0;
236 pthread_mutex_unlock(&cache.lock);
237 return err;
240 static int cache_getattr(const char *path, struct stat *stbuf)
242 int err = cache_get_attr(path, stbuf);
243 if (err) {
244 err = cache.next_oper->oper.getattr(path, stbuf);
245 if (!err)
246 cache_add_attr(path, stbuf, 0);
247 } else {
248 cache_invalidate_weak(path);
250 return err;
253 static int cache_readlink(const char *path, char *buf, size_t size)
255 struct node *node;
256 int err;
258 pthread_mutex_lock(&cache.lock);
259 node = cache_lookup(path);
260 if (node != NULL) {
261 time_t now = time(NULL);
262 if (node->link_valid - now >= 0) {
263 strncpy(buf, node->link, size-1);
264 buf[size-1] = '\0';
265 pthread_mutex_unlock(&cache.lock);
266 return 0;
269 pthread_mutex_unlock(&cache.lock);
270 err = cache.next_oper->oper.readlink(path, buf, size);
271 if (!err)
272 cache_add_link(path, buf, size);
274 return err;
277 static int cache_dirfill(fuse_cache_dirh_t ch, const char *name,
278 const struct stat *stbuf)
280 int err = ch->filler(ch->h, name, 0, 0);
281 if (!err) {
282 g_ptr_array_add(ch->dir, g_strdup(name));
283 if (stbuf->st_mode & S_IFMT) {
284 char *fullpath =
285 g_strdup_printf("%s/%s", !ch->path[1] ? "" : ch->path, name);
286 cache_add_attr(fullpath, stbuf, 1);
287 g_free(fullpath);
290 return err;
293 static int cache_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
295 struct fuse_cache_dirhandle ch;
296 int err;
297 char **dir;
298 struct node *node;
300 pthread_mutex_lock(&cache.lock);
301 node = cache_lookup(path);
302 if (node != NULL && node->dir != NULL) {
303 time_t now = time(NULL);
304 if (node->dir_valid - now >= 0) {
305 for(dir = node->dir; *dir != NULL; dir++)
306 filler(h, *dir, 0, 0);
307 pthread_mutex_unlock(&cache.lock);
308 return 0;
311 pthread_mutex_unlock(&cache.lock);
313 ch.path = path;
314 ch.h = h;
315 ch.filler = filler;
316 ch.dir = g_ptr_array_new();
317 err = cache.next_oper->cache_getdir(path, &ch, cache_dirfill);
318 g_ptr_array_add(ch.dir, NULL);
319 dir = (char **) ch.dir->pdata;
320 if (!err)
321 cache_add_dir(path, dir);
322 else
323 g_strfreev(dir);
324 g_ptr_array_free(ch.dir, FALSE);
325 return err;
328 static int cache_unity_dirfill(fuse_cache_dirh_t ch, const char *name,
329 const struct stat *stbuf)
331 (void) stbuf;
332 return ch->filler(ch->h, name, 0, 0);
335 static int cache_unity_getdir(const char *path, fuse_dirh_t h,
336 fuse_dirfil_t filler)
338 struct fuse_cache_dirhandle ch;
339 ch.h = h;
340 ch.filler = filler;
341 return cache.next_oper->cache_getdir(path, &ch, cache_unity_dirfill);
344 static int cache_mknod(const char *path, mode_t mode, dev_t rdev)
346 int err = cache.next_oper->oper.mknod(path, mode, rdev);
347 if (!err)
348 cache_invalidate_dir(path);
349 return err;
352 static int cache_mkdir(const char *path, mode_t mode)
354 int err = cache.next_oper->oper.mkdir(path, mode);
355 if (!err)
356 cache_invalidate_dir(path);
357 return err;
360 static int cache_unlink(const char *path)
362 int err = cache.next_oper->oper.unlink(path);
363 if (!err)
364 cache_invalidate_dir(path);
365 return err;
368 static int cache_rmdir(const char *path)
370 int err = cache.next_oper->oper.rmdir(path);
371 if (!err)
372 cache_invalidate_dir(path);
373 return err;
376 static int cache_symlink(const char *from, const char *to)
378 int err = cache.next_oper->oper.symlink(from, to);
379 if (!err)
380 cache_invalidate_dir(to);
381 return err;
384 static int cache_rename(const char *from, const char *to)
386 int err = cache.next_oper->oper.rename(from, to);
387 if (!err)
388 cache_do_rename(from, to);
389 return err;
392 static int cache_link(const char *from, const char *to)
394 int err = cache.next_oper->oper.link(from, to);
395 if (!err) {
396 cache_invalidate(from);
397 cache_invalidate_dir(to);
399 return err;
402 static int cache_chmod(const char *path, mode_t mode)
404 int err = cache.next_oper->oper.chmod(path, mode);
405 if (!err)
406 cache_invalidate(path);
407 return err;
410 static int cache_chown(const char *path, uid_t uid, gid_t gid)
412 int err = cache.next_oper->oper.chown(path, uid, gid);
413 if (!err)
414 cache_invalidate(path);
415 return err;
418 static int cache_truncate(const char *path, off_t size)
420 int err = cache.next_oper->oper.truncate(path, size);
421 if (!err)
422 cache_invalidate(path);
423 return err;
426 static int cache_utime(const char *path, struct utimbuf *buf)
428 int err = cache.next_oper->oper.utime(path, buf);
429 if (!err)
430 cache_invalidate(path);
431 return err;
434 static int cache_write(const char *path, const char *buf, size_t size,
435 off_t offset, struct fuse_file_info *fi)
437 int res = cache.next_oper->oper.write(path, buf, size, offset, fi);
438 if (res >= 0)
439 cache_invalidate(path);
440 return res;
443 #if FUSE_VERSION >= 25
444 static int cache_create(const char *path, mode_t mode,
445 struct fuse_file_info *fi)
447 int err = cache.next_oper->oper.create(path, mode, fi);
448 if (!err)
449 cache_invalidate_dir(path);
450 return err;
453 static int cache_ftruncate(const char *path, off_t size,
454 struct fuse_file_info *fi)
456 int err = cache.next_oper->oper.ftruncate(path, size, fi);
457 if (!err)
458 cache_invalidate(path);
459 return err;
462 static int cache_fgetattr(const char *path, struct stat *stbuf,
463 struct fuse_file_info *fi)
465 int err = cache_get_attr(path, stbuf);
466 if (err) {
467 err = cache.next_oper->oper.fgetattr(path, stbuf, fi);
468 if (!err)
469 cache_add_attr(path, stbuf, 0);
471 return err;
473 #endif
475 static void cache_unity_fill(struct fuse_cache_operations *oper,
476 struct fuse_operations *cache_oper)
478 #if FUSE_VERSION >= 23
479 cache_oper->init = oper->oper.init;
480 #endif
481 cache_oper->getattr = oper->oper.getattr;
482 cache_oper->readlink = oper->oper.readlink;
483 cache_oper->getdir = cache_unity_getdir;
484 cache_oper->mknod = oper->oper.mknod;
485 cache_oper->mkdir = oper->oper.mkdir;
486 cache_oper->symlink = oper->oper.symlink;
487 cache_oper->unlink = oper->oper.unlink;
488 cache_oper->rmdir = oper->oper.rmdir;
489 cache_oper->rename = oper->oper.rename;
490 cache_oper->link = oper->oper.link;
491 cache_oper->chmod = oper->oper.chmod;
492 cache_oper->chown = oper->oper.chown;
493 cache_oper->truncate = oper->oper.truncate;
494 cache_oper->utime = oper->oper.utime;
495 cache_oper->open = oper->oper.open;
496 cache_oper->read = oper->oper.read;
497 cache_oper->write = oper->oper.write;
498 cache_oper->flush = oper->oper.flush;
499 cache_oper->release = oper->oper.release;
500 cache_oper->fsync = oper->oper.fsync;
501 cache_oper->statfs = oper->oper.statfs;
502 cache_oper->setxattr = oper->oper.setxattr;
503 cache_oper->getxattr = oper->oper.getxattr;
504 cache_oper->listxattr = oper->oper.listxattr;
505 cache_oper->removexattr = oper->oper.removexattr;
506 #if FUSE_VERSION >= 25
507 cache_oper->create = oper->oper.create;
508 cache_oper->ftruncate = oper->oper.ftruncate;
509 cache_oper->fgetattr = oper->oper.fgetattr;
510 #endif
513 struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
515 static struct fuse_operations cache_oper;
516 cache.next_oper = oper;
518 cache_unity_fill(oper, &cache_oper);
519 if (cache.on) {
520 cache_oper.getattr = oper->oper.getattr ? cache_getattr : NULL;
521 cache_oper.readlink = oper->oper.readlink ? cache_readlink : NULL;
522 cache_oper.getdir = oper->cache_getdir ? cache_getdir : NULL;
523 cache_oper.mknod = oper->oper.mknod ? cache_mknod : NULL;
524 cache_oper.mkdir = oper->oper.mkdir ? cache_mkdir : NULL;
525 cache_oper.symlink = oper->oper.symlink ? cache_symlink : NULL;
526 cache_oper.unlink = oper->oper.unlink ? cache_unlink : NULL;
527 cache_oper.rmdir = oper->oper.rmdir ? cache_rmdir : NULL;
528 cache_oper.rename = oper->oper.rename ? cache_rename : NULL;
529 cache_oper.link = oper->oper.link ? cache_link : NULL;
530 cache_oper.chmod = oper->oper.chmod ? cache_chmod : NULL;
531 cache_oper.chown = oper->oper.chown ? cache_chown : NULL;
532 cache_oper.truncate = oper->oper.truncate ? cache_truncate : NULL;
533 cache_oper.utime = oper->oper.utime ? cache_utime : NULL;
534 cache_oper.write = oper->oper.write ? cache_write : NULL;
535 #if FUSE_VERSION >= 25
536 cache_oper.create = oper->oper.create ? cache_create : NULL;
537 cache_oper.ftruncate = oper->oper.ftruncate ? cache_ftruncate : NULL;
538 cache_oper.fgetattr = oper->oper.fgetattr ? cache_fgetattr : NULL;
539 #endif
540 pthread_mutex_init(&cache.lock, NULL);
541 cache.table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
542 free_node);
543 if (cache.table == NULL) {
544 fprintf(stderr, "failed to create cache\n");
545 return NULL;
548 return &cache_oper;
551 static const struct fuse_opt cache_opts[] = {
552 { "cache=yes", offsetof(struct cache, on), 1 },
553 { "cache=no", offsetof(struct cache, on), 0 },
554 { "cache_timeout=%u", offsetof(struct cache, stat_timeout), 0 },
555 { "cache_timeout=%u", offsetof(struct cache, dir_timeout), 0 },
556 { "cache_timeout=%u", offsetof(struct cache, link_timeout), 0 },
557 { "cache_stat_timeout=%u", offsetof(struct cache, stat_timeout), 0 },
558 { "cache_dir_timeout=%u", offsetof(struct cache, dir_timeout), 0 },
559 { "cache_link_timeout=%u", offsetof(struct cache, link_timeout), 0 },
560 FUSE_OPT_END
563 int cache_parse_options(struct fuse_args *args)
565 cache.stat_timeout = DEFAULT_CACHE_TIMEOUT;
566 cache.dir_timeout = DEFAULT_CACHE_TIMEOUT;
567 cache.link_timeout = DEFAULT_CACHE_TIMEOUT;
568 cache.on = 1;
570 return fuse_opt_parse(args, &cache, cache_opts, NULL);