Files
libguestfs/lib/inspect-icon.c
Neil Hanlon 631962c0e8 Add detection support for Rocky Linux (CentOS/RHEL-like)
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2030709
Thanks: label@rockylinux.org

---

RWMJ notes: I fixed the original patch so it compiled.  This patch
sets osinfo to "rocky8", which doesn't exist in the osinfo db yet.
Arguably we might want to set this to "centos8", but we can see what
libosinfo decides to do.  Here is partial virt-inspector output on a
Rocky Linux disk image:

$ ./run virt-inspector -a disk.img
<?xml version="1.0"?>
<operatingsystems>
  <operatingsystem>
    <root>/dev/rl/root</root>
    <name>linux</name>
    <arch>x86_64</arch>
    <distro>rocky</distro>
    <product_name>Rocky Linux 8.5 (Green Obsidian)</product_name>
    <major_version>8</major_version>
    <minor_version>5</minor_version>
    <package_format>rpm</package_format>
    <package_management>dnf</package_management>
    <hostname>localhost.localdomain</hostname>
    <osinfo>rocky8</osinfo>
    <mountpoints>
      <mountpoint dev="/dev/rl/root">/</mountpoint>
      <mountpoint dev="/dev/sda1">/boot</mountpoint>
    </mountpoints>
    <filesystems>
      <filesystem dev="/dev/rl/root">
        <type>xfs</type>
        <uuid>fed8331f-9f25-40cd-883e-090cd640559d</uuid>
      </filesystem>
      <filesystem dev="/dev/rl/swap">
        <type>swap</type>
        <uuid>6da2c121-ea7d-49ce-98a3-14a37fceaadd</uuid>
      </filesystem>
      <filesystem dev="/dev/sda1">
        <type>xfs</type>
        <uuid>4efafe61-2d20-4d93-8055-537e09bfd033</uuid>
      </filesystem>
    </filesystems>
2021-12-10 09:09:47 +00:00

760 lines
21 KiB
C

