/* guestmount - mount guests using libguestfs and FUSE
- * Copyright (C) 2009 Red Hat Inc.
+ * Copyright (C) 2009-2010 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
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
+#include <inttypes.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/types.h>
+#include <locale.h>
#include <fuse.h>
#include <guestfs.h>
#include "progname.h"
#include "guestmount.h"
+#include "options.h"
#include "dircache.h"
/* See <attr/xattr.h> */
#define ENOATTR ENODATA
#endif
-static guestfs_h *g = NULL;
-static int read_only = 0;
+guestfs_h *g = NULL;
+int read_only = 0;
int verbose = 0;
+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;
-/* This is ugly: guestfs errors are strings, FUSE wants -errno. We
- * have to do the conversion as best we can.
- */
-#define MAX_ERRNO 256
+#define TRACE_CALL(fs,...) \
+ if (trace_calls) { \
+ fprintf (stderr, "%s: %s (" fs ")\n", \
+ program_name, __func__, __VA_ARGS__); \
+ }
static int
error (void)
{
- int i;
- const char *err = guestfs_last_error (g);
-
- if (!err)
- return -EINVAL;
-
- if (verbose)
- fprintf (stderr, "%s\n", err);
-
- /* Add a few of our own ... */
-
- /* This indicates guestfsd died. Translate into a hard EIO error.
- * Arguably we could relaunch the guest if we hit this error.
- */
- if (strstr (err, "call launch before using this function"))
- return -EIO;
-
- /* See if it matches an errno string in the host. */
- for (i = 0; i < MAX_ERRNO; ++i) {
- const char *e = strerror (i);
- if (e && strstr (err, e) != NULL)
- return -i;
- }
-
- /* Too bad, return a generic error. */
- return -EINVAL;
+ return -guestfs_last_errno (g);
}
static struct guestfs_xattr_list *
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);
static int
fg_getattr (const char *path, struct stat *statbuf)
{
+ TRACE_CALL ("%s, %p", path, statbuf);
+
const struct stat *buf;
buf = lsc_lookup (path);
static int
fg_access (const char *path, int mask)
{
+ TRACE_CALL ("%s, %d", path, mask);
+
struct stat statbuf;
int r;
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;
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;
static int
fg_mkdir (const char *path, mode_t mode)
{
+ TRACE_CALL ("%s, 0%o", path, mode);
+
int r;
if (read_only) return -EROFS;
static int
fg_unlink (const char *path)
{
+ TRACE_CALL ("%s", path);
+
int r;
if (read_only) return -EROFS;
static int
fg_rmdir (const char *path)
{
+ TRACE_CALL ("%s", path);
+
int r;
if (read_only) return -EROFS;
static int
fg_symlink (const char *from, const char *to)
{
+ TRACE_CALL ("%s, %s", from, to);
+
int r;
if (read_only) return -EROFS;
static int
fg_rename (const char *from, const char *to)
{
+ TRACE_CALL ("%s, %s", from, to);
+
int r;
if (read_only) return -EROFS;
static int
fg_link (const char *from, const char *to)
{
+ TRACE_CALL ("%s, %s", from, to);
+
int r;
if (read_only) return -EROFS;
static int
fg_chmod (const char *path, mode_t mode)
{
+ TRACE_CALL ("%s, 0%o", path, mode);
+
int r;
if (read_only) return -EROFS;
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;
static int
fg_truncate (const char *path, off_t size)
{
+ TRACE_CALL ("%s, %ld", path, (long) size);
+
int r;
if (read_only) return -EROFS;
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;
return 0;
}
-/* This call is quite hard to emulate through the guestfs(3) API. In
- * one sense it's a little like access (see above) because it tests
- * whether opening a file would succeed given the flags. But it also
- * has side effects such as truncating the file if O_TRUNC is given.
- * Therefore we need to emulate it ... painfully.
+/* 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)
{
- int r, exists;
-
- if (fi->flags & O_WRONLY) {
- if (read_only)
- return -EROFS;
- }
-
- exists = guestfs_exists (g, path);
- if (exists == -1)
- return error ();
-
- if (fi->flags & O_CREAT) {
- if (read_only)
- return -EROFS;
+ TRACE_CALL ("%s, 0%o", path, fi->flags);
+
+ int flags = fi->flags & 3;
- dir_cache_invalidate (path);
-
- /* Exclusive? File must not exist already. */
- if (fi->flags & O_EXCL) {
- if (exists)
- return -EEXIST;
- }
-
- /* Create? Touch it and optionally truncate it. */
- r = guestfs_touch (g, path);
- if (r == -1)
- return error ();
-
- if (fi->flags & O_TRUNC) {
- r = guestfs_truncate (g, path);
- if (r == -1)
- return error ();
- }
- } else {
- /* Not create, just check it exists. */
- if (!exists)
- return -ENOENT;
- }
+ if (read_only && flags != O_RDONLY)
+ return -EROFS;
return 0;
}
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, offset);
+
char *r;
size_t rsize;
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, offset);
+
if (read_only) return -EROFS;
dir_cache_invalidate (path);
- return -ENOSYS; /* XXX */
+ /* 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);
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.
*/
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);
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;
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;
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;
static int
fg_removexattr(const char *path, const char *name)
{
+ TRACE_CALL ("%s, %s", path, name);
+
int r;
if (read_only) return -EROFS;
.removexattr = fg_removexattr,
};
-struct drv {
- struct drv *next;
- char *filename;
-};
-
-struct mp {
- struct mp *next;
- char *device;
- char *mountpoint;
-};
-
-static void add_drives (struct drv *);
-static void mount_mps (struct mp *);
-
static void __attribute__((noreturn))
fuse_help (void)
{
fprintf (stdout,
_("%s: FUSE module for libguestfs\n"
"%s lets you mount a virtual machine filesystem\n"
- "Copyright (C) 2009 Red Hat Inc.\n"
+ "Copyright (C) 2009-2010 Red Hat Inc.\n"
"Usage:\n"
" %s [--options] [-- [--FUSE-options]] mountpoint\n"
"Options:\n"
" -a|--add image Add image\n"
+ " -c|--connect uri Specify libvirt URI for -d option\n"
" --dir-cache-timeout Set readdir cache timeout (default 5 sec)\n"
+ " -d|--domain guest Add disks from libvirt guest\n"
+ " --echo-keys Don't turn off echo for passphrases\n"
+ " --format[=raw|..] Force disk format for -a option\n"
" --fuse-help Display extra FUSE options\n"
+ " -i|--inspector Automatically mount filesystems\n"
" --help Display help message and exit\n"
+ " --keys-from-stdin Read passphrases from stdin\n"
" -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n"
" -n|--no-sync Don't autosync\n"
" -o|--option opt Pass extra option to FUSE\n"
" -r|--ro Mount read-only\n"
" --selinux Enable SELinux support\n"
- " --trace Trace guestfs API calls (to stderr)\n"
" -v|--verbose Verbose messages\n"
" -V|--version Display version and exit\n"
+ " -x|--trace Trace guestfs API calls\n"
),
program_name, program_name, program_name);
}
int
main (int argc, char *argv[])
{
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEBASEDIR);
+ textdomain (PACKAGE);
+
enum { HELP_OPTION = CHAR_MAX + 1 };
/* The command line arguments are broadly compatible with (a subset
* of) guestfish. Thus we have to deal mainly with -a, -m and --ro.
*/
- static const char *options = "a:m:no:rv?V";
+ static const char *options = "a:c:d:im:no:rv?Vwx";
static const struct option long_options[] = {
{ "add", 1, 0, 'a' },
+ { "connect", 1, 0, 'c' },
{ "dir-cache-timeout", 1, 0, 0 },
+ { "domain", 1, 0, 'd' },
+ { "echo-keys", 0, 0, 0 },
+ { "format", 2, 0, 0 },
{ "fuse-help", 0, 0, 0 },
{ "help", 0, 0, HELP_OPTION },
+ { "inspector", 0, 0, 'i' },
+ { "keys-from-stdin", 0, 0, 0 },
{ "mount", 1, 0, 'm' },
{ "no-sync", 0, 0, 'n' },
{ "option", 1, 0, 'o' },
{ "ro", 0, 0, 'r' },
+ { "rw", 0, 0, 'w' },
{ "selinux", 0, 0, 0 },
- { "trace", 0, 0, 0 },
+ { "trace", 0, 0, 'x' },
{ "verbose", 0, 0, 'v' },
{ "version", 0, 0, 'V' },
{ 0, 0, 0, 0 }
struct mp *mps = NULL;
struct mp *mp;
char *p;
- int c, i, r;
+ const char *format = NULL;
+ int c, r;
int option_index;
struct sigaction sa;
exit (EXIT_FAILURE);
}
- guestfs_set_autosync (g, 1);
guestfs_set_recovery_proc (g, 0);
ADD_FUSE_ARG (program_name);
fuse_help ();
else if (STREQ (long_options[option_index].name, "selinux"))
guestfs_set_selinux (g, 1);
- else if (STREQ (long_options[option_index].name, "trace")) {
- ADD_FUSE_ARG ("-f");
- guestfs_set_trace (g, 1);
- guestfs_set_recovery_proc (g, 1);
- }
- else {
+ else if (STREQ (long_options[option_index].name, "format")) {
+ if (!optarg || STREQ (optarg, ""))
+ format = NULL;
+ else
+ format = optarg;
+ } else if (STREQ (long_options[option_index].name, "keys-from-stdin")) {
+ keys_from_stdin = 1;
+ } else if (STREQ (long_options[option_index].name, "echo-keys")) {
+ echo_keys = 1;
+ } else {
fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
program_name, long_options[option_index].name, option_index);
exit (EXIT_FAILURE);
break;
case 'a':
- if (access (optarg, R_OK) != 0) {
- perror (optarg);
- exit (EXIT_FAILURE);
- }
- drv = malloc (sizeof (struct drv));
- if (!drv) {
- perror ("malloc");
- exit (EXIT_FAILURE);
- }
- drv->filename = optarg;
- drv->next = drvs;
- drvs = drv;
+ OPTION_a;
+ break;
+
+ case 'c':
+ OPTION_c;
+ break;
+
+ case 'd':
+ OPTION_d;
+ break;
+
+ case 'i':
+ OPTION_i;
break;
case 'm':
- mp = malloc (sizeof (struct mp));
- if (!mp) {
- perror ("malloc");
- exit (EXIT_FAILURE);
- }
- p = strchr (optarg, ':');
- if (p) {
- *p = '\0';
- mp->mountpoint = p+1;
- } else
- mp->mountpoint = bad_cast ("/");
- mp->device = optarg;
- mp->next = mps;
- mps = mp;
+ OPTION_m;
break;
case 'n':
- guestfs_set_autosync (g, 0);
+ OPTION_n;
break;
case 'o':
break;
case 'r':
- read_only = 1;
+ OPTION_r;
break;
case 'v':
- verbose++;
- guestfs_set_verbose (g, verbose);
+ OPTION_v;
break;
case 'V':
- printf ("%s %s\n", program_name, PACKAGE_VERSION);
- exit (EXIT_SUCCESS);
+ OPTION_V;
+ break;
+
+ case 'w':
+ OPTION_w;
+ break;
+
+ case 'x':
+ OPTION_x;
+ ADD_FUSE_ARG ("-f");
+ guestfs_set_recovery_proc (g, 1);
+ trace_calls = 1;
+ break;
case HELP_OPTION:
usage (EXIT_SUCCESS);
}
}
- /* We must have at least one -a and at least one -m. */
- if (!drvs || !mps) {
+ /* Check we have the right options. */
+ if (!drvs || !(mps || inspector)) {
fprintf (stderr,
- _("%s: must have at least one -a and at least one -m option\n"),
+ _("%s: must have at least one -a/-d and at least one -m/-i option\n"),
program_name);
exit (EXIT_FAILURE);
}
}
/* Do the guest drives and mountpoints. */
- add_drives (drvs);
+ add_drives (drvs, 'a');
if (guestfs_launch (g) == -1)
exit (EXIT_FAILURE);
+ if (inspector)
+ inspect_mount ();
mount_mps (mps);
+ free_drives (drvs);
+ free_mps (mps);
+
/* FUSE example does this, not clear if it's necessary, but ... */
if (guestfs_umask (g, 0) == -1)
exit (EXIT_FAILURE);
exit (r == -1 ? 1 : 0);
}
-
-/* List is built in reverse order, so add them in reverse order. */
-static void
-add_drives (struct drv *drv)
-{
- int r;
-
- if (drv) {
- add_drives (drv->next);
- if (!read_only)
- r = guestfs_add_drive (g, drv->filename);
- else
- r = guestfs_add_drive_ro (g, drv->filename);
- if (r == -1)
- exit (EXIT_FAILURE);
- }
-}
-
-/* List is built in reverse order, so mount them in reverse order. */
-static void
-mount_mps (struct mp *mp)
-{
- int r;
-
- if (mp) {
- mount_mps (mp->next);
- if (!read_only)
- r = guestfs_mount (g, mp->device, mp->mountpoint);
- else
- r = guestfs_mount_ro (g, mp->device, mp->mountpoint);
- if (r == -1)
- exit (EXIT_FAILURE);
- }
-}