source: FTPfs/curlftpfs-0.9.1/cache.c @ 10

Last change on this file since 10 was 10, checked in by zsjheng, 16 years ago
File size: 15.2 KB
Line 
1/*
2    Caching file system proxy
3    Copyright (C) 2004  Miklos Szeredi <miklos@szeredi.hu>
4
5    This program can be distributed under the terms of the GNU GPL.
6    See the file COPYING.
7*/
8
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>
16
17#define DEFAULT_CACHE_TIMEOUT 300
18#define MAX_CACHE_SIZE 10000
19#define MIN_CACHE_CLEAN_INTERVAL 5
20#define CACHE_CLEAN_INTERVAL 60
21
22struct 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;
31};
32
33static struct cache cache;
34
35struct node {
36    struct stat stat;
37    int not_found;
38    time_t stat_valid;
39    char **dir;
40    time_t dir_valid;
41    char *link;
42    time_t link_valid;
43    time_t valid;
44};
45
46struct fuse_cache_dirhandle {
47    const char *path;
48    fuse_dirh_t h;
49    fuse_dirfil_t filler;
50    GPtrArray *dir;
51};
52
53static void free_node(gpointer node_)
54{
55    struct node *node = (struct node *) node_;
56    g_strfreev(node->dir);
57    g_free(node);
58}
59
60static int cache_clean_entry(void *key_, struct node *node, time_t *now)
61{
62    (void) key_;
63    if (*now > node->valid)
64        return TRUE;
65    else
66        return FALSE;
67}
68
69static void cache_clean(void)
70{
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;
78    }
79}
80
81static struct node *cache_lookup(const char *path)
82{
83    return (struct node *) g_hash_table_lookup(cache.table, path);
84}
85
86static void cache_purge(const char *path)
87{
88    g_hash_table_remove(cache.table, path);
89}
90
91static void cache_purge_parent(const char *path)
92{
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);
101        }
102    }
103}
104
105static void cache_invalidate(const char *path)
106{
107    pthread_mutex_lock(&cache.lock);
108    cache_purge(path);
109    pthread_mutex_unlock(&cache.lock);
110}
111
112static void cache_invalidate_dir(const char *path)
113{
114    pthread_mutex_lock(&cache.lock);
115    cache_purge(path);
116    cache_purge_parent(path);
117    pthread_mutex_unlock(&cache.lock);
118}
119
120static void cache_do_rename(const char *from, const char *to)
121{
122    pthread_mutex_lock(&cache.lock);
123    cache_purge(from);
124    cache_purge(to);
125    cache_purge_parent(from);
126    cache_purge_parent(to);
127    pthread_mutex_unlock(&cache.lock);
128}
129
130static struct node *cache_get(const char *path)
131{
132    struct node *node = cache_lookup(path);
133    if (node == NULL) {
134        char *pathcopy = g_strdup(path);
135        node = g_new0(struct node, 1);
136        g_hash_table_insert(cache.table, pathcopy, node);
137    }
138    return node;
139}
140
141void cache_add_attr(const char *path, const struct stat *stbuf)
142{
143    struct node *node;
144    time_t now;
145
146    pthread_mutex_lock(&cache.lock);
147    node = cache_get(path);
148    now = time(NULL);
149    if (stbuf) {
150      node->stat = *stbuf;
151      node->not_found = 0;
152    } else {
153      node->not_found = 1;
154    }
155    node->stat_valid = time(NULL) + cache.stat_timeout;
156    if (node->stat_valid > node->valid)
157        node->valid = node->stat_valid;
158    cache_clean();
159    pthread_mutex_unlock(&cache.lock);
160}
161
162void cache_add_dir(const char *path, char **dir)
163{
164    struct node *node;
165    time_t now;
166
167    pthread_mutex_lock(&cache.lock);
168    node = cache_get(path);
169    now = time(NULL);
170    g_strfreev(node->dir);
171    node->dir = dir;
172    node->not_found = 0;
173    node->dir_valid = time(NULL) + cache.dir_timeout;
174    if (node->dir_valid > node->valid)
175        node->valid = node->dir_valid;
176    cache_clean();
177    pthread_mutex_unlock(&cache.lock);
178}
179
180static size_t my_strnlen(const char *s, size_t maxsize)
181{
182    const char *p;
183    for (p = s; maxsize && *p; maxsize--, p++);
184    return p - s;
185}
186
187void cache_add_link(const char *path, const char *link, size_t size)
188{
189    struct node *node;
190    time_t now;
191
192    pthread_mutex_lock(&cache.lock);
193    node = cache_get(path);
194    now = time(NULL);
195    g_free(node->link);
196    node->link = g_strndup(link, my_strnlen(link, size-1));
197    node->not_found = 0;
198    node->link_valid = time(NULL) + cache.link_timeout;
199    if (node->link_valid > node->valid)
200        node->valid = node->link_valid;
201    cache_clean();
202    pthread_mutex_unlock(&cache.lock);
203}
204
205static int cache_get_attr(const char *path, struct stat *stbuf)
206{
207    struct node *node;
208    int err = -EAGAIN;
209    pthread_mutex_lock(&cache.lock);
210    node = cache_lookup(path);
211    if (node != NULL) {
212        time_t now = time(NULL);
213        if (node->stat_valid - now >= 0) {
214            if (node->not_found) {
215              err = -ENOENT;
216            } else {
217              *stbuf = node->stat;
218              err = 0;
219            }
220        }
221    }
222    pthread_mutex_unlock(&cache.lock);
223    return err;
224}
225
226static int cache_getattr(const char *path, struct stat *stbuf)
227{
228    int err = cache_get_attr(path, stbuf);
229    if (err == -EAGAIN) {
230        err = cache.next_oper->oper.getattr(path, stbuf);
231        if (!err)
232            cache_add_attr(path, stbuf);
233        else if (err == -ENOENT)
234            cache_add_attr(path, NULL);
235    }
236    return err;
237}
238
239static int cache_readlink(const char *path, char *buf, size_t size)
240{
241    struct node *node;
242    int err;
243
244    pthread_mutex_lock(&cache.lock);
245    node = cache_lookup(path);
246    if (node != NULL) {
247        time_t now = time(NULL);
248        if (node->link_valid - now >= 0) {
249            strncpy(buf, node->link, size-1);
250            buf[size-1] = '\0';
251            pthread_mutex_unlock(&cache.lock);
252            return 0;
253        }
254    }
255    pthread_mutex_unlock(&cache.lock);
256    err = cache.next_oper->oper.readlink(path, buf, size);
257    if (!err)
258        cache_add_link(path, buf, size);
259
260    return err;
261}
262
263static int cache_dirfill(fuse_cache_dirh_t ch, const char *name,
264                         const struct stat *stbuf)
265{
266    int err = ch->filler(ch->h, name, 0, 0);
267    if (!err) {
268        char *fullpath;
269        g_ptr_array_add(ch->dir, g_strdup(name));
270        fullpath = g_strdup_printf("%s/%s", !ch->path[1] ? "" : ch->path, name);
271        cache_add_attr(fullpath, stbuf);
272        g_free(fullpath);
273    }
274    return err;
275}
276
277static int cache_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
278{
279    struct fuse_cache_dirhandle ch;
280    int err;
281    char **dir;
282    struct node *node;
283
284    pthread_mutex_lock(&cache.lock);
285    node = cache_lookup(path);
286    if (node != NULL && node->dir != NULL) {
287        time_t now = time(NULL);
288        if (node->dir_valid - now >= 0) {
289            for(dir = node->dir; *dir != NULL; dir++)
290                filler(h, *dir, 0, 0);
291            pthread_mutex_unlock(&cache.lock);
292            return 0;
293        }
294    }
295    pthread_mutex_unlock(&cache.lock);
296
297    ch.path = path;
298    ch.h = h;
299    ch.filler = filler;
300    ch.dir = g_ptr_array_new();
301    err = cache.next_oper->cache_getdir(path, &ch, cache_dirfill);
302    g_ptr_array_add(ch.dir, NULL);
303    dir = (char **) ch.dir->pdata;
304    if (!err)
305        cache_add_dir(path, dir);
306    else
307        g_strfreev(dir);
308    g_ptr_array_free(ch.dir, FALSE);
309    return err;
310}
311
312static int cache_unity_dirfill(fuse_cache_dirh_t ch, const char *name,
313                               const struct stat *stbuf)
314{
315    (void) stbuf;
316    return ch->filler(ch->h, name, 0, 0);
317}
318
319static int cache_unity_getdir(const char *path, fuse_dirh_t h,
320                              fuse_dirfil_t filler)
321{
322    struct fuse_cache_dirhandle ch;
323    ch.h = h;
324    ch.filler = filler;
325    return cache.next_oper->cache_getdir(path, &ch, cache_unity_dirfill);
326}
327
328static int cache_mknod(const char *path, mode_t mode, dev_t rdev)
329{
330    int err = cache.next_oper->oper.mknod(path, mode, rdev);
331    if (!err)
332        cache_invalidate_dir(path);
333    return err;
334}
335
336static int cache_mkdir(const char *path, mode_t mode)
337{
338    int err = cache.next_oper->oper.mkdir(path, mode);
339    if (!err)
340        cache_invalidate_dir(path);
341    return err;
342}
343
344static int cache_unlink(const char *path)
345{
346    int err = cache.next_oper->oper.unlink(path);
347    if (!err)
348        cache_invalidate_dir(path);
349    return err;
350}
351
352static int cache_rmdir(const char *path)
353{
354    int err = cache.next_oper->oper.rmdir(path);
355    if (!err)
356        cache_invalidate_dir(path);
357    return err;
358}
359
360static int cache_symlink(const char *from, const char *to)
361{
362    int err = cache.next_oper->oper.symlink(from, to);
363    if (!err)
364        cache_invalidate_dir(to);
365    return err;
366}
367
368static int cache_rename(const char *from, const char *to)
369{
370    int err = cache.next_oper->oper.rename(from, to);
371    if (!err)
372        cache_do_rename(from, to);
373    return err;
374}
375
376static int cache_link(const char *from, const char *to)
377{
378    int err = cache.next_oper->oper.link(from, to);
379    if (!err) {
380        cache_invalidate(from);
381        cache_invalidate_dir(to);
382    }
383    return err;
384}
385
386static int cache_chmod(const char *path, mode_t mode)
387{
388    int err = cache.next_oper->oper.chmod(path, mode);
389    if (!err)
390        cache_invalidate(path);
391    return err;
392}
393
394static int cache_chown(const char *path, uid_t uid, gid_t gid)
395{
396    int err = cache.next_oper->oper.chown(path, uid, gid);
397    if (!err)
398        cache_invalidate(path);
399    return err;
400}
401
402static int cache_truncate(const char *path, off_t size)
403{
404    int err = cache.next_oper->oper.truncate(path, size);
405    if (!err)
406        cache_invalidate(path);
407    return err;
408}
409
410static int cache_utime(const char *path, struct utimbuf *buf)
411{
412    int err = cache.next_oper->oper.utime(path, buf);
413    if (!err)
414        cache_invalidate(path);
415    return err;
416}
417
418static int cache_write(const char *path, const char *buf, size_t size,
419                       off_t offset, struct fuse_file_info *fi)
420{
421    int res = cache.next_oper->oper.write(path, buf, size, offset, fi);
422    if (res >= 0)
423        cache_invalidate(path);
424    return res;
425}
426
427#if FUSE_VERSION >= 25
428static int cache_create(const char *path, mode_t mode,
429                        struct fuse_file_info *fi)
430{
431    int err = cache.next_oper->oper.create(path, mode, fi);
432    if (!err)
433        cache_invalidate_dir(path);
434    return err;
435}
436
437static int cache_ftruncate(const char *path, off_t size,
438                           struct fuse_file_info *fi)
439{
440    int err = cache.next_oper->oper.ftruncate(path, size, fi);
441    if (!err)
442        cache_invalidate(path);
443    return err;
444}
445
446static int cache_fgetattr(const char *path, struct stat *stbuf,
447                          struct fuse_file_info *fi)
448{
449    int err = cache_get_attr(path, stbuf);
450    if (err == -EAGAIN) {
451        err = cache.next_oper->oper.fgetattr(path, stbuf, fi);
452        if (!err)
453            cache_add_attr(path, stbuf);
454        else if (err == -ENOENT)
455            cache_add_attr(path, NULL);
456    }
457    return err;
458}
459#endif
460
461static void cache_unity_fill(struct fuse_cache_operations *oper,
462                             struct fuse_operations *cache_oper)
463{
464#if FUSE_VERSION >= 23
465    cache_oper->init        = oper->oper.init;
466#endif
467    cache_oper->getattr     = oper->oper.getattr;
468    cache_oper->readlink    = oper->oper.readlink;
469    cache_oper->getdir      = cache_unity_getdir;
470    cache_oper->mknod       = oper->oper.mknod;
471    cache_oper->mkdir       = oper->oper.mkdir;
472    cache_oper->symlink     = oper->oper.symlink;
473    cache_oper->unlink      = oper->oper.unlink;
474    cache_oper->rmdir       = oper->oper.rmdir;
475    cache_oper->rename      = oper->oper.rename;
476    cache_oper->link        = oper->oper.link;
477    cache_oper->chmod       = oper->oper.chmod;
478    cache_oper->chown       = oper->oper.chown;
479    cache_oper->truncate    = oper->oper.truncate;
480    cache_oper->utime       = oper->oper.utime;
481    cache_oper->open        = oper->oper.open;
482    cache_oper->read        = oper->oper.read;
483    cache_oper->write       = oper->oper.write;
484    cache_oper->flush       = oper->oper.flush;
485    cache_oper->release     = oper->oper.release;
486    cache_oper->fsync       = oper->oper.fsync;
487    cache_oper->statfs      = oper->oper.statfs;
488    cache_oper->setxattr    = oper->oper.setxattr;
489    cache_oper->getxattr    = oper->oper.getxattr;
490    cache_oper->listxattr   = oper->oper.listxattr;
491    cache_oper->removexattr = oper->oper.removexattr;
492#if FUSE_VERSION >= 25
493    cache_oper->create      = oper->oper.create;
494    cache_oper->ftruncate   = oper->oper.ftruncate;
495    cache_oper->fgetattr    = oper->oper.fgetattr;
496#endif
497}
498
499struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
500{
501    static struct fuse_operations cache_oper;
502    cache.next_oper = oper;
503
504    cache_unity_fill(oper, &cache_oper);
505    if (cache.on) {
506        cache_oper.getattr  = oper->oper.getattr ? cache_getattr : NULL;
507        cache_oper.readlink = oper->oper.readlink ? cache_readlink : NULL;
508        cache_oper.getdir   = oper->cache_getdir ? cache_getdir : NULL;
509        cache_oper.mknod    = oper->oper.mknod ? cache_mknod : NULL;
510        cache_oper.mkdir    = oper->oper.mkdir ? cache_mkdir : NULL;
511        cache_oper.symlink  = oper->oper.symlink ? cache_symlink : NULL;
512        cache_oper.unlink   = oper->oper.unlink ? cache_unlink : NULL;
513        cache_oper.rmdir    = oper->oper.rmdir ? cache_rmdir : NULL;
514        cache_oper.rename   = oper->oper.rename ? cache_rename : NULL;
515        cache_oper.link     = oper->oper.link ? cache_link : NULL;
516        cache_oper.chmod    = oper->oper.chmod ? cache_chmod : NULL;
517        cache_oper.chown    = oper->oper.chown ? cache_chown : NULL;
518        cache_oper.truncate = oper->oper.truncate ? cache_truncate : NULL;
519        cache_oper.utime    = oper->oper.utime ? cache_utime : NULL;
520        cache_oper.write    = oper->oper.write ? cache_write : NULL;
521#if FUSE_VERSION >= 25
522        cache_oper.create   = oper->oper.create ? cache_create : NULL;
523        cache_oper.ftruncate = oper->oper.ftruncate ? cache_ftruncate : NULL;
524        cache_oper.fgetattr = oper->oper.fgetattr ? cache_fgetattr : NULL;
525#endif
526        pthread_mutex_init(&cache.lock, NULL);
527        cache.table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
528                                            free_node);
529        if (cache.table == NULL) {
530            fprintf(stderr, "failed to create cache\n");
531            return NULL;
532        }
533    }
534    return &cache_oper;
535}
536
537static const struct fuse_opt cache_opts[] = {
538    { "cache=yes", offsetof(struct cache, on), 1 },
539    { "cache=no", offsetof(struct cache, on), 0 },
540    { "cache_timeout=%u", offsetof(struct cache, stat_timeout), 0 },
541    { "cache_timeout=%u", offsetof(struct cache, dir_timeout), 0 },
542    { "cache_timeout=%u", offsetof(struct cache, link_timeout), 0 },
543    { "cache_stat_timeout=%u", offsetof(struct cache, stat_timeout), 0 },
544    { "cache_dir_timeout=%u", offsetof(struct cache, dir_timeout), 0 },
545    { "cache_link_timeout=%u", offsetof(struct cache, link_timeout), 0 },
546    FUSE_OPT_END
547};
548
549int cache_parse_options(struct fuse_args *args)
550{
551    cache.stat_timeout = DEFAULT_CACHE_TIMEOUT;
552    cache.dir_timeout = DEFAULT_CACHE_TIMEOUT;
553    cache.link_timeout = DEFAULT_CACHE_TIMEOUT;
554    cache.on = 1;
555
556    return fuse_opt_parse(args, &cache, cache_opts, NULL);
557}
Note: See TracBrowser for help on using the repository browser.