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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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>
40 #include "guestmount.h"
43 /* Note on attribute caching: FUSE can cache filesystem attributes for
44 * short periods of time (configurable via -o attr_timeout). It
45 * doesn't cache xattrs, and in any case FUSE caching doesn't solve
46 * the problem that we have to make a series of guestfs_lstat and
47 * guestfs_lgetxattr calls when we first list a directory (thus, many
50 * For this reason, we also implement a readdir cache here which is
51 * invoked when a readdir call is made. readdir is modified so that
52 * as well as reading the directory, it also requests all the stat
53 * structures, xattrs and readlinks of all entries in the directory,
54 * and these are added to the cache here (for a short, configurable
55 * period of time) in anticipation that they will be needed
56 * immediately afterwards, which is usually the case when the user is
57 * doing an "ls"-like operation.
59 * You can still use FUSE attribute caching on top of this mechanism
63 struct lsc_entry { /* lstat cache entry */
64 char *pathname; /* full path to the file */
65 time_t timeout; /* when this entry expires */
66 struct stat statbuf; /* statbuf */
69 struct xac_entry { /* xattr cache entry */
70 /* NB first two fields must be same as lsc_entry */
71 char *pathname; /* full path to the file */
72 time_t timeout; /* when this entry expires */
73 struct guestfs_xattr_list *xattrs;
76 struct rlc_entry { /* readlink cache entry */
77 /* NB first two fields must be same as lsc_entry */
78 char *pathname; /* full path to the file */
79 time_t timeout; /* when this entry expires */
84 gen_hash (void const *x, size_t table_size)
86 struct lsc_entry const *p = x;
87 return hash_pjw (p->pathname, table_size);
91 gen_compare (void const *x, void const *y)
93 struct lsc_entry const *a = x;
94 struct lsc_entry const *b = y;
95 return STREQ (a->pathname, b->pathname);
102 struct lsc_entry *p = x;
113 struct xac_entry *p = x;
115 guestfs_free_xattr_list (p->xattrs);
124 struct rlc_entry *p = x;
131 static Hash_table *lsc_ht, *xac_ht, *rlc_ht;
134 init_dir_caches (void)
136 lsc_ht = hash_initialize (1024, NULL, gen_hash, gen_compare, lsc_free);
137 xac_ht = hash_initialize (1024, NULL, gen_hash, gen_compare, xac_free);
138 rlc_ht = hash_initialize (1024, NULL, gen_hash, gen_compare, rlc_free);
139 if (!lsc_ht || !xac_ht || !rlc_ht) {
140 fprintf (stderr, "guestmount: could not initialize dir cache hashtables\n");
146 free_dir_caches (void)
153 struct gen_remove_data {
156 Hash_data_freer freer;
160 gen_remove_if_expired (void *x, void *data)
162 /* XXX hash_do_for_each was observed calling this function
166 struct lsc_entry *p = x;
167 struct gen_remove_data *d = data;
169 if (p->timeout < d->now) {
171 fprintf (stderr, "dir cache: expiring entry %p (%s)\n",
173 d->freer (hash_delete (d->ht, x));
181 gen_remove_all_expired (Hash_table *ht, Hash_data_freer freer, time_t now)
183 struct gen_remove_data data;
188 /* Careful reading of the documentation to hash _seems_ to indicate
189 * that this is safe, _provided_ we use the default thresholds (in
190 * particular, no shrink threshold).
192 hash_do_for_each (ht, gen_remove_if_expired, &data);
196 dir_cache_remove_all_expired (time_t now)
198 gen_remove_all_expired (lsc_ht, lsc_free, now);
199 gen_remove_all_expired (xac_ht, xac_free, now);
200 gen_remove_all_expired (rlc_ht, rlc_free, now);
204 gen_replace (Hash_table *ht, struct lsc_entry *new_entry, Hash_data_freer freer)
206 struct lsc_entry *old_entry;
208 old_entry = hash_delete (ht, new_entry);
211 if (verbose && old_entry)
212 fprintf (stderr, "dir cache: this entry replaced old entry %p (%s)\n",
213 old_entry, old_entry->pathname);
215 old_entry = hash_insert (ht, new_entry);
216 if (old_entry == NULL) {
217 perror ("hash_insert");
221 assert (old_entry == new_entry);
227 lsc_insert (const char *path, const char *name, time_t now,
228 struct stat const *statbuf)
230 struct lsc_entry *entry;
232 entry = malloc (sizeof *entry);
238 size_t len = strlen (path) + strlen (name) + 2;
239 entry->pathname = malloc (len);
240 if (entry->pathname == NULL) {
245 if (STREQ (path, "/"))
246 snprintf (entry->pathname, len, "/%s", name);
248 snprintf (entry->pathname, len, "%s/%s", path, name);
250 memcpy (&entry->statbuf, statbuf, sizeof entry->statbuf);
252 entry->timeout = now + dir_cache_timeout;
255 fprintf (stderr, "dir cache: inserting lstat entry %p (%s)\n",
256 entry, entry->pathname);
258 return gen_replace (lsc_ht, entry, lsc_free);
262 xac_insert (const char *path, const char *name, time_t now,
263 struct guestfs_xattr_list *xattrs)
265 struct xac_entry *entry;
267 entry = malloc (sizeof *entry);
273 size_t len = strlen (path) + strlen (name) + 2;
274 entry->pathname = malloc (len);
275 if (entry->pathname == NULL) {
280 if (STREQ (path, "/"))
281 snprintf (entry->pathname, len, "/%s", name);
283 snprintf (entry->pathname, len, "%s/%s", path, name);
285 entry->xattrs = xattrs;
287 entry->timeout = now + dir_cache_timeout;
290 fprintf (stderr, "dir cache: inserting xattr entry %p (%s)\n",
291 entry, entry->pathname);
293 return gen_replace (xac_ht, (struct lsc_entry *) entry, xac_free);
297 rlc_insert (const char *path, const char *name, time_t now,
300 struct rlc_entry *entry;
302 entry = malloc (sizeof *entry);
308 size_t len = strlen (path) + strlen (name) + 2;
309 entry->pathname = malloc (len);
310 if (entry->pathname == NULL) {
315 if (STREQ (path, "/"))
316 snprintf (entry->pathname, len, "/%s", name);
318 snprintf (entry->pathname, len, "%s/%s", path, name);
322 entry->timeout = now + dir_cache_timeout;
325 fprintf (stderr, "dir cache: inserting readlink entry %p (%s)\n",
326 entry, entry->pathname);
328 return gen_replace (rlc_ht, (struct lsc_entry *) entry, rlc_free);
332 lsc_lookup (const char *pathname)
334 const struct lsc_entry key = { .pathname = bad_cast (pathname) };
335 struct lsc_entry *entry;
340 entry = hash_lookup (lsc_ht, &key);
341 if (entry && entry->timeout >= now)
342 return &entry->statbuf;
347 const struct guestfs_xattr_list *
348 xac_lookup (const char *pathname)
350 const struct xac_entry key = { .pathname = bad_cast (pathname) };
351 struct xac_entry *entry;
356 entry = hash_lookup (xac_ht, &key);
357 if (entry && entry->timeout >= now)
358 return entry->xattrs;
364 rlc_lookup (const char *pathname)
366 const struct rlc_entry key = { .pathname = bad_cast (pathname) };
367 struct rlc_entry *entry;
372 entry = hash_lookup (rlc_ht, &key);
373 if (entry && entry->timeout >= now)
380 lsc_remove (Hash_table *ht, const char *pathname, Hash_data_freer freer)
382 const struct lsc_entry key = { .pathname = bad_cast (pathname) };
383 struct lsc_entry *entry;
385 entry = hash_delete (ht, &key);
387 if (verbose && entry)
388 fprintf (stderr, "dir cache: invalidating entry %p (%s)\n",
389 entry, entry->pathname);
395 dir_cache_invalidate (const char *path)
397 lsc_remove (lsc_ht, path, lsc_free);
398 lsc_remove (xac_ht, path, xac_free);
399 lsc_remove (rlc_ht, path, rlc_free);