From c6f09fac0666260587f95bdfee3c20c9166dae94 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 14 Dec 2011 08:44:41 +0000 Subject: New APIs: mount-local, mount-local-run, umount-local (FUSE support in the API). Add FUSE support directly to the API. Instead of needing to use the external 'guestmount' command, you can mount the libguestfs filesystem space on a local mountpoint using an API call from any language. Note that although mount-local-run is marked as Cancellable, the current implementation does not support it, but it would be relatively simple to add it. --- fuse/Makefile.am | 2 - fuse/dircache.c | 400 ---------------------- fuse/dircache.h | 46 --- fuse/guestmount.c | 965 ++++-------------------------------------------------- fuse/guestmount.h | 2 - 5 files changed, 69 insertions(+), 1346 deletions(-) delete mode 100644 fuse/dircache.c delete mode 100644 fuse/dircache.h (limited to 'fuse') diff --git a/fuse/Makefile.am b/fuse/Makefile.am index 19c498bb..f9d513bd 100644 --- a/fuse/Makefile.am +++ b/fuse/Makefile.am @@ -37,8 +37,6 @@ SHARED_SOURCE_FILES = \ guestmount_SOURCES = \ $(SHARED_SOURCE_FILES) \ - dircache.c \ - dircache.h \ guestmount.c \ guestmount.h diff --git a/fuse/dircache.c b/fuse/dircache.c deleted file mode 100644 index 771e3133..00000000 --- a/fuse/dircache.c +++ /dev/null @@ -1,400 +0,0 @@ -/* guestmount - mount guests using libguestfs and FUSE - * Copyright (C) 2009 Red Hat Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Derived from the example program 'fusexmp.c': - * Copyright (C) 2001-2007 Miklos Szeredi - * - * This program can be distributed under the terms of the GNU GPL. - * See the file COPYING. - */ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "hash.h" -#include "hash-pjw.h" - -#include "guestmount.h" -#include "dircache.h" - -/* Note on attribute caching: FUSE can cache filesystem attributes for - * short periods of time (configurable via -o attr_timeout). It - * doesn't cache xattrs, and in any case FUSE caching doesn't solve - * the problem that we have to make a series of guestfs_lstat and - * guestfs_lgetxattr calls when we first list a directory (thus, many - * round trips). - * - * For this reason, we also implement a readdir cache here which is - * invoked when a readdir call is made. readdir is modified so that - * as well as reading the directory, it also requests all the stat - * structures, xattrs and readlinks of all entries in the directory, - * and these are added to the cache here (for a short, configurable - * period of time) in anticipation that they will be needed - * immediately afterwards, which is usually the case when the user is - * doing an "ls"-like operation. - * - * You can still use FUSE attribute caching on top of this mechanism - * if you like. - */ - -struct lsc_entry { /* lstat cache entry */ - char *pathname; /* full path to the file */ - time_t timeout; /* when this entry expires */ - struct stat statbuf; /* statbuf */ -}; - -struct xac_entry { /* xattr cache entry */ - /* NB first two fields must be same as lsc_entry */ - char *pathname; /* full path to the file */ - time_t timeout; /* when this entry expires */ - struct guestfs_xattr_list *xattrs; -}; - -struct rlc_entry { /* readlink cache entry */ - /* NB first two fields must be same as lsc_entry */ - char *pathname; /* full path to the file */ - time_t timeout; /* when this entry expires */ - char *link; -}; - -static size_t -gen_hash (void const *x, size_t table_size) -{ - struct lsc_entry const *p = x; - return hash_pjw (p->pathname, table_size); -} - -static bool -gen_compare (void const *x, void const *y) -{ - struct lsc_entry const *a = x; - struct lsc_entry const *b = y; - return STREQ (a->pathname, b->pathname); -} - -static void -lsc_free (void *x) -{ - if (x) { - struct lsc_entry *p = x; - - free (p->pathname); - free (p); - } -} - -static void -xac_free (void *x) -{ - if (x) { - struct xac_entry *p = x; - - guestfs_free_xattr_list (p->xattrs); - lsc_free (x); - } -} - -static void -rlc_free (void *x) -{ - if (x) { - struct rlc_entry *p = x; - - free (p->link); - lsc_free (x); - } -} - -static Hash_table *lsc_ht, *xac_ht, *rlc_ht; - -void -init_dir_caches (void) -{ - lsc_ht = hash_initialize (1024, NULL, gen_hash, gen_compare, lsc_free); - xac_ht = hash_initialize (1024, NULL, gen_hash, gen_compare, xac_free); - rlc_ht = hash_initialize (1024, NULL, gen_hash, gen_compare, rlc_free); - if (!lsc_ht || !xac_ht || !rlc_ht) { - fprintf (stderr, "guestmount: could not initialize dir cache hashtables\n"); - exit (EXIT_FAILURE); - } -} - -void -free_dir_caches (void) -{ - hash_free (lsc_ht); - hash_free (xac_ht); - hash_free (rlc_ht); -} - -struct gen_remove_data { - time_t now; - Hash_table *ht; - Hash_data_freer freer; -}; - -static bool -gen_remove_if_expired (void *x, void *data) -{ - /* XXX hash_do_for_each was observed calling this function - * with x == NULL. - */ - if (x) { - struct lsc_entry *p = x; - struct gen_remove_data *d = data; - - if (p->timeout < d->now) { - if (verbose) - fprintf (stderr, "dir cache: expiring entry %p (%s)\n", - p, p->pathname); - d->freer (hash_delete (d->ht, x)); - } - } - - return 1; -} - -static void -gen_remove_all_expired (Hash_table *ht, Hash_data_freer freer, time_t now) -{ - struct gen_remove_data data; - data.now = now; - data.ht = ht; - data.freer = freer; - - /* Careful reading of the documentation to hash _seems_ to indicate - * that this is safe, _provided_ we use the default thresholds (in - * particular, no shrink threshold). - */ - hash_do_for_each (ht, gen_remove_if_expired, &data); -} - -void -dir_cache_remove_all_expired (time_t now) -{ - gen_remove_all_expired (lsc_ht, lsc_free, now); - gen_remove_all_expired (xac_ht, xac_free, now); - gen_remove_all_expired (rlc_ht, rlc_free, now); -} - -static int -gen_replace (Hash_table *ht, struct lsc_entry *new_entry, Hash_data_freer freer) -{ - struct lsc_entry *old_entry; - - old_entry = hash_delete (ht, new_entry); - freer (old_entry); - - if (verbose && old_entry) - fprintf (stderr, "dir cache: this entry replaced old entry %p (%s)\n", - old_entry, old_entry->pathname); - - old_entry = hash_insert (ht, new_entry); - if (old_entry == NULL) { - perror ("hash_insert"); - freer (new_entry); - return -1; - } - assert (old_entry == new_entry); - - return 0; -} - -int -lsc_insert (const char *path, const char *name, time_t now, - struct stat const *statbuf) -{ - struct lsc_entry *entry; - - entry = malloc (sizeof *entry); - if (entry == NULL) { - perror ("malloc"); - return -1; - } - - size_t len = strlen (path) + strlen (name) + 2; - entry->pathname = malloc (len); - if (entry->pathname == NULL) { - perror ("malloc"); - free (entry); - return -1; - } - if (STREQ (path, "/")) - snprintf (entry->pathname, len, "/%s", name); - else - snprintf (entry->pathname, len, "%s/%s", path, name); - - memcpy (&entry->statbuf, statbuf, sizeof entry->statbuf); - - entry->timeout = now + dir_cache_timeout; - - if (verbose) - fprintf (stderr, "dir cache: inserting lstat entry %p (%s)\n", - entry, entry->pathname); - - return gen_replace (lsc_ht, entry, lsc_free); -} - -int -xac_insert (const char *path, const char *name, time_t now, - struct guestfs_xattr_list *xattrs) -{ - struct xac_entry *entry; - - entry = malloc (sizeof *entry); - if (entry == NULL) { - perror ("malloc"); - return -1; - } - - size_t len = strlen (path) + strlen (name) + 2; - entry->pathname = malloc (len); - if (entry->pathname == NULL) { - perror ("malloc"); - free (entry); - return -1; - } - if (STREQ (path, "/")) - snprintf (entry->pathname, len, "/%s", name); - else - snprintf (entry->pathname, len, "%s/%s", path, name); - - entry->xattrs = xattrs; - - entry->timeout = now + dir_cache_timeout; - - if (verbose) - fprintf (stderr, "dir cache: inserting xattr entry %p (%s)\n", - entry, entry->pathname); - - return gen_replace (xac_ht, (struct lsc_entry *) entry, xac_free); -} - -int -rlc_insert (const char *path, const char *name, time_t now, - char *link) -{ - struct rlc_entry *entry; - - entry = malloc (sizeof *entry); - if (entry == NULL) { - perror ("malloc"); - return -1; - } - - size_t len = strlen (path) + strlen (name) + 2; - entry->pathname = malloc (len); - if (entry->pathname == NULL) { - perror ("malloc"); - free (entry); - return -1; - } - if (STREQ (path, "/")) - snprintf (entry->pathname, len, "/%s", name); - else - snprintf (entry->pathname, len, "%s/%s", path, name); - - entry->link = link; - - entry->timeout = now + dir_cache_timeout; - - if (verbose) - fprintf (stderr, "dir cache: inserting readlink entry %p (%s)\n", - entry, entry->pathname); - - return gen_replace (rlc_ht, (struct lsc_entry *) entry, rlc_free); -} - -const struct stat * -lsc_lookup (const char *pathname) -{ - const struct lsc_entry key = { .pathname = bad_cast (pathname) }; - struct lsc_entry *entry; - time_t now; - - time (&now); - - entry = hash_lookup (lsc_ht, &key); - if (entry && entry->timeout >= now) - return &entry->statbuf; - else - return NULL; -} - -const struct guestfs_xattr_list * -xac_lookup (const char *pathname) -{ - const struct xac_entry key = { .pathname = bad_cast (pathname) }; - struct xac_entry *entry; - time_t now; - - time (&now); - - entry = hash_lookup (xac_ht, &key); - if (entry && entry->timeout >= now) - return entry->xattrs; - else - return NULL; -} - -const char * -rlc_lookup (const char *pathname) -{ - const struct rlc_entry key = { .pathname = bad_cast (pathname) }; - struct rlc_entry *entry; - time_t now; - - time (&now); - - entry = hash_lookup (rlc_ht, &key); - if (entry && entry->timeout >= now) - return entry->link; - else - return NULL; -} - -static void -lsc_remove (Hash_table *ht, const char *pathname, Hash_data_freer freer) -{ - const struct lsc_entry key = { .pathname = bad_cast (pathname) }; - struct lsc_entry *entry; - - entry = hash_delete (ht, &key); - - if (verbose && entry) - fprintf (stderr, "dir cache: invalidating entry %p (%s)\n", - entry, entry->pathname); - - freer (entry); -} - -void -dir_cache_invalidate (const char *path) -{ - lsc_remove (lsc_ht, path, lsc_free); - lsc_remove (xac_ht, path, xac_free); - lsc_remove (rlc_ht, path, rlc_free); -} diff --git a/fuse/dircache.h b/fuse/dircache.h deleted file mode 100644 index 76ee9302..00000000 --- a/fuse/dircache.h +++ /dev/null @@ -1,46 +0,0 @@ -/* guestmount - mount guests using libguestfs and FUSE - * Copyright (C) 2009 Red Hat Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Derived from the example program 'fusexmp.c': - * Copyright (C) 2001-2007 Miklos Szeredi - * - * This program can be distributed under the terms of the GNU GPL. - * See the file COPYING. - */ - -#ifndef GUESTMOUNT_DIRCACHE_H -#define GUESTMOUNT_DIRCACHE_H 1 - -#include -#include -#include - -extern void init_dir_caches (void); -extern void free_dir_caches (void); -extern void dir_cache_remove_all_expired (time_t now); -extern void dir_cache_invalidate (const char *path); - -extern int lsc_insert (const char *path, const char *name, time_t now, struct stat const *statbuf); -extern int xac_insert (const char *path, const char *name, time_t now, struct guestfs_xattr_list *xattrs); -extern int rlc_insert (const char *path, const char *name, time_t now, char *link); -extern const struct stat *lsc_lookup (const char *pathname); -extern const struct guestfs_xattr_list *xac_lookup (const char *pathname); -extern const char *rlc_lookup (const char *pathname); - -extern int dir_cache_timeout; - -#endif /* GUESTMOUNT_DIRCACHE_H */ diff --git a/fuse/guestmount.c b/fuse/guestmount.c index f6c3463a..7c5e0af5 100644 --- a/fuse/guestmount.c +++ b/fuse/guestmount.c @@ -14,12 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Derived from the example program 'fusexmp.c': - * Copyright (C) 2001-2007 Miklos Szeredi - * - * This program can be distributed under the terms of the GNU GPL. - * See the file COPYING. */ #define FUSE_USE_VERSION 26 @@ -33,30 +27,18 @@ #include #include #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include +/* We're still using some of FUSE to handle command line options. */ #include -#include + +#include "guestfs.h" #include "progname.h" +#include "ignore-value.h" #include "guestmount.h" #include "options.h" -#include "dircache.h" - -/* See */ -#ifndef ENOATTR -#define ENOATTR ENODATA -#endif guestfs_h *g = NULL; int read_only = 0; @@ -66,838 +48,13 @@ int inspector = 0; int keys_from_stdin = 0; int echo_keys = 0; const char *libvirt_uri; -int dir_cache_timeout = 60; -static int trace_calls = 0; - -#define TRACE_CALL(fs,...) \ - if (trace_calls) { \ - fprintf (stderr, "%s: %s (" fs ")\n", \ - program_name, __func__, __VA_ARGS__); \ - } - -static int -error (void) -{ - return -guestfs_last_errno (g); -} - -static struct guestfs_xattr_list * -copy_xattr_list (const struct guestfs_xattr *first, size_t num) -{ - struct guestfs_xattr_list *xattrs; - - xattrs = malloc (sizeof *xattrs); - if (xattrs == NULL) { - perror ("malloc"); - return NULL; - } - - xattrs->len = num; - xattrs->val = malloc (num * sizeof (struct guestfs_xattr)); - if (xattrs->val == NULL) { - perror ("malloc"); - free (xattrs); - return NULL; - } - - size_t i; - for (i = 0; i < num; ++i) { - xattrs->val[i].attrname = strdup (first[i].attrname); - xattrs->val[i].attrval_len = first[i].attrval_len; - xattrs->val[i].attrval = malloc (first[i].attrval_len); - memcpy (xattrs->val[i].attrval, first[i].attrval, first[i].attrval_len); - } - - return xattrs; -} - -static int -fg_readdir (const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) -{ - TRACE_CALL ("%s, %p, %ld", path, buf, (long) offset); - - time_t now; - time (&now); - - dir_cache_remove_all_expired (now); - - struct guestfs_dirent_list *ents; - - ents = guestfs_readdir (g, path); - if (ents == NULL) - return error (); - - size_t i; - for (i = 0; i < ents->len; ++i) { - struct stat stat; - memset (&stat, 0, sizeof stat); - - stat.st_ino = ents->val[i].ino; - switch (ents->val[i].ftyp) { - case 'b': stat.st_mode = S_IFBLK; break; - case 'c': stat.st_mode = S_IFCHR; break; - case 'd': stat.st_mode = S_IFDIR; break; - case 'f': stat.st_mode = S_IFIFO; break; - case 'l': stat.st_mode = S_IFLNK; break; - case 'r': stat.st_mode = S_IFREG; break; - case 's': stat.st_mode = S_IFSOCK; break; - case 'u': - case '?': - default: stat.st_mode = 0; - } - - /* Copied from the example, which also ignores 'offset'. I'm - * not quite sure how this is ever supposed to work on large - * directories. XXX - */ - if (filler (buf, ents->val[i].name, &stat, 0)) - break; - } - - /* Now prepopulate the directory caches. This step is just an - * optimization, don't worry if it fails. - */ - char **names = malloc ((ents->len + 1) * sizeof (char *)); - if (names) { - for (i = 0; i < ents->len; ++i) - names[i] = ents->val[i].name; - names[i] = NULL; - - struct guestfs_stat_list *ss = guestfs_lstatlist (g, path, names); - if (ss) { - for (i = 0; i < ss->len; ++i) { - if (ss->val[i].ino >= 0) { - struct stat statbuf; - - statbuf.st_dev = ss->val[i].dev; - statbuf.st_ino = ss->val[i].ino; - statbuf.st_mode = ss->val[i].mode; - statbuf.st_nlink = ss->val[i].nlink; - statbuf.st_uid = ss->val[i].uid; - statbuf.st_gid = ss->val[i].gid; - statbuf.st_rdev = ss->val[i].rdev; - statbuf.st_size = ss->val[i].size; - statbuf.st_blksize = ss->val[i].blksize; - statbuf.st_blocks = ss->val[i].blocks; - statbuf.st_atime = ss->val[i].atime; - statbuf.st_mtime = ss->val[i].mtime; - statbuf.st_ctime = ss->val[i].ctime; - - lsc_insert (path, names[i], now, &statbuf); - } - } - guestfs_free_stat_list (ss); - } - - struct guestfs_xattr_list *xattrs = guestfs_lxattrlist (g, path, names); - if (xattrs) { - size_t ni, num; - struct guestfs_xattr *first; - struct guestfs_xattr_list *copy; - for (i = 0, ni = 0; i < xattrs->len; ++i, ++ni) { - assert (strlen (xattrs->val[i].attrname) == 0); - if (xattrs->val[i].attrval_len > 0) { - ++i; - first = &xattrs->val[i]; - num = 0; - for (; i < xattrs->len && strlen (xattrs->val[i].attrname) > 0; ++i) - num++; - - copy = copy_xattr_list (first, num); - if (copy) - xac_insert (path, names[ni], now, copy); - - i--; - } - } - guestfs_free_xattr_list (xattrs); - } - - char **links = guestfs_readlinklist (g, path, names); - if (links) { - for (i = 0; names[i] != NULL; ++i) { - if (links[i][0]) - /* Note that rlc_insert owns the string links[i] after this, */ - rlc_insert (path, names[i], now, links[i]); - else - /* which is why we have to free links[i] here. */ - free (links[i]); - } - free (links); /* free the array, not the strings */ - } - - free (names); - } - - guestfs_free_dirent_list (ents); - - return 0; -} - -static int -fg_getattr (const char *path, struct stat *statbuf) -{ - TRACE_CALL ("%s, %p", path, statbuf); - - const struct stat *buf; - - buf = lsc_lookup (path); - if (buf) { - memcpy (statbuf, buf, sizeof *statbuf); - return 0; - } - - struct guestfs_stat *r; - - r = guestfs_lstat (g, path); - if (r == NULL) - return error (); - - statbuf->st_dev = r->dev; - statbuf->st_ino = r->ino; - statbuf->st_mode = r->mode; - statbuf->st_nlink = r->nlink; - statbuf->st_uid = r->uid; - statbuf->st_gid = r->gid; - statbuf->st_rdev = r->rdev; - statbuf->st_size = r->size; - statbuf->st_blksize = r->blksize; - statbuf->st_blocks = r->blocks; - statbuf->st_atime = r->atime; - statbuf->st_mtime = r->mtime; - statbuf->st_ctime = r->ctime; - - guestfs_free_stat (r); - - return 0; -} - -/* Nautilus loves to use access(2) to test everything about a file, - * such as whether it's executable. Therefore treat this a lot like - * fg_getattr. - */ -static int -fg_access (const char *path, int mask) -{ - TRACE_CALL ("%s, %d", path, mask); - - struct stat statbuf; - int r; - - if (read_only && (mask & W_OK)) - return -EROFS; - - r = fg_getattr (path, &statbuf); - if (r < 0 || mask == F_OK) - return r; - - struct fuse_context *fuse = fuse_get_context (); - int ok = 1; - - if (mask & R_OK) - ok = ok && - ( fuse->uid == statbuf.st_uid ? statbuf.st_mode & S_IRUSR - : fuse->gid == statbuf.st_gid ? statbuf.st_mode & S_IRGRP - : statbuf.st_mode & S_IROTH); - if (mask & W_OK) - ok = ok && - ( fuse->uid == statbuf.st_uid ? statbuf.st_mode & S_IWUSR - : fuse->gid == statbuf.st_gid ? statbuf.st_mode & S_IWGRP - : statbuf.st_mode & S_IWOTH); - if (mask & X_OK) - ok = ok && - ( fuse->uid == statbuf.st_uid ? statbuf.st_mode & S_IXUSR - : fuse->gid == statbuf.st_gid ? statbuf.st_mode & S_IXGRP - : statbuf.st_mode & S_IXOTH); - - return ok ? 0 : -EACCES; -} - -static int -fg_readlink (const char *path, char *buf, size_t size) -{ - TRACE_CALL ("%s, %p, %zu", path, buf, size); - - const char *r; - int free_it = 0; - - r = rlc_lookup (path); - if (!r) { - r = guestfs_readlink (g, path); - if (r == NULL) - return error (); - free_it = 1; - } - - /* Note this is different from the real readlink(2) syscall. FUSE wants - * the string to be always nul-terminated, even if truncated. - */ - size_t len = strlen (r); - if (len > size - 1) - len = size - 1; - - memcpy (buf, r, len); - buf[len] = '\0'; - - if (free_it) { - char *tmp = (char *) r; - free (tmp); - } - - return 0; -} - -static int -fg_mknod (const char *path, mode_t mode, dev_t rdev) -{ - TRACE_CALL ("%s, 0%o, 0x%lx", path, mode, (long) rdev); - - int r; - - if (read_only) return -EROFS; - - dir_cache_invalidate (path); - - r = guestfs_mknod (g, mode, major (rdev), minor (rdev), path); - if (r == -1) - return error (); - - return 0; -} - -static int -fg_mkdir (const char *path, mode_t mode) -{ - TRACE_CALL ("%s, 0%o", path, mode); - - int r; - - if (read_only) return -EROFS; - - dir_cache_invalidate (path); - - r = guestfs_mkdir_mode (g, path, mode); - if (r == -1) - return error (); - - return 0; -} - -static int -fg_unlink (const char *path) -{ - TRACE_CALL ("%s", path); - - int r; - - if (read_only) return -EROFS; - - dir_cache_invalidate (path); - - r = guestfs_rm (g, path); - if (r == -1) - return error (); - - return 0; -} - -static int -fg_rmdir (const char *path) -{ - TRACE_CALL ("%s", path); - - int r; - - if (read_only) return -EROFS; - - dir_cache_invalidate (path); - - r = guestfs_rmdir (g, path); - if (r == -1) - return error (); - - return 0; -} - -static int -fg_symlink (const char *from, const char *to) -{ - TRACE_CALL ("%s, %s", from, to); - - int r; - - if (read_only) return -EROFS; - - dir_cache_invalidate (to); - - r = guestfs_ln_s (g, from, to); - if (r == -1) - return error (); - - return 0; -} - -static int -fg_rename (const char *from, const char *to) -{ - TRACE_CALL ("%s, %s", from, to); - - int r; - - if (read_only) return -EROFS; - - dir_cache_invalidate (from); - dir_cache_invalidate (to); - - /* XXX It's not clear how close the 'mv' command is to the - * rename syscall. We might need to add the rename syscall - * to the guestfs(3) API. - */ - r = guestfs_mv (g, from, to); - if (r == -1) - return error (); - - return 0; -} - -static int -fg_link (const char *from, const char *to) -{ - TRACE_CALL ("%s, %s", from, to); - - int r; - - if (read_only) return -EROFS; - - dir_cache_invalidate (from); - dir_cache_invalidate (to); - - r = guestfs_ln (g, from, to); - if (r == -1) - return error (); - - return 0; -} - -static int -fg_chmod (const char *path, mode_t mode) -{ - TRACE_CALL ("%s, 0%o", path, mode); - - int r; - - if (read_only) return -EROFS; - - dir_cache_invalidate (path); - - r = guestfs_chmod (g, mode, path); - if (r == -1) - return error (); - - return 0; -} - -static int -fg_chown (const char *path, uid_t uid, gid_t gid) -{ - TRACE_CALL ("%s, %ld, %ld", path, (long) uid, (long) gid); - - int r; - - if (read_only) return -EROFS; - - dir_cache_invalidate (path); - - r = guestfs_lchown (g, uid, gid, path); - if (r == -1) - return error (); - - return 0; -} - -static int -fg_truncate (const char *path, off_t size) -{ - TRACE_CALL ("%s, %ld", path, (long) size); - - int r; - - if (read_only) return -EROFS; - - dir_cache_invalidate (path); - - r = guestfs_truncate_size (g, path, size); - if (r == -1) - return error (); - - return 0; -} - -static int -fg_utimens (const char *path, const struct timespec ts[2]) -{ - TRACE_CALL ("%s, [{ %ld, %ld }, { %ld, %ld }]", - path, ts[0].tv_sec, ts[0].tv_nsec, ts[1].tv_sec, ts[1].tv_nsec); - - int r; - - if (read_only) return -EROFS; - - dir_cache_invalidate (path); - - time_t atsecs = ts[0].tv_sec; - long atnsecs = ts[0].tv_nsec; - time_t mtsecs = ts[1].tv_sec; - long mtnsecs = ts[1].tv_nsec; - -#ifdef UTIME_NOW - if (atnsecs == UTIME_NOW) - atnsecs = -1; -#endif -#ifdef UTIME_OMIT - if (atnsecs == UTIME_OMIT) - atnsecs = -2; -#endif -#ifdef UTIME_NOW - if (mtnsecs == UTIME_NOW) - mtnsecs = -1; -#endif -#ifdef UTIME_OMIT - if (mtnsecs == UTIME_OMIT) - mtnsecs = -2; -#endif - - r = guestfs_utimens (g, path, atsecs, atnsecs, mtsecs, mtnsecs); - if (r == -1) - return error (); - - return 0; -} - -/* All this function needs to do is to check that the requested open - * flags are valid. See the notes in . - */ -static int -fg_open (const char *path, struct fuse_file_info *fi) -{ - TRACE_CALL ("%s, 0%o", path, fi->flags); - - int flags = fi->flags & O_ACCMODE; - - if (read_only && flags != O_RDONLY) - return -EROFS; - - return 0; -} - -static int -fg_read (const char *path, char *buf, size_t size, off_t offset, - struct fuse_file_info *fi) -{ - TRACE_CALL ("%s, %p, %zu, %ld", path, buf, size, (long) offset); - - char *r; - size_t rsize; - - if (verbose) - fprintf (stderr, "fg_read: %s: size %zu offset %ju\n", - path, size, offset); - - /* The guestfs protocol limits size to somewhere over 2MB. We just - * reduce the requested size here accordingly and push the problem - * up to every user. http://www.jwz.org/doc/worse-is-better.html - */ - const size_t limit = 2 * 1024 * 1024; - if (size > limit) - size = limit; - - r = guestfs_pread (g, path, size, offset, &rsize); - if (r == NULL) - return error (); - - /* This should never happen, but at least it stops us overflowing - * the output buffer if it does happen. - */ - if (rsize > size) - rsize = size; - - memcpy (buf, r, rsize); - free (r); - - return rsize; -} - -static int -fg_write (const char *path, const char *buf, size_t size, - off_t offset, struct fuse_file_info *fi) -{ - TRACE_CALL ("%s, %p, %zu, %ld", path, buf, size, (long) offset); - - if (read_only) return -EROFS; - - dir_cache_invalidate (path); - - /* See fg_read. */ - const size_t limit = 2 * 1024 * 1024; - if (size > limit) - size = limit; - - int r; - r = guestfs_pwrite (g, path, buf, size, offset); - if (r == -1) - return error (); - - return r; -} - -static int -fg_statfs (const char *path, struct statvfs *stbuf) -{ - TRACE_CALL ("%s, %p", path, stbuf); - - struct guestfs_statvfs *r; - - r = guestfs_statvfs (g, path); - if (r == NULL) - return error (); - - stbuf->f_bsize = r->bsize; - stbuf->f_frsize = r->frsize; - stbuf->f_blocks = r->blocks; - stbuf->f_bfree = r->bfree; - stbuf->f_bavail = r->bavail; - stbuf->f_files = r->files; - stbuf->f_ffree = r->ffree; - stbuf->f_favail = r->favail; - stbuf->f_fsid = r->fsid; - stbuf->f_flag = r->flag; - stbuf->f_namemax = r->namemax; - - guestfs_free_statvfs (r); - - return 0; -} - -static int -fg_release (const char *path, struct fuse_file_info *fi) -{ - TRACE_CALL ("%s", path); - - /* Just a stub. This method is optional and can safely be left - * unimplemented. - */ - return 0; -} - -/* Emulate this by calling sync. */ -static int fg_fsync(const char *path, int isdatasync, - struct fuse_file_info *fi) -{ - TRACE_CALL ("%s, %d", path, isdatasync); - - int r; - - r = guestfs_sync (g); - if (r == -1) - return error (); - - return 0; -} - -static int -fg_setxattr (const char *path, const char *name, const char *value, - size_t size, int flags) -{ - TRACE_CALL ("%s, %s, %p, %zu", path, name, value, size); - - int r; - - if (read_only) return -EROFS; - - dir_cache_invalidate (path); - - /* XXX Underlying guestfs(3) API doesn't understand the flags. */ - r = guestfs_lsetxattr (g, name, value, size, path); - if (r == -1) - return error (); - - return 0; -} - -/* The guestfs(3) API for getting xattrs is much easier to use - * than the real syscall. Unfortunately we now have to emulate - * the real syscall using that API :-( - */ -static int -fg_getxattr (const char *path, const char *name, char *value, - size_t size) -{ - TRACE_CALL ("%s, %s, %p, %zu", path, name, value, size); - - const struct guestfs_xattr_list *xattrs; - int free_attrs = 0; - - xattrs = xac_lookup (path); - if (xattrs == NULL) { - xattrs = guestfs_lgetxattrs (g, path); - if (xattrs == NULL) - return error (); - free_attrs = 1; - } - - /* Find the matching attribute (index in 'i'). */ - ssize_t r; - size_t i; - for (i = 0; i < xattrs->len; ++i) { - if (STREQ (xattrs->val[i].attrname, name)) - break; - } - - if (i == xattrs->len) { /* not found */ - r = -ENOATTR; - goto out; - } - - /* The getxattr man page is unclear, but if value == NULL then we - * return the space required (the caller then makes a second syscall - * after allocating the required amount of space). If value != NULL - * then it's not clear what we should do, but it appears we should - * copy as much as possible and return -ERANGE if there's not enough - * space in the buffer. - */ - size_t sz = xattrs->val[i].attrval_len; - if (value == NULL) { - r = sz; - goto out; - } - - if (sz <= size) - r = sz; - else { - r = -ERANGE; - sz = size; - } - memcpy (value, xattrs->val[i].attrval, sz); - -out: - if (free_attrs) - guestfs_free_xattr_list ((struct guestfs_xattr_list *) xattrs); - - return r; -} - -/* Ditto as above. */ -static int -fg_listxattr (const char *path, char *list, size_t size) -{ - TRACE_CALL ("%s, %p, %zu", path, list, size); - - const struct guestfs_xattr_list *xattrs; - int free_attrs = 0; - - xattrs = xac_lookup (path); - if (xattrs == NULL) { - xattrs = guestfs_lgetxattrs (g, path); - if (xattrs == NULL) - return error (); - free_attrs = 1; - } - - /* Calculate how much space is required to hold the result. */ - size_t space = 0; - size_t len; - size_t i; - for (i = 0; i < xattrs->len; ++i) { - len = strlen (xattrs->val[i].attrname) + 1; - space += len; - } - - /* The listxattr man page is unclear, but if list == NULL then we - * return the space required (the caller then makes a second syscall - * after allocating the required amount of space). If list != NULL - * then it's not clear what we should do, but it appears we should - * copy as much as possible and return -ERANGE if there's not enough - * space in the buffer. - */ - ssize_t r; - if (list == NULL) { - r = space; - goto out; - } - - r = 0; - for (i = 0; i < xattrs->len; ++i) { - len = strlen (xattrs->val[i].attrname) + 1; - if (size >= len) { - memcpy (list, xattrs->val[i].attrname, len); - size -= len; - list += len; - r += len; - } else { - r = -ERANGE; - break; - } - } - - out: - if (free_attrs) - guestfs_free_xattr_list ((struct guestfs_xattr_list *) xattrs); - - return r; -} - -static int -fg_removexattr(const char *path, const char *name) -{ - TRACE_CALL ("%s, %s", path, name); - - int r; - - if (read_only) return -EROFS; - - dir_cache_invalidate (path); - - r = guestfs_lremovexattr (g, name, path); - if (r == -1) - return error (); - - return 0; -} - -static struct fuse_operations fg_operations = { - .getattr = fg_getattr, - .access = fg_access, - .readlink = fg_readlink, - .readdir = fg_readdir, - .mknod = fg_mknod, - .mkdir = fg_mkdir, - .symlink = fg_symlink, - .unlink = fg_unlink, - .rmdir = fg_rmdir, - .rename = fg_rename, - .link = fg_link, - .chmod = fg_chmod, - .chown = fg_chown, - .truncate = fg_truncate, - .utimens = fg_utimens, - .open = fg_open, - .read = fg_read, - .write = fg_write, - .statfs = fg_statfs, - .release = fg_release, - .fsync = fg_fsync, - .setxattr = fg_setxattr, - .getxattr = fg_getxattr, - .listxattr = fg_listxattr, - .removexattr = fg_removexattr, -}; static void __attribute__((noreturn)) fuse_help (void) { + static struct fuse_operations null_operations; const char *tmp_argv[] = { program_name, "--help", NULL }; - fuse_main (2, (char **) tmp_argv, &fg_operations, NULL); + fuse_main (2, (char **) tmp_argv, &null_operations, NULL); exit (EXIT_SUCCESS); } @@ -990,20 +147,12 @@ main (int argc, char *argv[]) int option_index; struct sigaction sa; - size_t fuse_argc = 0; - const char **fuse_argv = NULL; + int debug_calls = 0; + int dir_cache_timeout = -1; + int do_fork = 1; + char *fuse_options = NULL; -#define ADD_FUSE_ARG(str) \ - do { \ - fuse_argc ++; \ - fuse_argv = realloc (fuse_argv, (1+fuse_argc) * sizeof (char *)); \ - if (!fuse_argv) { \ - perror ("realloc"); \ - exit (EXIT_FAILURE); \ - } \ - fuse_argv[fuse_argc-1] = (str); \ - fuse_argv[fuse_argc] = NULL; \ - } while (0) + struct guestfs_mount_local_argv optargs; /* LC_ALL=C is required so we can parse error messages. */ setenv ("LC_ALL", "C", 1); @@ -1016,24 +165,12 @@ main (int argc, char *argv[]) sa.sa_flags = SA_RESTART; sigaction (SIGPIPE, &sa, NULL); - /* Various initialization. */ - init_dir_caches (); - g = guestfs_create (); if (g == NULL) { fprintf (stderr, _("guestfs_create: failed to create handle\n")); exit (EXIT_FAILURE); } - guestfs_set_recovery_proc (g, 0); - - ADD_FUSE_ARG (program_name); - /* MUST be single-threaded. You cannot have two threads accessing the - * same libguestfs handle, and opening more than one handle is likely - * to be very expensive. - */ - ADD_FUSE_ARG ("-s"); - for (;;) { c = getopt_long (argc, argv, options, long_options, &option_index); if (c == -1) break; @@ -1089,8 +226,7 @@ main (int argc, char *argv[]) break; case 'o': - ADD_FUSE_ARG ("-o"); - ADD_FUSE_ARG (optarg); + fuse_opt_add_opt_escaped (&fuse_options, optarg); break; case 'r': @@ -1111,9 +247,8 @@ main (int argc, char *argv[]) case 'x': OPTION_x; - ADD_FUSE_ARG ("-f"); - guestfs_set_recovery_proc (g, 1); - trace_calls = 1; + debug_calls = 1; + do_fork = 0; break; case HELP_OPTION: @@ -1181,6 +316,9 @@ main (int argc, char *argv[]) exit (EXIT_FAILURE); } + /* If we're forking, we can't use the recovery process. */ + guestfs_set_recovery_proc (g, !do_fork); + /* Do the guest drives and mountpoints. */ add_drives (drvs, 'a'); if (guestfs_launch (g) == -1) @@ -1196,36 +334,71 @@ main (int argc, char *argv[]) if (guestfs_umask (g, 0) == -1) exit (EXIT_FAILURE); + optargs.bitmask = 0; + if (read_only) { + optargs.bitmask |= GUESTFS_MOUNT_LOCAL_READONLY_BITMASK; + optargs.readonly = 1; + } + if (debug_calls) { + optargs.bitmask |= GUESTFS_MOUNT_LOCAL_DEBUGCALLS_BITMASK; + optargs.debugcalls = 1; + } + if (dir_cache_timeout > 0) { + optargs.bitmask |= GUESTFS_MOUNT_LOCAL_CACHETIMEOUT_BITMASK; + optargs.cachetimeout = dir_cache_timeout; + } + if (fuse_options != NULL) { + optargs.bitmask |= GUESTFS_MOUNT_LOCAL_OPTIONS_BITMASK; + optargs.options = fuse_options; + } + + if (guestfs_mount_local_argv (g, argv[optind], &optargs) == -1) + exit (EXIT_FAILURE); + /* At the last minute, remove the libguestfs error handler. In code * above this point, the default error handler has been used which - * sends all errors to stderr. Now before entering FUSE itself we - * want to silence errors so we can convert them (see error() - * function above). + * sends all errors to stderr. From now on, the FUSE code will + * convert errors into error codes (errnos) when appropriate. */ guestfs_set_error_handler (g, NULL, NULL); - /* Finish off FUSE args. */ - ADD_FUSE_ARG (argv[optind]); + /* Daemonize. */ + if (do_fork) { + pid_t pid; + int fd; + + pid = fork (); + if (pid == -1) { + perror ("fork"); + exit (EXIT_FAILURE); + } + + if (pid != 0) /* parent */ + _exit (EXIT_SUCCESS); + + /* Emulate what old fuse_daemonize used to do. */ + if (setsid () == -1) { + perror ("setsid"); + exit (EXIT_FAILURE); + } - /* - It says about the line containing the for-statement: - error: assuming signed overflow does not occur when simplifying conditional to constant [-Wstrict-overflow] + ignore_value (chdir ("/")); - if (verbose) { - fprintf (stderr, "guestmount: invoking FUSE with args ["); - for (i = 0; i < fuse_argc; ++i) { - if (i > 0) fprintf (stderr, ", "); - fprintf (stderr, "%s", fuse_argv[i]); + fd = open ("/dev/null", O_RDWR); + if (fd >= 0) { + dup2 (fd, 0); + dup2 (fd, 1); + dup2 (fd, 2); + if (fd > 2) + close (fd); } - fprintf (stderr, "]\n"); } - */ - r = fuse_main (fuse_argc, (char **) fuse_argv, &fg_operations, NULL); + /* Main loop. */ + r = guestfs_mount_local_run (g); /* Cleanup. */ guestfs_close (g); - free_dir_caches (); exit (r == 0 ? EXIT_SUCCESS : EXIT_FAILURE); } diff --git a/fuse/guestmount.h b/fuse/guestmount.h index 81d026db..a6249ec9 100644 --- a/fuse/guestmount.h +++ b/fuse/guestmount.h @@ -50,6 +50,4 @@ bad_cast (char const *s) return (char *) s; } -extern int verbose; - #endif /* GUESTMOUNT_H_ */ -- cgit