summaryrefslogtreecommitdiffstats
path: root/fuse
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2011-12-14 08:44:41 +0000
committerRichard W.M. Jones <rjones@redhat.com>2012-03-29 16:45:01 +0100
commitc6f09fac0666260587f95bdfee3c20c9166dae94 (patch)
tree25b1b6493697034120508518dcfcbc6d34417156 /fuse
parent49fdba0ae98b5b6995ccaf0f0ba4f542bfff7ce3 (diff)
downloadlibguestfs-c6f09fac0666260587f95bdfee3c20c9166dae94.tar.gz
libguestfs-c6f09fac0666260587f95bdfee3c20c9166dae94.tar.xz
libguestfs-c6f09fac0666260587f95bdfee3c20c9166dae94.zip
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.
Diffstat (limited to 'fuse')
-rw-r--r--fuse/Makefile.am2
-rw-r--r--fuse/dircache.c400
-rw-r--r--fuse/dircache.h46
-rw-r--r--fuse/guestmount.c965
-rw-r--r--fuse/guestmount.h2
5 files changed, 69 insertions, 1346 deletions
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 <miklos@szeredi.hu>
- *
- * This program can be distributed under the terms of the GNU GPL.
- * See the file COPYING.
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <assert.h>
-#include <sys/time.h>
-#include <sys/types.h>
-
-#include <guestfs.h>
-
-#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 <miklos@szeredi.hu>
- *
- * 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 <time.h>
-#include <sys/stat.h>
-#include <guestfs.h>
-
-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 <miklos@szeredi.hu>
- *
- * 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 <string.h>
#include <unistd.h>
#include <getopt.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <errno.h>
#include <signal.h>
-#include <time.h>
-#include <assert.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <locale.h>
-#include <libintl.h>
+/* We're still using some of FUSE to handle command line options. */
#include <fuse.h>
-#include <guestfs.h>
+
+#include "guestfs.h"
#include "progname.h"
+#include "ignore-value.h"
#include "guestmount.h"
#include "options.h"
-#include "dircache.h"
-
-/* See <attr/xattr.h> */
-#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 <fuse/fuse.h>.
- */
-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_ */