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.
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
24 unsigned stat_timeout
;
26 unsigned link_timeout
;
27 struct fuse_cache_operations
*next_oper
;
33 static struct cache cache
;
46 struct fuse_cache_dirhandle
{
53 static void free_node(gpointer node_
)
55 struct node
*node
= (struct node
*) node_
;
56 g_strfreev(node
->dir
);
60 static int cache_clean_entry(void *key_
, struct node
*node
, time_t *now
)
63 if (*now
> node
->valid
)
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
, '/');
96 g_hash_table_remove(cache
.table
, "/");
98 char *parent
= g_strndup(path
, s
- path
);
105 void cache_invalidate_weak(const char *path
)
109 pthread_mutex_lock(&cache
.lock
);
110 n
= (struct node
*) g_hash_table_lookup(cache
.table
, path
);
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
);
122 pthread_mutex_unlock(&cache
.lock
);
125 static void cache_invalidate_dir(const char *path
)
127 pthread_mutex_lock(&cache
.lock
);
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
)
136 if (strncmp(key
, path
, strlen(path
)) == 0)
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
,
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
);
158 char *pathcopy
= g_strdup(path
);
159 node
= g_new0(struct node
, 1);
160 g_hash_table_insert(cache
.table
, pathcopy
, node
);
165 void cache_add_attr(const char *path
, const struct stat
*stbuf
, int weak
)
170 pthread_mutex_lock(&cache
.lock
);
171 node
= cache_get(path
);
175 node
->stat_valid
= time(NULL
) + cache
.stat_timeout
;
176 if (node
->stat_valid
> node
->valid
)
177 node
->valid
= node
->stat_valid
;
179 pthread_mutex_unlock(&cache
.lock
);
182 static void cache_add_dir(const char *path
, char **dir
)
187 pthread_mutex_lock(&cache
.lock
);
188 node
= cache_get(path
);
190 g_strfreev(node
->dir
);
192 node
->dir_valid
= time(NULL
) + cache
.dir_timeout
;
193 if (node
->dir_valid
> node
->valid
)
194 node
->valid
= node
->dir_valid
;
196 pthread_mutex_unlock(&cache
.lock
);
199 static size_t my_strnlen(const char *s
, size_t maxsize
)
202 for (p
= s
; maxsize
&& *p
; maxsize
--, p
++);
206 static void cache_add_link(const char *path
, const char *link
, size_t size
)
211 pthread_mutex_lock(&cache
.lock
);
212 node
= cache_get(path
);
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
;
220 pthread_mutex_unlock(&cache
.lock
);
223 static int cache_get_attr(const char *path
, struct stat
*stbuf
)
227 pthread_mutex_lock(&cache
.lock
);
228 node
= cache_lookup(path
);
230 time_t now
= time(NULL
);
231 if (node
->stat_valid
- now
>= 0) {
236 pthread_mutex_unlock(&cache
.lock
);
240 static int cache_getattr(const char *path
, struct stat
*stbuf
)
242 int err
= cache_get_attr(path
, stbuf
);
244 err
= cache
.next_oper
->oper
.getattr(path
, stbuf
);
246 cache_add_attr(path
, stbuf
, 0);
248 cache_invalidate_weak(path
);
253 static int cache_readlink(const char *path
, char *buf
, size_t size
)
258 pthread_mutex_lock(&cache
.lock
);
259 node
= cache_lookup(path
);
261 time_t now
= time(NULL
);
262 if (node
->link_valid
- now
>= 0) {
263 strncpy(buf
, node
->link
, size
-1);
265 pthread_mutex_unlock(&cache
.lock
);
269 pthread_mutex_unlock(&cache
.lock
);
270 err
= cache
.next_oper
->oper
.readlink(path
, buf
, size
);
272 cache_add_link(path
, buf
, size
);
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);
282 g_ptr_array_add(ch
->dir
, g_strdup(name
));
283 if (stbuf
->st_mode
& S_IFMT
) {
285 g_strdup_printf("%s/%s", !ch
->path
[1] ? "" : ch
->path
, name
);
286 cache_add_attr(fullpath
, stbuf
, 1);
293 static int cache_getdir(const char *path
, fuse_dirh_t h
, fuse_dirfil_t filler
)
295 struct fuse_cache_dirhandle ch
;
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
);
311 pthread_mutex_unlock(&cache
.lock
);
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
;
321 cache_add_dir(path
, dir
);
324 g_ptr_array_free(ch
.dir
, FALSE
);
328 static int cache_unity_dirfill(fuse_cache_dirh_t ch
, const char *name
,
329 const struct stat
*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
;
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
);
348 cache_invalidate_dir(path
);
352 static int cache_mkdir(const char *path
, mode_t mode
)
354 int err
= cache
.next_oper
->oper
.mkdir(path
, mode
);
356 cache_invalidate_dir(path
);
360 static int cache_unlink(const char *path
)
362 int err
= cache
.next_oper
->oper
.unlink(path
);
364 cache_invalidate_dir(path
);
368 static int cache_rmdir(const char *path
)
370 int err
= cache
.next_oper
->oper
.rmdir(path
);
372 cache_invalidate_dir(path
);
376 static int cache_symlink(const char *from
, const char *to
)
378 int err
= cache
.next_oper
->oper
.symlink(from
, to
);
380 cache_invalidate_dir(to
);
384 static int cache_rename(const char *from
, const char *to
)
386 int err
= cache
.next_oper
->oper
.rename(from
, to
);
388 cache_do_rename(from
, to
);
392 static int cache_link(const char *from
, const char *to
)
394 int err
= cache
.next_oper
->oper
.link(from
, to
);
396 cache_invalidate(from
);
397 cache_invalidate_dir(to
);
402 static int cache_chmod(const char *path
, mode_t mode
)
404 int err
= cache
.next_oper
->oper
.chmod(path
, mode
);
406 cache_invalidate(path
);
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
);
414 cache_invalidate(path
);
418 static int cache_truncate(const char *path
, off_t size
)
420 int err
= cache
.next_oper
->oper
.truncate(path
, size
);
422 cache_invalidate(path
);
426 static int cache_utime(const char *path
, struct utimbuf
*buf
)
428 int err
= cache
.next_oper
->oper
.utime(path
, buf
);
430 cache_invalidate(path
);
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
);
439 cache_invalidate(path
);
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
);
449 cache_invalidate_dir(path
);
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
);
458 cache_invalidate(path
);
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
);
467 err
= cache
.next_oper
->oper
.fgetattr(path
, stbuf
, fi
);
469 cache_add_attr(path
, stbuf
, 0);
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
;
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
;
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
);
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
;
540 pthread_mutex_init(&cache
.lock
, NULL
);
541 cache
.table
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
,
543 if (cache
.table
== NULL
) {
544 fprintf(stderr
, "failed to create cache\n");
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 },
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
;
570 return fuse_opt_parse(args
, &cache
, cache_opts
, NULL
);