/* 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
-#ifdef HAVE_GETTEXT
-#include "gettext.h"
-#define _(str) dgettext(PACKAGE, (str))
-//#define N_(str) dgettext(PACKAGE, (str))
-#else
-#define _(str) str
-//#define N_(str) str
-#endif
-
-static inline char *
-bad_cast (char const *s)
-{
- return (char *) s;
-}
-
-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;
-/* This is ugly: guestfs errors are strings, FUSE wants -errno. We
- * have to do the conversion as best we can.
- */
-#define MAX_ERRNO 256
-
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 *
dir_cache_invalidate (to);
- r = guestfs_ln_s (g, to, from);
+ r = guestfs_ln_s (g, from, to);
if (r == -1)
return error ();
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
if (read_only) return -EROFS;
+ dir_cache_invalidate (from);
dir_cache_invalidate (to);
- r = guestfs_ln (g, to, from);
+ r = guestfs_ln (g, from, to);
if (r == -1)
return error ();
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)
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
.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)
{
const char *tmp_argv[] = { program_name, "--help", NULL };
fuse_main (2, (char **) tmp_argv, &fg_operations, NULL);
- exit (0);
+ exit (EXIT_SUCCESS);
}
static void __attribute__((noreturn))
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;
fuse_argv = realloc (fuse_argv, (1+fuse_argc) * sizeof (char *)); \
if (!fuse_argv) { \
perror ("realloc"); \
- exit (1); \
+ exit (EXIT_FAILURE); \
} \
fuse_argv[fuse_argc-1] = (str); \
fuse_argv[fuse_argc] = NULL; \
g = guestfs_create ();
if (g == NULL) {
fprintf (stderr, _("guestfs_create: failed to create handle\n"));
- exit (1);
+ 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 (1);
+ exit (EXIT_FAILURE);
}
break;
case 'a':
- if (access (optarg, R_OK) != 0) {
- perror (optarg);
- exit (1);
- }
- drv = malloc (sizeof (struct drv));
- if (!drv) {
- perror ("malloc");
- exit (1);
- }
- 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 (1);
- }
- 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 (0);
+ OPTION_V;
+ break;
+
+ case 'w':
+ OPTION_w;
+ break;
+
+ case 'x':
+ OPTION_x;
+ ADD_FUSE_ARG ("-f");
+ guestfs_set_recovery_proc (g, 1);
+ break;
case HELP_OPTION:
- usage (0);
+ usage (EXIT_SUCCESS);
default:
- usage (1);
+ usage (EXIT_FAILURE);
}
}
- /* 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 (1);
+ exit (EXIT_FAILURE);
}
/* We'd better have a mountpoint. */
fprintf (stderr,
_("%s: you must specify a mountpoint in the host filesystem\n"),
program_name);
- exit (1);
+ exit (EXIT_FAILURE);
}
/* Do the guest drives and mountpoints. */
- add_drives (drvs);
+ add_drives (drvs, 'a');
if (guestfs_launch (g) == -1)
- exit (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 (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
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 (1);
- }
-}
-
-/* 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 (1);
- }
-}