1 /* guestmount - mount guests using libguestfs and FUSE
2 * Copyright (C) 2009 Red Hat Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * Derived from the example program 'fusexmp.c':
19 * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
21 * This program can be distributed under the terms of the GNU GPL.
22 * See the file COPYING.
33 #include <sys/types.h>
43 extern int dir_cache_timeout;
46 bad_cast (char const *s)
51 /* Note on attribute caching: FUSE can cache filesystem attributes for
52 * short periods of time (configurable via -o attr_timeout). It
53 * doesn't cache xattrs, and in any case FUSE caching doesn't solve
54 * the problem that we have to make a series of guestfs_lstat and
55 * guestfs_lgetxattr calls when we first list a directory (thus, many
58 * For this reason, we also implement a readdir cache here which is
59 * invoked when a readdir call is made. readdir is modified so that
60 * as well as reading the directory, it also requests all the stat
61 * structures, xattrs and readlinks of all entries in the directory,
62 * and these are added to the cache here (for a short, configurable
63 * period of time) in anticipation that they will be needed
64 * immediately afterwards, which is usually the case when the user is
65 * doing an "ls"-like operation.
67 * You can still use FUSE attribute caching on top of this mechanism
71 struct lsc_entry { /* lstat cache entry */
72 char *pathname; /* full path to the file */
73 time_t timeout; /* when this entry expires */
74 struct stat statbuf; /* statbuf */
77 struct xac_entry { /* xattr cache entry */
78 /* NB first two fields must be same as lsc_entry */
79 char *pathname; /* full path to the file */
80 time_t timeout; /* when this entry expires */
81 struct guestfs_xattr_list *xattrs;
84 struct rlc_entry { /* readlink cache entry */
85 /* NB first two fields must be same as lsc_entry */
86 char *pathname; /* full path to the file */
87 time_t timeout; /* when this entry expires */
92 gen_hash (void const *x, size_t table_size)
94 struct lsc_entry const *p = x;
95 return hash_pjw (p->pathname, table_size);
99 gen_compare (void const *x, void const *y)
101 struct lsc_entry const *a = x;
102 struct lsc_entry const *b = y;
103 return STREQ (a->pathname, b->pathname);
110 struct lsc_entry *p = x;
121 struct xac_entry *p = x;
123 guestfs_free_xattr_list (p->xattrs);
132 struct rlc_entry *p = x;
139 static Hash_table *lsc_ht, *xac_ht, *rlc_ht;
142 init_dir_caches (void)
144 lsc_ht = hash_initialize (1024, NULL, gen_hash, gen_compare, lsc_free);
145 xac_ht = hash_initialize (1024, NULL, gen_hash, gen_compare, xac_free);
146 rlc_ht = hash_initialize (1024, NULL, gen_hash, gen_compare, rlc_free);
147 if (!lsc_ht || !xac_ht || !rlc_ht) {
148 fprintf (stderr, "guestmount: could not initialize dir cache hashtables\n");
154 free_dir_caches (void)
161 struct gen_remove_data {
164 Hash_data_freer freer;
168 gen_remove_if_expired (void *x, void *data)
170 /* XXX hash_do_for_each was observed calling this function
174 struct lsc_entry *p = x;
175 struct gen_remove_data *d = data;
177 if (p->timeout < d->now) {
179 fprintf (stderr, "dir cache: expiring entry %p (%s)\n",
181 d->freer (hash_delete (d->ht, x));
189 gen_remove_all_expired (Hash_table *ht, Hash_data_freer freer, time_t now)
191 struct gen_remove_data data;
196 /* Careful reading of the documentation to hash _seems_ to indicate
197 * that this is safe, _provided_ we use the default thresholds (in
198 * particular, no shrink threshold).
200 hash_do_for_each (ht, gen_remove_if_expired, &data);
204 dir_cache_remove_all_expired (time_t now)
206 gen_remove_all_expired (lsc_ht, lsc_free, now);
207 gen_remove_all_expired (xac_ht, xac_free, now);
208 gen_remove_all_expired (rlc_ht, rlc_free, now);
212 gen_replace (Hash_table *ht, struct lsc_entry *new_entry, Hash_data_freer freer)
214 struct lsc_entry *old_entry;
216 old_entry = hash_delete (ht, new_entry);
219 if (verbose && old_entry)
220 fprintf (stderr, "dir cache: this entry replaced old entry %p (%s)\n",
221 old_entry, old_entry->pathname);
223 old_entry = hash_insert (ht, new_entry);
224 if (old_entry == NULL) {
225 perror ("hash_insert");
229 assert (old_entry == new_entry);
235 lsc_insert (const char *path, const char *name, time_t now,
236 struct stat const *statbuf)
238 struct lsc_entry *entry;
240 entry = malloc (sizeof *entry);
246 size_t len = strlen (path) + strlen (name) + 2;
247 entry->pathname = malloc (len);
248 if (entry->pathname == NULL) {
253 if (STREQ (path, "/"))
254 snprintf (entry->pathname, len, "/%s", name);
256 snprintf (entry->pathname, len, "%s/%s", path, name);
258 memcpy (&entry->statbuf, statbuf, sizeof entry->statbuf);
260 entry->timeout = now + dir_cache_timeout;
263 fprintf (stderr, "dir cache: inserting lstat entry %p (%s)\n",
264 entry, entry->pathname);
266 return gen_replace (lsc_ht, entry, lsc_free);
270 xac_insert (const char *path, const char *name, time_t now,
271 struct guestfs_xattr_list *xattrs)
273 struct xac_entry *entry;
275 entry = malloc (sizeof *entry);
281 size_t len = strlen (path) + strlen (name) + 2;
282 entry->pathname = malloc (len);
283 if (entry->pathname == NULL) {
288 if (STREQ (path, "/"))
289 snprintf (entry->pathname, len, "/%s", name);
291 snprintf (entry->pathname, len, "%s/%s", path, name);
293 entry->xattrs = xattrs;
295 entry->timeout = now + dir_cache_timeout;
298 fprintf (stderr, "dir cache: inserting xattr entry %p (%s)\n",
299 entry, entry->pathname);
301 return gen_replace (xac_ht, (struct lsc_entry *) entry, xac_free);
305 rlc_insert (const char *path, const char *name, time_t now,
308 struct rlc_entry *entry;
310 entry = malloc (sizeof *entry);
316 size_t len = strlen (path) + strlen (name) + 2;
317 entry->pathname = malloc (len);
318 if (entry->pathname == NULL) {
323 if (STREQ (path, "/"))
324 snprintf (entry->pathname, len, "/%s", name);
326 snprintf (entry->pathname, len, "%s/%s", path, name);
330 entry->timeout = now + dir_cache_timeout;
333 fprintf (stderr, "dir cache: inserting readlink entry %p (%s)\n",
334 entry, entry->pathname);
336 return gen_replace (rlc_ht, (struct lsc_entry *) entry, rlc_free);
340 lsc_lookup (const char *pathname)
342 const struct lsc_entry key = { .pathname = bad_cast (pathname) };
343 struct lsc_entry *entry;
348 entry = hash_lookup (lsc_ht, &key);
349 if (entry && entry->timeout >= now)
350 return &entry->statbuf;
355 const struct guestfs_xattr_list *
356 xac_lookup (const char *pathname)
358 const struct xac_entry key = { .pathname = bad_cast (pathname) };
359 struct xac_entry *entry;
364 entry = hash_lookup (xac_ht, &key);
365 if (entry && entry->timeout >= now)
366 return entry->xattrs;
372 rlc_lookup (const char *pathname)
374 const struct rlc_entry key = { .pathname = bad_cast (pathname) };
375 struct rlc_entry *entry;
380 entry = hash_lookup (rlc_ht, &key);
381 if (entry && entry->timeout >= now)
388 lsc_remove (Hash_table *ht, const char *pathname, Hash_data_freer freer)
390 const struct lsc_entry key = { .pathname = bad_cast (pathname) };
391 struct lsc_entry *entry;
393 entry = hash_delete (ht, &key);
396 fprintf (stderr, "dir cache: invalidating entry %p (%s)\n",
397 entry, entry->pathname);
403 dir_cache_invalidate (const char *path)
405 lsc_remove (lsc_ht, path, lsc_free);
406 lsc_remove (xac_ht, path, xac_free);
407 lsc_remove (rlc_ht, path, rlc_free);