/* guestmount - mount guests using libguestfs and FUSE
- * Copyright (C) 2009-2010 Red Hat Inc.
+ * Copyright (C) 2009-2011 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 <sys/time.h>
#include <sys/types.h>
#include <locale.h>
+#include <libintl.h>
#include <fuse.h>
#include <guestfs.h>
guestfs_h *g = NULL;
int read_only = 0;
+int live = 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;
+
+#define TRACE_CALL(fs,...) \
+ if (trace_calls) { \
+ fprintf (stderr, "%s: %s (" fs ")\n", \
+ program_name, __func__, __VA_ARGS__); \
+ }
static int
error (void)
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);
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;
free_attrs = 1;
}
+ /* Find the matching attribute (index in 'i'). */
+ ssize_t r;
size_t i;
- int r = -ENOATTR;
for (i = 0; i < xattrs->len; ++i) {
- if (STREQ (xattrs->val[i].attrname, name)) {
- size_t sz = xattrs->val[i].attrval_len;
- if (sz > size)
- sz = size;
- memcpy (value, xattrs->val[i].attrval, sz);
- r = 0;
+ 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);
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;
free_attrs = 1;
}
+ /* Calculate how much space is required to hold the result. */
+ size_t space = 0;
+ size_t len;
size_t i;
- ssize_t copied = 0;
for (i = 0; i < xattrs->len; ++i) {
- size_t len = strlen (xattrs->val[i].attrname) + 1;
+ 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;
- copied += len;
+ r += len;
} else {
- copied = -ERANGE;
+ r = -ERANGE;
break;
}
}
+ out:
if (free_attrs)
guestfs_free_xattr_list ((struct guestfs_xattr_list *) xattrs);
- return copied;
+ 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;
" -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"
+ " --live Connect to a live virtual machine\n"
+ " -m|--mount dev[:mnt[:opts]] 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"
" -v|--verbose Verbose messages\n"
" -V|--version Display version and exit\n"
+ " -w|--rw Mount read-write\n"
" -x|--trace Trace guestfs API calls\n"
),
program_name, program_name, program_name);
bindtextdomain (PACKAGE, LOCALEBASEDIR);
textdomain (PACKAGE);
+ parse_config ();
+
enum { HELP_OPTION = CHAR_MAX + 1 };
/* The command line arguments are broadly compatible with (a subset
{ "help", 0, 0, HELP_OPTION },
{ "inspector", 0, 0, 'i' },
{ "keys-from-stdin", 0, 0, 0 },
+ { "live", 0, 0, 0 },
{ "mount", 1, 0, 'm' },
{ "no-sync", 0, 0, 'n' },
{ "option", 1, 0, 'o' },
*/
ADD_FUSE_ARG ("-s");
- /* If developing, add ./appliance to the path. Note that libtools
- * interferes with this because uninstalled guestfish is a shell
- * script that runs the real program with an absolute path. Detect
- * that too.
- *
- * BUT if LIBGUESTFS_PATH environment variable is already set by
- * the user, then don't override it.
- */
- if (getenv ("LIBGUESTFS_PATH") == NULL &&
- argv[0] &&
- (argv[0][0] != '/' || strstr (argv[0], "/.libs/lt-") != NULL))
- guestfs_set_path (g, "appliance:" GUESTFS_DEFAULT_PATH);
-
for (;;) {
c = getopt_long (argc, argv, options, long_options, &option_index);
if (c == -1) break;
keys_from_stdin = 1;
} else if (STREQ (long_options[option_index].name, "echo-keys")) {
echo_keys = 1;
+ } else if (STREQ (long_options[option_index].name, "live")) {
+ live = 1;
} else {
fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
program_name, long_options[option_index].name, option_index);
OPTION_x;
ADD_FUSE_ARG ("-f");
guestfs_set_recovery_proc (g, 1);
+ trace_calls = 1;
break;
case HELP_OPTION: