diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/guestfs.pod | 115 | ||||
-rw-r--r-- | src/launch.c | 158 |
2 files changed, 232 insertions, 41 deletions
diff --git a/src/guestfs.pod b/src/guestfs.pod index d034c8e3..c1e595c7 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -124,7 +124,22 @@ disk, an actual block device, or simply an empty file of zeroes that you have created through L<posix_fallocate(3)>. Libguestfs lets you do useful things to all of these. -You can add a disk read-only using L</guestfs_add_drive_ro>, in which +The call you should use in modern code for adding drives is +L</guestfs_add_drive_opts>. To add a disk image, allowing writes, and +specifying that the format is raw, do: + + guestfs_add_drive_opts (g, filename, + GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", + -1); + +You can add a disk read-only using: + + guestfs_add_drive_opts (g, filename, + GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", + GUESTFS_ADD_DRIVE_OPTS_READONLY, 1, + -1); + +or by calling the older function L</guestfs_add_drive_ro>. In either case libguestfs won't modify the file. Be extremely cautious if the disk image is in use, eg. if it is being @@ -835,7 +850,8 @@ L</MULTIPLE HANDLES AND MULTIPLE THREADS> below. Create a connection handle. -You have to call L</guestfs_add_drive> on the handle at least once. +You have to call L</guestfs_add_drive_opts> (or one of the equivalent +calls) on the handle at least once. This function returns a non-NULL pointer to a handle on success or NULL on error. @@ -1050,6 +1066,101 @@ package versioning: Requires: libguestfs >= 1.0.80 +=head1 CALLS WITH OPTIONAL ARGUMENTS + +A recent feature of the API is the introduction of calls which take +optional arguments. In C these are declared 3 ways. The main way is +as a call which takes variable arguments (ie. C<...>), as in this +example: + + int guestfs_add_drive_opts (guestfs_h *g, const char *filename, ...); + +Call this with a list of optional arguments, terminated by C<-1>. +So to call with no optional arguments specified: + + guestfs_add_drive_opts (g, filename, -1); + +With a single optional argument: + + guestfs_add_drive_opts (g, filename, + GUESTFS_ADD_DRIVE_OPTS_FORMAT, "qcow2", + -1); + +With two: + + guestfs_add_drive_opts (g, filename, + GUESTFS_ADD_DRIVE_OPTS_FORMAT, "qcow2", + GUESTFS_ADD_DRIVE_OPTS_READONLY, 1, + -1); + +and so forth. Don't forget the terminating C<-1> otherwise +Bad Things will happen! + +=head2 USING va_list FOR OPTIONAL ARGUMENTS + +The second variant has the same name with the suffix C<_va>, which +works the same way but takes a C<va_list>. See the C manual for +details. For the example function, this is declared: + + int guestfs_add_drive_opts_va (guestfs_h *g, const char *filename, + va_list args); + +=head2 CONSTRUCTING OPTIONAL ARGUMENTS + +The third variant is useful where you need to construct these +calls. You pass in a structure where you fill in the optional +fields. The structure has a bitmask as the first element which +you must set to indicate which fields you have filled in. For +our example function the structure and call are declared: + + struct guestfs_add_drive_opts_argv { + uint64_t bitmask; + int readonly; + const char *format; + /* ... */ + }; + int guestfs_add_drive_opts_argv (guestfs_h *g, const char *filename, + const struct guestfs_add_drive_opts_argv *optargs); + +You could call it like this: + + struct guestfs_add_drive_opts_argv optargs = { + .bitmask = GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK | + GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK, + .readonly = 1, + .format = "qcow2" + }; + + guestfs_add_drive_opts_argv (g, filename, &optargs); + +Notes: + +=over 4 + +=item * + +The C<_BITMASK> suffix on each option name when specifying the +bitmask. + +=item * + +You do not need to fill in all fields of the structure. + +=item * + +There must be a one-to-one correspondence between fields of the +structure that are filled in, and bits set in the bitmask. + +=back + +=head2 OPTIONAL ARGUMENTS IN OTHER LANGUAGES + +In other languages, optional arguments are expressed in the +way that is natural for that language. We refer you to the +language-specific documentation for more details on that. + +For guestfish, see L<guestfish(1)/OPTIONAL ARGUMENTS>. + =begin html <!-- old anchor for the next section --> diff --git a/src/launch.c b/src/launch.c index f9d8329f..7f2f74ca 100644 --- a/src/launch.c +++ b/src/launch.c @@ -61,6 +61,7 @@ #include <arpa/inet.h> #include <netinet/in.h> +#include "c-ctype.h" #include "glthread/lock.h" #include "guestfs.h" @@ -131,64 +132,108 @@ guestfs__config (guestfs_h *g, return 0; } -int -guestfs__add_drive_with_if (guestfs_h *g, const char *filename, - const char *drive_if) +/* cache=off improves reliability in the event of a host crash. + * + * However this option causes qemu to try to open the file with + * O_DIRECT. This fails on some filesystem types (notably tmpfs). + * So we check if we can open the file with or without O_DIRECT, + * and use cache=off (or not) accordingly. + */ +static int +test_cache_off (guestfs_h *g, const char *filename) { - size_t len = strlen (filename) + 64; - char buf[len]; - - if (strchr (filename, ',') != NULL) { - error (g, _("filename cannot contain ',' (comma) character")); - return -1; + int fd = open (filename, O_RDONLY|O_DIRECT); + if (fd >= 0) { + close (fd); + return 1; } - /* cache=off improves reliability in the event of a host crash. - * - * However this option causes qemu to try to open the file with - * O_DIRECT. This fails on some filesystem types (notably tmpfs). - * So we check if we can open the file with or without O_DIRECT, - * and use cache=off (or not) accordingly. - * - * This test also checks for the presence of the file, which - * is a documented semantic of this interface. - */ - int fd = open (filename, O_RDONLY|O_DIRECT); + fd = open (filename, O_RDONLY); if (fd >= 0) { close (fd); - snprintf (buf, len, "file=%s,cache=off,if=%s", filename, drive_if); - } else { - fd = open (filename, O_RDONLY); - if (fd >= 0) { - close (fd); - snprintf (buf, len, "file=%s,if=%s", filename, drive_if); - } else { - perrorf (g, "%s", filename); - return -1; - } + return 0; } - return guestfs__config (g, "-drive", buf); + perrorf (g, "%s", filename); + return -1; +} + +/* Check string parameter matches ^[-_[:alnum:]]+$ (in C locale). */ +static int +valid_format_iface (const char *str) +{ + size_t len = strlen (str); + + if (len == 0) + return 0; + + while (len > 0) { + char c = *str++; + len--; + if (c != '-' && c != '_' && !c_isalnum (c)) + return 0; + } + return 1; } int -guestfs__add_drive_ro_with_if (guestfs_h *g, const char *filename, - const char *drive_if) +guestfs__add_drive_opts (guestfs_h *g, const char *filename, + const struct guestfs_add_drive_opts_argv *optargs) { + int readonly; + const char *format; + const char *iface; + if (strchr (filename, ',') != NULL) { error (g, _("filename cannot contain ',' (comma) character")); return -1; } - if (access (filename, F_OK) == -1) { - perrorf (g, "%s", filename); + readonly = optargs->bitmask & GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK + ? optargs->readonly : 0; + format = optargs->bitmask & GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK + ? optargs->format : NULL; + iface = optargs->bitmask & GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK + ? optargs->iface : DRIVE_IF; + + if (format && !valid_format_iface (format)) { + error (g, _("%s parameter is empty or contains disallowed characters"), + "format"); return -1; } + if (!valid_format_iface (iface)) { + error (g, _("%s parameter is empty or contains disallowed characters"), + "iface"); + return -1; + } + + /* For writable files, see if we can use cache=off. This also + * checks for the existence of the file. For readonly we have + * to do the check explicitly. + */ + int use_cache_off = readonly ? 0 : test_cache_off (g, filename); + if (use_cache_off == -1) + return -1; + + if (readonly) { + if (access (filename, F_OK) == -1) { + perrorf (g, "%s", filename); + return -1; + } + } - size_t len = strlen (filename) + 64; + /* Construct the final -drive parameter. */ + size_t len = 64 + strlen (filename) + strlen (iface); + if (format) len += strlen (format); char buf[len]; - snprintf (buf, len, "file=%s,snapshot=on,if=%s", filename, drive_if); + snprintf (buf, len, "file=%s%s%s%s%s,if=%s", + filename, + readonly ? ",snapshot=on" : "", + use_cache_off ? ",cache=off" : "", + format ? ",format=" : "", + format ? format : "", + iface); return guestfs__config (g, "-drive", buf); } @@ -196,13 +241,48 @@ guestfs__add_drive_ro_with_if (guestfs_h *g, const char *filename, int guestfs__add_drive (guestfs_h *g, const char *filename) { - return guestfs__add_drive_with_if (g, filename, DRIVE_IF); + struct guestfs_add_drive_opts_argv optargs = { + .bitmask = 0, + }; + + return guestfs__add_drive_opts (g, filename, &optargs); } int guestfs__add_drive_ro (guestfs_h *g, const char *filename) { - return guestfs__add_drive_ro_with_if (g, filename, DRIVE_IF); + struct guestfs_add_drive_opts_argv optargs = { + .bitmask = GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK, + .readonly = 1, + }; + + return guestfs__add_drive_opts (g, filename, &optargs); +} + +int +guestfs__add_drive_with_if (guestfs_h *g, const char *filename, + const char *iface) +{ + struct guestfs_add_drive_opts_argv optargs = { + .bitmask = GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK, + .iface = iface, + }; + + return guestfs__add_drive_opts (g, filename, &optargs); +} + +int +guestfs__add_drive_ro_with_if (guestfs_h *g, const char *filename, + const char *iface) +{ + struct guestfs_add_drive_opts_argv optargs = { + .bitmask = GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK + | GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK, + .iface = iface, + .readonly = 1, + }; + + return guestfs__add_drive_opts (g, filename, &optargs); } int |