/* libguestfs
* Copyright (C) 2011 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <libintl.h>
#include <sys/wait.h>
#include "guestfs.h"
#include "guestfs-internal.h"
#include "guestfs-internal-actions.h"
/* External tools are required for some icon types. Check we have them. */
#if defined(PBMTEXT) && defined (PNMTOPNG)
#define CAN_DO_CIRROS 1
#endif
#if defined(WRESTOOL) && defined(BMPTOPNM) && defined(PNMTOPNG) && \
defined(PAMCUT)
#define CAN_DO_WINDOWS 1
#endif
/* All these icon_*() functions return the same way. One of:
*
* ret == NULL:
* An error occurred. Error has been set in the handle. The caller
* should return NULL immediately.
*
* ret == NOT_FOUND:
* Not an error, but no icon was found. 'ret' is just a dummy value
* which should be ignored (do not free it!)
*
* ret == ordinary pointer:
* An icon was found. 'ret' points to the icon buffer, and *size_r
* is the size.
*/
static char *icon_favicon (guestfs_h *g, const char *type, size_t *size_r);
static char *icon_fedora (guestfs_h *g, size_t *size_r);
static char *icon_rhel (guestfs_h *g, int major, size_t *size_r);
static char *icon_debian (guestfs_h *g, size_t *size_r);
static char *icon_ubuntu (guestfs_h *g, size_t *size_r);
static char *icon_mageia (guestfs_h *g, size_t *size_r);
static char *icon_opensuse (guestfs_h *g, size_t *size_r);
#if CAN_DO_CIRROS
static char *icon_cirros (guestfs_h *g, size_t *size_r);
#endif
static char *icon_voidlinux (guestfs_h *g, size_t *size_r);
static char *icon_altlinux (guestfs_h *g, size_t *size_r);
static char *icon_gentoo (guestfs_h *g, size_t *size_r);
static char *icon_openmandriva (guestfs_h *g, size_t *size_r);
#if CAN_DO_WINDOWS
static char *icon_windows (guestfs_h *g, const char *root, size_t *size_r);
#endif
static char *case_sensitive_path_silently (guestfs_h *g, const char *path);
/* Dummy static object. */
static char *NOT_FOUND = (char *) "not_found";
/* For the unexpected legal consequences of this function, see:
* http://lists.fedoraproject.org/pipermail/legal/2011-April/001615.html
*
* Returns an RBufferOut, so the length of the returned buffer is
* returned in *size_r.
*
* Check optargs for the optional argument.
*/
char *
guestfs_impl_inspect_get_icon (guestfs_h *g, const char *root, size_t *size_r,
const struct guestfs_inspect_get_icon_argv *optargs)
{
char *r = NOT_FOUND;
int favicon, highquality;
size_t size;
CLEANUP_FREE char *type = NULL;
CLEANUP_FREE char *distro = NULL;
type = guestfs_inspect_get_type (g, root);
if (!type)
return NULL;
distro = guestfs_inspect_get_distro (g, root);
if (!distro)
return NULL;
/* Get optargs, or defaults. */
favicon =
optargs->bitmask & GUESTFS_INSPECT_GET_ICON_FAVICON_BITMASK ?
optargs->favicon : 1;
highquality =
optargs->bitmask & GUESTFS_INSPECT_GET_ICON_HIGHQUALITY_BITMASK ?
optargs->highquality : 0;
/* Favicons are never high quality, so ... */
if (highquality)
favicon = 0;
/* Try looking for a favicon first. */
if (favicon) {
r = icon_favicon (g, type, &size);
if (!r)
return NULL;
if (r != NOT_FOUND) {
/* try_favicon succeeded in finding a favicon. */
*size_r = size;
return r;
}
}
/* Favicon failed, so let's try a method based on the detected operating
* system.
*/
if (STREQ (type, "linux") || STREQ (type, "hurd")) {
if (STREQ (distro, "fedora")) {
r = icon_fedora (g, &size);
}
else if (STREQ (distro, "rhel") ||
STREQ (distro, "redhat-based") ||
STREQ (distro, "centos") ||
STREQ (distro, "rocky") ||
STREQ (distro, "scientificlinux") ||
STREQ (distro, "oraclelinux")) {
r = icon_rhel (g, guestfs_inspect_get_major_version (g, root), &size);
}
else if (STREQ (distro, "debian")) {
r = icon_debian (g, &size);
}
else if (STREQ (distro, "ubuntu")) {
if (!highquality)
r = icon_ubuntu (g, &size);
}
else if (STREQ (distro, "mageia")) {
r = icon_mageia (g, &size);
}
else if (STREQ (distro, "suse-based") ||
STREQ (distro, "opensuse") ||
STREQ (distro, "sles")) {
r = icon_opensuse (g, &size);
}
else if (STREQ (distro, "cirros")) {
#if CAN_DO_CIRROS
r = icon_cirros (g, &size);
#endif
}
else if (STREQ (distro, "voidlinux")) {
r = icon_voidlinux (g, &size);
}
else if (STREQ (distro, "altlinux")) {
r = icon_altlinux (g, &size);
}
else if (STREQ (distro, "gentoo")) {
r = icon_gentoo (g, &size);
}
else if (STREQ (distro, "openmandriva")) {
r = icon_openmandriva (g, &size);
}
}
else if (STREQ (type, "windows")) {
#if CAN_DO_WINDOWS
/* We don't know how to get high quality icons from a Windows guest,
* so disable this if high quality was specified.
*/
if (!highquality)
r = icon_windows (g, root, &size);
#endif
}
if (r == NOT_FOUND) {
/* Not found, but not an error. So return the special zero-length
* buffer. Use malloc(1) here to ensure that malloc won't return
* NULL.
*/
r = safe_malloc (g, 1);
size = 0;
}
*size_r = size;
return r;
}
/* Check that the named file 'filename' is a PNG file and is reasonable.
* If it is, download and return it.
*/
static char *
get_png (guestfs_h *g, const char *filename, size_t *size_r, size_t max_size)
{
char *ret;
CLEANUP_FREE char *real = NULL;
CLEANUP_FREE char *type = NULL;
CLEANUP_FREE char *local = NULL;
int r, w, h;
r = guestfs_is_file_opts (g, filename,
GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1);
if (r == -1)
return NULL; /* a real error */
if (r == 0)
return NOT_FOUND;
/* Resolve the path, in case it's a symbolic link (as in RHEL 7). */
guestfs_push_error_handler (g, NULL, NULL);
real = guestfs_realpath (g, filename);
guestfs_pop_error_handler (g);
if (real == NULL)
return NOT_FOUND; /* could just be a broken link */
/* Check the file type and geometry. */
type = guestfs_file (g, real);
if (!type)
return NOT_FOUND;
if (!STRPREFIX (type, "PNG image data, "))
return NOT_FOUND;
if (sscanf (&type[16], "%d x %d", &w, &h) != 2)
return NOT_FOUND;
if (w < 16 || h < 16 || w > 1024 || h > 1024)
return NOT_FOUND;
/* Define a maximum reasonable size based on the geometry. This
* also limits the maximum we allocate below to around 4 MB.
*/
if (max_size == 0)
max_size = 4 * w * h;
local = guestfs_int_download_to_tmp (g, real, "png", max_size);
if (!local)
return NOT_FOUND;
/* Successfully passed checks and downloaded. Read it into memory. */
if (guestfs_int_read_whole_file (g, local, &ret, size_r) == -1)
return NULL;
return ret;
}
static char *
find_png (guestfs_h *g, const char **filenames, size_t *size_r, size_t max_size)
{
size_t i;
char *ret;
for (i = 0; filenames[i] != NULL; ++i) {
ret = get_png (g, filenames[i], size_r, max_size);
if (ret == NULL)
return NULL;
if (ret != NOT_FOUND)
return ret;
}
return NOT_FOUND;
}
/* Return /etc/favicon.png (or \etc\favicon.png) if it exists and if
* it has a reasonable size and format.
*/
static char *
icon_favicon (guestfs_h *g, const char *type, size_t *size_r)
{
char *ret;
char *filename = safe_strdup (g, "/etc/favicon.png");
if (STREQ (type, "windows")) {
char *f = case_sensitive_path_silently (g, filename);
if (f) {
free (filename);
filename = f;
}
}
ret = get_png (g, filename, size_r, 0);
free (filename);
return ret;
}
/* Return FEDORA_ICON. I checked that this exists on at least Fedora 6
* through 16.
*/
#define FEDORA_ICON "/usr/share/icons/hicolor/96x96/apps/fedora-logo-icon.png"
static char *
icon_fedora (guestfs_h *g, size_t *size_r)
{
return get_png (g, FEDORA_ICON, size_r, 0);
}
/* RHEL 3, 4:
* /usr/share/pixmaps/redhat/shadowman-transparent.png is a 517x515
* PNG with alpha channel, around 64K in size.
*
* RHEL 5, 6:
* As above, but the file has been optimized to about 16K.
*
* In RHEL 7 the logos were completely broken (RHBZ#1063300).
*
* Conveniently the RHEL clones also have the same file with the
* same name, but containing their own logos. Sense prevails!
*
* Use a generic 100K limit for all the images, as logos in the
* RHEL clones have different sizes.
*/
static char *
icon_rhel (guestfs_h *g, int major, size_t *size_r)
{
const char *shadowman;
if (major < 7)
shadowman = "/usr/share/pixmaps/redhat/shadowman-transparent.png";
else
shadowman = "/usr/share/icons/hicolor/96x96/apps/system-logo-icon.png";
return get_png (g, shadowman, size_r, 102400);
}
#define DEBIAN_ICON "/usr/share/pixmaps/debian-logo.png"
static char *
icon_debian (guestfs_h *g, size_t *size_r)
{
return get_png (g, DEBIAN_ICON, size_r, 2048);
}
static char *
icon_ubuntu (guestfs_h *g, size_t *size_r)
{
const char *icons[] = {
"/usr/share/icons/gnome/24x24/places/ubuntu-logo.png",
/* Very low quality and only present when ubuntu-desktop packages
* have been installed.
*/
"/usr/share/help/C/ubuntu-help/figures/ubuntu-logo.png",
NULL
};
return find_png (g, icons, size_r, 2048);
}
#define MAGEIA_ICON "/usr/share/icons/mageia.png"
static char *
icon_mageia (guestfs_h *g, size_t *size_r)
{
return get_png (g, MAGEIA_ICON, size_r, 10240);
}
static char *
icon_opensuse (guestfs_h *g, size_t *size_r)
{
const char *icons[] = {
"/usr/share/icons/hicolor/48x48/apps/distributor.png",
"/usr/share/icons/hicolor/24x24/apps/distributor.png",
NULL
};
return find_png (g, icons, size_r, 10240);
}
#if CAN_DO_CIRROS
/* Cirros's logo is a text file! */
#define CIRROS_LOGO "/usr/share/cirros/logo"
static char *
icon_cirros (guestfs_h *g, size_t *size_r)
{
char *ret;
CLEANUP_FREE char *type = NULL;
CLEANUP_FREE char *local = NULL;
CLEANUP_FREE char *pngfile = NULL;
CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
int r;
r = guestfs_is_file_opts (g, CIRROS_LOGO,
GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1);
if (r == -1)
return NULL; /* a real error */
if (r == 0)
return NOT_FOUND;
/* Check the file type and geometry. */
type = guestfs_file (g, CIRROS_LOGO);
if (!type)
return NOT_FOUND;
if (!STRPREFIX (type, "ASCII text"))
return NOT_FOUND;
local = guestfs_int_download_to_tmp (g, CIRROS_LOGO, "png", 1024);
if (!local)
return NOT_FOUND;
/* Use pbmtext to render it. */
pngfile = guestfs_int_make_temp_path (g, "cirros", "png");
if (!pngfile)
return NOT_FOUND;
guestfs_int_cmd_add_string_unquoted (cmd, PBMTEXT " < ");
guestfs_int_cmd_add_string_quoted (cmd, local);
guestfs_int_cmd_add_string_unquoted (cmd, " | " PNMTOPNG " > ");
guestfs_int_cmd_add_string_quoted (cmd, pngfile);
r = guestfs_int_cmd_run (cmd);
if (r == -1)
return NULL;
if (!WIFEXITED (r) || WEXITSTATUS (r) != 0)
return NOT_FOUND;
/* Read it into memory. */
if (guestfs_int_read_whole_file (g, pngfile, &ret, size_r) == -1)
return NULL;
return ret;
}
#endif /* CAN_DO_CIRROS */
#define VOIDLINUX_ICON "/usr/share/void-artwork/void-logo.png"
static char *
icon_voidlinux (guestfs_h *g, size_t *size_r)
{
return get_png (g, VOIDLINUX_ICON, size_r, 20480);
}
#define ALTLINUX_ICON "/usr/share/icons/hicolor/48x48/apps/altlinux.png"
static char *
icon_altlinux (guestfs_h *g, size_t *size_r)
{
return get_png (g, ALTLINUX_ICON, size_r, 20480);
}
/* Installed by x11-themes/gentoo-artwork. */
#define GENTOO_ICON "/usr/share/icons/gentoo/48x48/gentoo.png"
static char *
icon_gentoo (guestfs_h *g, size_t *size_r)
{
return get_png (g, GENTOO_ICON, size_r, 10240);
}
static char *
icon_openmandriva (guestfs_h *g, size_t *size_r)
{
const char *icons[] = {
"/usr/share/icons/large/mandriva.png",
"/usr/share/icons/mandriva.png",
NULL
};
return find_png (g, icons, size_r, 10240);
}
#if CAN_DO_WINDOWS
/* Windows, as usual, has to be much more complicated and stupid than
* anything else.
*
* We have to download %systemroot%\explorer.exe and use a special
* program called 'wrestool' to extract the icons from this file. For
* each version of Windows, the icon we want is in a different place.
* The icon is in a stupid format (BMP), and in some cases multiple
* icons are in a single BMP file so we have to do some manipulation
* on the file.
*
* XXX I've only bothered with this nonsense for a few versions of
* Windows that I have handy. Please send patches to support other
* versions.
*/
static char *
icon_windows_xp (guestfs_h *g, const char *systemroot, size_t *size_r)
{
CLEANUP_FREE char *filename = NULL;
CLEANUP_FREE char *filename_case = NULL;
CLEANUP_FREE char *filename_downloaded = NULL;
CLEANUP_FREE char *pngfile = NULL;
CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
int r;
char *ret;
/* Download %systemroot%\explorer.exe */
filename = safe_asprintf (g, "%s/explorer.exe", systemroot);
filename_case = guestfs_case_sensitive_path (g, filename);
if (filename_case == NULL)
return NULL;
guestfs_push_error_handler (g, NULL, NULL);
r = guestfs_is_file (g, filename_case);
guestfs_pop_error_handler (g);
if (r == -1)
return NULL;
if (r == 0)
return NOT_FOUND;
filename_downloaded = guestfs_int_download_to_tmp (g, filename_case, "exe",
MAX_WINDOWS_EXPLORER_SIZE);
if (filename_downloaded == NULL)
return NOT_FOUND;
pngfile = guestfs_int_make_temp_path (g, "windows-xp-icon", "png");
if (!pngfile)
return NOT_FOUND;
guestfs_int_cmd_add_string_unquoted (cmd, WRESTOOL " -x --type=2 --name=143 ");
guestfs_int_cmd_add_string_quoted (cmd, filename_downloaded);
guestfs_int_cmd_add_string_unquoted (cmd,
" | " BMPTOPNM " | " PNMTOPNG " > ");
guestfs_int_cmd_add_string_quoted (cmd, pngfile);
r = guestfs_int_cmd_run (cmd);
if (r == -1)
return NULL;
if (!WIFEXITED (r) || WEXITSTATUS (r) != 0)
return NOT_FOUND;
if (guestfs_int_read_whole_file (g, pngfile, &ret, size_r) == -1)
return NULL;
return ret;
}
/* For Windows 7 we get the icon from explorer.exe. Prefer
* %systemroot%\SysWOW64\explorer.exe, a PE32 binary that usually
* contains the icons on 64 bit guests. Note the whole SysWOW64
* directory doesn't exist on 32 bit guests, so we have to be prepared
* for that.
*/
static const char *win7_explorer[] = {
"SysWOW64/explorer.exe",
"explorer.exe",
NULL
};
static char *
icon_windows_7 (guestfs_h *g, const char *systemroot, size_t *size_r)
{
size_t i;
CLEANUP_FREE char *filename_case = NULL;
CLEANUP_FREE char *filename_downloaded = NULL;
CLEANUP_FREE char *pngfile = NULL;
CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
int r;
char *ret;
for (i = 0; win7_explorer[i] != NULL; ++i) {
CLEANUP_FREE char *filename = NULL;
filename = safe_asprintf (g, "%s/%s", systemroot, win7_explorer[i]);
free (filename_case);
filename_case = case_sensitive_path_silently (g, filename);
if (filename_case == NULL)
continue;
guestfs_push_error_handler (g, NULL, NULL);
r = guestfs_is_file (g, filename_case);
guestfs_pop_error_handler (g);
if (r == -1)
return NULL;
if (r)
break;
}
if (win7_explorer[i] == NULL)
return NOT_FOUND;
filename_downloaded = guestfs_int_download_to_tmp (g, filename_case, "exe",
MAX_WINDOWS_EXPLORER_SIZE);
if (filename_downloaded == NULL)
return NOT_FOUND;
pngfile = guestfs_int_make_temp_path (g, "windows-7-icon", "png");
if (!pngfile)
return NOT_FOUND;
guestfs_int_cmd_add_string_unquoted (cmd,
WRESTOOL " -x --type=2 --name=6801 ");
guestfs_int_cmd_add_string_quoted (cmd, filename_downloaded);
guestfs_int_cmd_add_string_unquoted (cmd,
" | " BMPTOPNM " | "
PAMCUT " -bottom 54 | "
PNMTOPNG " > ");
guestfs_int_cmd_add_string_quoted (cmd, pngfile);
r = guestfs_int_cmd_run (cmd);
if (r == -1)
return NULL;
if (!WIFEXITED (r) || WEXITSTATUS (r) != 0)
return NOT_FOUND;
if (guestfs_int_read_whole_file (g, pngfile, &ret, size_r) == -1)
return NULL;
return ret;
}
/* There are several sources we might use:
* - /ProgramData/Microsoft/Windows Live/WLive48x48.png
* - w-brand.png (in a very long directory name)
* - /Windows/System32/slui.exe --type=14 group icon #2
*/
static char *
icon_windows_8 (guestfs_h *g, size_t *size_r)
{
CLEANUP_FREE char *filename_case = NULL;
CLEANUP_FREE char *filename_downloaded = NULL;
int r;
char *ret;
filename_case = case_sensitive_path_silently
(g, "/ProgramData/Microsoft/Windows Live/WLive48x48.png");
if (filename_case == NULL)
return NOT_FOUND; /* Not an error since a parent dir might not exist. */
guestfs_push_error_handler (g, NULL, NULL);
r = guestfs_is_file (g, filename_case);
guestfs_pop_error_handler (g);
if (r == -1)
return NULL;
if (r == 0)
return NOT_FOUND;
filename_downloaded = guestfs_int_download_to_tmp (g, filename_case, "png",
8192);
if (filename_downloaded == NULL)
return NOT_FOUND;
if (guestfs_int_read_whole_file (g, filename_downloaded, &ret, size_r) == -1)
return NULL;
return ret;
}
static char *
icon_windows (guestfs_h *g, const char *root, size_t *size_r)
{
CLEANUP_FREE char *systemroot =
guestfs_inspect_get_windows_systemroot (g, root);
int major = guestfs_inspect_get_major_version (g, root);
int minor = guestfs_inspect_get_minor_version (g, root);
if (systemroot == NULL)
return NOT_FOUND;
/* Windows XP. */
if (major == 5 && minor == 1)
return icon_windows_xp (g, systemroot, size_r);
/* Windows 7. */
else if (major == 6 && minor == 1)
return icon_windows_7 (g, systemroot, size_r);
/* Windows 8. */
else if (major == 6 && minor == 2)
return icon_windows_8 (g, size_r);
/* Not (yet) a supported version of Windows. */
else return NOT_FOUND;
}
#endif /* CAN_DO_WINDOWS */
/* NB: This function DOES NOT test for the existence of the file. It
* will return non-NULL even if the file/directory does not exist.
* You have to call guestfs_is_file{,_opts} etc.
*/
static char *
case_sensitive_path_silently (guestfs_h *g, const char *path)
{
char *ret;
guestfs_push_error_handler (g, NULL, NULL);
ret = guestfs_case_sensitive_path (g, path);
guestfs_pop_error_handler (g);
return ret;
}
/**
* Download a guest file to a local temporary file.
*
* The name of the temporary (downloaded) file is returned. The
* caller must free the pointer, but does I<not> need to delete the
* temporary file. It will be deleted when the handle is closed.
*
* The name of the temporary file is randomly generated, but an
* extension can be specified using C<extension> (or pass C<NULL> for none).
*
* Refuse to download the guest file if it is larger than C<max_size>.
* On this and other errors, C<NULL> is returned.
*/
char *
guestfs_int_download_to_tmp (guestfs_h *g, const char *filename,
const char *extension,
uint64_t max_size)
{
char *r;
int fd;
char devfd[32];
int64_t size;
r = guestfs_int_make_temp_path (g, "download", extension);
if (r == NULL)
return NULL;
/* Check size of remote file. */
size = guestfs_filesize (g, filename);
if (size == -1)
/* guestfs_filesize failed and has already set error in handle */
goto error;
if ((uint64_t) size > max_size) {
error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"),
filename, size);
goto error;
}
fd = open (r, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY|O_CLOEXEC, 0600);
if (fd == -1) {
perrorf (g, "open: %s", r);
goto error;
}
snprintf (devfd, sizeof devfd, "/dev/fd/%d", fd);
if (guestfs_download (g, filename, devfd) == -1) {
unlink (r);
close (fd);
goto error;
}
if (close (fd) == -1) {
perrorf (g, "close: %s", r);
unlink (r);
goto error;
}
return r;
error:
free (r);
return NULL;
}