From 83f74f5c564c51a1324b12e5edc4b50356d49dc6 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Sat, 11 May 2013 18:18:12 +0100 Subject: [PATCH] Add support for qemu's curl driver (ie. FTP, FTPS, HTTP, HTTPS, TFTP). --- fish/guestfish.pod | 16 +++++++ generator/actions.ml | 12 ++++- src/drives.c | 99 ++++++++++++++++++++++++++++++++++++++++++ src/guestfs-internal.h | 5 +++ src/guestfs.pod | 23 ++++++++++ src/launch-libvirt.c | 18 ++++++++ 6 files changed, 171 insertions(+), 2 deletions(-) diff --git a/fish/guestfish.pod b/fish/guestfish.pod index d32e747f6..f8b39fc09 100644 --- a/fish/guestfish.pod +++ b/fish/guestfish.pod @@ -1117,6 +1117,22 @@ The possible I<-a URI> formats are described below. Add the local disk image (or device) called C. +=head2 B<-a ftp://[user@]example.com[:port]/disk.img> + +=head2 B<-a ftps://[user@]example.com[:port]/disk.img> + +=head2 B<-a http://[user@]example.com[:port]/disk.img> + +=head2 B<-a https://[user@]example.com[:port]/disk.img> + +=head2 B<-a tftp://[user@]example.com[:port]/disk.img> + +Add a disk located on a remote FTP, HTTP or TFTP server. + +The equivalent API command would be: + + > add /disk.img protocol:(ftp|...) server:tcp:example.com + =head2 B<-a gluster://example.com[:port]/disk> Add a disk image located on GlusterFS storage. diff --git a/generator/actions.ml b/generator/actions.ml index f518ecdfc..277ca27a1 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -1334,6 +1334,13 @@ C is interpreted as a local file or device. This is the default if the optional protocol parameter is omitted. +=item C + +Connect to a remote FTP, HTTP or TFTP server. +The C parameter must also be supplied - see below. + +See also: L + =item C Connect to the GlusterFS server. @@ -1390,6 +1397,7 @@ is a list of server(s). Protocol Number of servers required -------- -------------------------- file List must be empty or param not used at all + ftp|ftps|http|https|tftp Exactly one gluster Exactly one iscsi Exactly one nbd Exactly one @@ -1411,8 +1419,8 @@ for the protocol is used (see C). =item C -For the C, C, C protocols only, this specifies the -remote username. +For the C, C, C, C, C, C, C +and C protocols, this specifies the remote username. If not given, then the local username is used for C, and no authentication is attempted for ceph. But note this sometimes may give unexpected results, for diff --git a/src/drives.c b/src/drives.c index acfb0bb0b..d12605465 100644 --- a/src/drives.c +++ b/src/drives.c @@ -137,6 +137,50 @@ create_drive_non_file (guestfs_h *g, return drv; } +static struct drive * +create_drive_curl (guestfs_h *g, + enum drive_protocol protocol, + struct drive_server *servers, size_t nr_servers, + const char *exportname, + const char *username, const char *secret, + bool readonly, const char *format, + const char *iface, const char *name, + const char *disk_label, + bool use_cache_none) +{ + if (secret != NULL) { + error (g, _("curl: you cannot specify a secret with this protocol")); + return NULL; + } + + if (nr_servers != 1) { + error (g, _("curl: you must specify exactly one server")); + return NULL; + } + + if (servers[0].transport != drive_transport_none && + servers[0].transport != drive_transport_tcp) { + error (g, _("curl: only tcp transport is supported")); + return NULL; + } + + if (STREQ (exportname, "")) { + error (g, _("curl: pathname should not be an empty string")); + return NULL; + } + + if (exportname[0] != '/') { + error (g, _("curl: pathname must begin with a '/'")); + return NULL; + } + + return create_drive_non_file (g, protocol, + servers, nr_servers, exportname, + username, secret, + readonly, format, iface, name, disk_label, + use_cache_none); +} + static struct drive * create_drive_gluster (guestfs_h *g, struct drive_server *servers, size_t nr_servers, @@ -871,12 +915,40 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename, disk_label, use_cache_none); } } + else if (STREQ (protocol, "ftp")) { + drv = create_drive_curl (g, drive_protocol_ftp, + servers, nr_servers, filename, + username, secret, + readonly, format, iface, name, + disk_label, false); + } + else if (STREQ (protocol, "ftps")) { + drv = create_drive_curl (g, drive_protocol_ftps, + servers, nr_servers, filename, + username, secret, + readonly, format, iface, name, + disk_label, false); + } else if (STREQ (protocol, "gluster")) { drv = create_drive_gluster (g, servers, nr_servers, filename, username, secret, readonly, format, iface, name, disk_label, false); } + else if (STREQ (protocol, "http")) { + drv = create_drive_curl (g, drive_protocol_http, + servers, nr_servers, filename, + username, secret, + readonly, format, iface, name, + disk_label, false); + } + else if (STREQ (protocol, "https")) { + drv = create_drive_curl (g, drive_protocol_https, + servers, nr_servers, filename, + username, secret, + readonly, format, iface, name, + disk_label, false); + } else if (STREQ (protocol, "iscsi")) { drv = create_drive_iscsi (g, servers, nr_servers, filename, username, secret, @@ -907,6 +979,13 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename, readonly, format, iface, name, disk_label, false); } + else if (STREQ (protocol, "tftp")) { + drv = create_drive_curl (g, drive_protocol_tftp, + servers, nr_servers, filename, + username, secret, + readonly, format, iface, name, + disk_label, false); + } else { error (g, _("unknown protocol '%s'"), protocol); drv = NULL; /*FALLTHROUGH*/ @@ -1175,6 +1254,14 @@ guestfs___drive_source_qemu_param (guestfs_h *g, const struct drive_source *src) else return safe_asprintf (g, "./%s", src->u.path); + case drive_protocol_ftp: + return make_uri (g, "ftp", src->username, + &src->servers[0], src->u.exportname); + + case drive_protocol_ftps: + return make_uri (g, "ftps", src->username, + &src->servers[0], src->u.exportname); + case drive_protocol_gluster: switch (src->servers[0].transport) { case drive_transport_none: @@ -1186,6 +1273,14 @@ guestfs___drive_source_qemu_param (guestfs_h *g, const struct drive_source *src) return make_uri (g, "gluster+unix", NULL, &src->servers[0], NULL); } + case drive_protocol_http: + return make_uri (g, "http", src->username, + &src->servers[0], src->u.exportname); + + case drive_protocol_https: + return make_uri (g, "https", src->username, + &src->servers[0], src->u.exportname); + case drive_protocol_iscsi: return make_uri (g, "iscsi", NULL, &src->servers[0], src->u.exportname); @@ -1277,6 +1372,10 @@ guestfs___drive_source_qemu_param (guestfs_h *g, const struct drive_source *src) case drive_protocol_ssh: return make_uri (g, "ssh", src->username, &src->servers[0], src->u.exportname); + + case drive_protocol_tftp: + return make_uri (g, "tftp", src->username, + &src->servers[0], src->u.exportname); } abort (); diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index c2681fb1a..6e97948e1 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -115,12 +115,17 @@ struct event { /* Drives added to the handle. */ enum drive_protocol { drive_protocol_file, + drive_protocol_ftp, + drive_protocol_ftps, drive_protocol_gluster, + drive_protocol_http, + drive_protocol_https, drive_protocol_iscsi, drive_protocol_nbd, drive_protocol_rbd, drive_protocol_sheepdog, drive_protocol_ssh, + drive_protocol_tftp, }; enum drive_transport { diff --git a/src/guestfs.pod b/src/guestfs.pod index 1477015f9..381f0f706 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -661,6 +661,29 @@ servers. The server string is documented in L. The C and C parameters are also optional, and if not given, then no authentication will be used. +=head3 FTP, HTTP AND TFTP + +Libguestfs can access remote disks over FTP, FTPS, HTTP, HTTPS +or TFTP protocols. + +To do this, set the optional C and C parameters of +L like this: + + char **servers = { "www.example.org", NULL }; + guestfs_add_drive_opts (g, "/disk.img", + GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", + GUESTFS_ADD_DRIVE_OPTS_PROTOCOL, "http", + GUESTFS_ADD_DRIVE_OPTS_SERVER, servers, + -1); + +The C can be one of C<"ftp">, C<"ftps">, C<"http">, +C<"https"> or C<"tftp">. + +C (the C parameter) is a list which must have a +single element. The single element is a string defining the Gluster +server. The format of this string is documented in +L. + =head3 GLUSTER Libguestfs can access Gluster disks. diff --git a/src/launch-libvirt.c b/src/launch-libvirt.c index d667e9889..4588602e8 100644 --- a/src/launch-libvirt.c +++ b/src/launch-libvirt.c @@ -1151,6 +1151,18 @@ construct_libvirt_xml_disk (guestfs_h *g, */ XMLERROR (-1, xmlTextWriterEndElement (xo)); } + break; + + /* libvirt doesn't support the qemu curl driver yet. Give a + * reasonable error message instead of trying and failing. + */ + case drive_protocol_ftp: + case drive_protocol_ftps: + case drive_protocol_http: + case drive_protocol_https: + case drive_protocol_tftp: + error (g, _("libvirt does not support the qemu curl driver protocols (ftp, http, etc.); try setting LIBGUESTFS_BACKEND=direct")); + return -1; } XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "target")); @@ -1599,12 +1611,17 @@ make_drive_priv (guestfs_h *g, struct drive *drv, } break; + case drive_protocol_ftp: + case drive_protocol_ftps: case drive_protocol_gluster: + case drive_protocol_http: + case drive_protocol_https: case drive_protocol_iscsi: case drive_protocol_nbd: case drive_protocol_rbd: case drive_protocol_sheepdog: case drive_protocol_ssh: + case drive_protocol_tftp: if (!drv->readonly) { guestfs___copy_drive_source (g, &drv->src, &drv_priv->real_src); drv_priv->format = drv->format ? safe_strdup (g, drv->format) : NULL; @@ -1621,6 +1638,7 @@ make_drive_priv (guestfs_h *g, struct drive *drv, return -1; drv_priv->format = safe_strdup (g, "qcow2"); } + break; } return 0;