int do_copy_file_to_file (const char *src, const char *dest, int64_t srcoffset, int64_t destoffset, int64_t size, int sparse, int append) { CLEANUP_FREE char *src_buf = NULL, *dest_buf = NULL; int wrflags = O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC; src_buf = sysroot_path (src); if (!src_buf) { reply_with_perror ("malloc"); return -1; } dest_buf = sysroot_path (dest); if (!dest_buf) { reply_with_perror ("malloc"); return -1; } if ((optargs_bitmask & GUESTFS_COPY_FILE_TO_FILE_APPEND_BITMASK) && append) wrflags |= O_APPEND; else wrflags |= O_TRUNC; return copy (src_buf, src, dest_buf, dest, wrflags, 0666, COPY_UNLINK_DEST_ON_FAILURE, srcoffset, destoffset, size, sparse); }
/* If the network is enabled, we want <sysroot>/etc/resolv.conf to * reflect the contents of /etc/resolv.conf so that name resolution * works. It would be nice to bind-mount the file (single file bind * mounts are possible). However annoyingly that doesn't work for * Ubuntu guests where the guest resolv.conf is a dangling symlink, * and for reasons unknown mount tries to follow the symlink and * fails (likely a bug). So this is a hack. Note we only invoke * this if the network is enabled. */ static int set_up_etc_resolv_conf (struct resolver_state *rs) { struct stat statbuf; CLEANUP_FREE char *buf = NULL; rs->sysroot_etc_resolv_conf_old = NULL; rs->sysroot_etc_resolv_conf = sysroot_path ("/etc/resolv.conf"); if (!rs->sysroot_etc_resolv_conf) { reply_with_perror ("malloc"); goto error; } /* If /etc/resolv.conf exists, rename it to the backup file. Note * that on Ubuntu it's a dangling symlink. */ if (lstat (rs->sysroot_etc_resolv_conf, &statbuf) == 0) { /* Make a random name for the backup file. */ if (asprintf (&buf, "%s/etc/XXXXXXXX", sysroot) == -1) { reply_with_perror ("asprintf"); goto error; } if (random_name (buf) == -1) { reply_with_perror ("random_name"); goto error; } rs->sysroot_etc_resolv_conf_old = strdup (buf); if (!rs->sysroot_etc_resolv_conf_old) { reply_with_perror ("strdup"); goto error; } if (verbose) fprintf (stderr, "renaming %s to %s\n", rs->sysroot_etc_resolv_conf, rs->sysroot_etc_resolv_conf_old); if (rename (rs->sysroot_etc_resolv_conf, rs->sysroot_etc_resolv_conf_old) == -1) { reply_with_perror ("rename: %s to %s", rs->sysroot_etc_resolv_conf, rs->sysroot_etc_resolv_conf_old); goto error; } } /* Now that the guest's <sysroot>/etc/resolv.conf is out the way, we * can create our own copy of the appliance /etc/resolv.conf. */ ignore_value (command (NULL, NULL, str_cp, "/etc/resolv.conf", rs->sysroot_etc_resolv_conf, NULL)); rs->mounted = true; return 0; error: free (rs->sysroot_etc_resolv_conf); free (rs->sysroot_etc_resolv_conf_old); return -1; }
int64_t do_inotify_add_watch (const char *path, int mask) { #ifdef HAVE_SYS_INOTIFY_H int64_t r; char *buf; NEED_INOTIFY (-1); buf = sysroot_path (path); if (!buf) { reply_with_perror ("malloc"); return -1; } r = inotify_add_watch (inotify_fd, buf, mask); free (buf); if (r == -1) { reply_with_perror ("%s", path); return -1; } return r; #else NOT_AVAILABLE (-1); #endif }
int do_hivex_commit (const char *filename) { CLEANUP_FREE char *buf = NULL; NEED_HANDLE (-1); /* The 'filename' parameter is an optional string, and in most * cases will be NULL. */ if (filename) { buf = sysroot_path (filename); if (!buf) { reply_with_perror ("malloc"); return -1; } if (hivex_commit (h, buf, 0) == -1) { reply_with_perror ("%s: commit failed", filename); return -1; } } else { if (hivex_commit (h, NULL, 0) == -1) { reply_with_perror ("commit failed"); return -1; } } return 0; }
guestfs_int_xfsinfo * do_xfs_info (const char *pathordevice) { int r; CLEANUP_FREE char *buf = NULL; CLEANUP_FREE char *out = NULL, *err = NULL; CLEANUP_FREE_STRING_LIST char **lines = NULL; int is_dev; is_dev = STREQLEN (pathordevice, "/dev/", 5); buf = is_dev ? strdup (pathordevice) : sysroot_path (pathordevice); if (buf == NULL) { reply_with_perror ("malloc"); return NULL; } r = command (&out, &err, str_xfs_info, buf, NULL); if (r == -1) { reply_with_error ("%s", err); return NULL; } lines = split_lines (out); if (lines == NULL) return NULL; return parse_xfs_info (lines); }
/* We need to rewrite the root path so it is based at /sysroot. */ int do_aug_init (const char *root, int flags) { #ifdef HAVE_AUGEAS char *buf; if (aug) { aug_close (aug); aug = NULL; } buf = sysroot_path (root); if (!buf) { reply_with_perror ("malloc"); return -1; } aug = aug_init (buf, NULL, flags); free (buf); if (!aug) { reply_with_error ("Augeas initialization failed"); return -1; } return 0; #else NOT_AVAILABLE (-1); #endif }
int64_t do_get_e2generation (const char *filename) { int r; CLEANUP_FREE char *buf = NULL, *out = NULL, *err = NULL; int64_t ret; buf = sysroot_path (filename); if (!buf) { reply_with_perror ("malloc"); return -1; } r = command (&out, &err, str_lsattr, "-dv", "--", buf, NULL); if (r == -1) { reply_with_error ("%s: %s: %s", "lsattr", filename, err); return -1; } if (sscanf (out, "%" SCNi64, &ret) != 1) { reply_with_error ("cannot parse output from '%s' command: %s", "lsattr", out); return -1; } if (ret < 0) { reply_with_error ("unexpected negative number from '%s' command: %s", "lsattr", out); return -1; } return ret; }
/* This implementation is quick and dirty, and allows people to try * to remove parts of the initramfs (eg. "rm -r /..") but if people * do stupid stuff, who are we to try to stop them? */ int do_rm_rf (const char *path) { int r; char *buf, *err; if (STREQ (path, "/")) { reply_with_error ("cannot remove root directory"); return -1; } buf = sysroot_path (path); if (buf == NULL) { reply_with_perror ("malloc"); return -1; } r = command (NULL, &err, str_rm, "-rf", buf, NULL); free (buf); /* rm -rf is never supposed to fail. I/O errors perhaps? */ if (r == -1) { reply_with_error ("%s: %s", path, err); free (err); return -1; } free (err); return 0; }
int64_t do_du (const char *path) { int r; int64_t rv; CLEANUP_FREE char *out = NULL, *err = NULL, *buf = NULL; /* Make the path relative to /sysroot. */ buf = sysroot_path (path); if (!buf) { reply_with_perror ("malloc"); return -1; } pulse_mode_start (); r = command (&out, &err, str_du, "-s", buf, NULL); if (r == -1) { pulse_mode_cancel (); reply_with_error ("%s: %s", path, err); return -1; } if (sscanf (out, "%"SCNi64, &rv) != 1) { pulse_mode_cancel (); reply_with_error ("%s: could not read output: %s", path, out); return -1; } pulse_mode_end (); return rv; }
/* Takes optional arguments, consult optargs_bitmask. */ int do_rsync (const char *src_orig, const char *dest_orig, int archive, int deletedest) { CLEANUP_FREE char *src = NULL, *dest = NULL; src = sysroot_path (src_orig); dest = sysroot_path (dest_orig); if (!src || !dest) { reply_with_perror ("malloc"); return -1; } if (!(optargs_bitmask & GUESTFS_RSYNC_ARCHIVE_BITMASK)) archive = 0; if (!(optargs_bitmask & GUESTFS_RSYNC_DELETEDEST_BITMASK)) deletedest = 0; return rsync (src, src_orig, dest, dest_orig, archive, deletedest); }
int do_copy_file_to_file (const char *src, const char *dest, int64_t srcoffset, int64_t destoffset, int64_t size, int sparse) { CLEANUP_FREE char *src_buf = NULL, *dest_buf = NULL; src_buf = sysroot_path (src); if (!src_buf) { reply_with_perror ("malloc"); return -1; } dest_buf = sysroot_path (dest); if (!dest_buf) { reply_with_perror ("malloc"); return -1; } return copy (src_buf, src, dest_buf, dest, DEST_FILE_FLAGS, srcoffset, destoffset, size, sparse); }
/* Resolve path within sysroot, calling sysroot_path on the resolved path. * * Caller must check for NULL and call reply_with_perror ("malloc/realpath") * if it is. Caller must also free the string. * * See also the custom %R printf formatter which does shell quoting too. */ char * sysroot_realpath (const char *path) { CLEANUP_FREE char *rp = NULL; CHROOT_IN; rp = realpath (path, NULL); CHROOT_OUT; if (rp == NULL) return NULL; return sysroot_path (rp); }
int do_swapoff_file (const char *path) { CLEANUP_FREE char *buf = NULL; buf = sysroot_path (path); if (!buf) { reply_with_perror ("malloc"); return -1; } return swaponoff ("swapoff", NULL, buf); }
guestfs_int_isoinfo * do_isoinfo (const char *path) { guestfs_int_isoinfo *ret; CLEANUP_FREE char *buf = sysroot_path (path); if (!buf) { reply_with_perror ("malloc"); return NULL; } ret = isoinfo (buf); return ret; }
int do_copy_file_to_device (const char *src, const char *dest, int64_t srcoffset, int64_t destoffset, int64_t size, int sparse) { CLEANUP_FREE char *src_buf = sysroot_path (src); if (!src_buf) { reply_with_perror ("malloc"); return -1; } return copy (src_buf, src, dest, dest, DEST_DEVICE_FLAGS, 0, srcoffset, destoffset, size, sparse); }
static int cpmv_cmd (const char *cmd, const char *flags, const char *src, const char *dest) { CLEANUP_FREE char *srcbuf = NULL, *destbuf = NULL; CLEANUP_FREE char *err = NULL; int r; srcbuf = sysroot_path (src); if (srcbuf == NULL) { reply_with_perror ("malloc"); return -1; } destbuf = sysroot_path (dest); if (destbuf == NULL) { reply_with_perror ("malloc"); return -1; } pulse_mode_start (); if (flags) r = command (NULL, &err, cmd, flags, srcbuf, destbuf, NULL); else r = command (NULL, &err, cmd, srcbuf, destbuf, NULL); if (r == -1) { pulse_mode_cancel (); reply_with_error ("%s", err); return -1; } pulse_mode_end (); return 0; }
/* While running the command, bind-mount /dev, /proc, /sys * into the chroot. However we must be careful to unmount them * afterwards because otherwise they would interfere with * future mount and unmount operations. * * We deliberately allow these commands to fail silently, BUT * if a mount fails, don't unmount the corresponding mount. */ static int bind_mount (struct bind_state *bs) { int r; memset (bs, 0, sizeof *bs); bs->sysroot_dev = sysroot_path ("/dev"); bs->sysroot_dev_pts = sysroot_path ("/dev/pts"); bs->sysroot_proc = sysroot_path ("/proc"); bs->sysroot_selinux = sysroot_path ("/selinux"); bs->sysroot_sys = sysroot_path ("/sys"); bs->sysroot_sys_fs_selinux = sysroot_path ("/sys/fs/selinux"); if (bs->sysroot_dev == NULL || bs->sysroot_dev_pts == NULL || bs->sysroot_proc == NULL || bs->sysroot_selinux == NULL || bs->sysroot_sys == NULL || bs->sysroot_sys_fs_selinux == NULL) { reply_with_perror ("malloc"); free (bs->sysroot_dev); free (bs->sysroot_dev_pts); free (bs->sysroot_proc); free (bs->sysroot_selinux); free (bs->sysroot_sys); free (bs->sysroot_sys_fs_selinux); return -1; } /* Note it is tempting to use --rbind here (to bind submounts). * However I have not found a reliable way to unmount the same set * of directories (umount -R does NOT work). */ r = command (NULL, NULL, str_mount, "--bind", "/dev", bs->sysroot_dev, NULL); bs->dev_ok = r != -1; r = command (NULL, NULL, str_mount, "--bind", "/dev/pts", bs->sysroot_dev_pts, NULL); bs->dev_pts_ok = r != -1; r = command (NULL, NULL, str_mount, "--bind", "/proc", bs->sysroot_proc, NULL); bs->proc_ok = r != -1; /* Note on the next line we have to bind-mount /sys/fs/selinux (appliance * kernel) on top of /selinux (where guest is expecting selinux). */ r = command (NULL, NULL, str_mount, "--bind", "/sys/fs/selinux", bs->sysroot_selinux, NULL); bs->selinux_ok = r != -1; r = command (NULL, NULL, str_mount, "--bind", "/sys", bs->sysroot_sys, NULL); bs->sys_ok = r != -1; r = command (NULL, NULL, str_mount, "--bind", "/sys/fs/selinux", bs->sysroot_sys_fs_selinux, NULL); bs->sys_fs_selinux_ok = r != -1; bs->mounted = true; return 0; }
/* Takes optional arguments, consult optargs_bitmask. */ int do_rsync_in (const char *remote, const char *dest_orig, int archive, int deletedest) { CLEANUP_FREE char *dest = NULL; dest = sysroot_path (dest_orig); if (!dest) { reply_with_perror ("malloc"); return -1; } if (!(optargs_bitmask & GUESTFS_RSYNC_IN_ARCHIVE_BITMASK)) archive = 0; if (!(optargs_bitmask & GUESTFS_RSYNC_IN_DELETEDEST_BITMASK)) deletedest = 0; return rsync (remote, remote, dest, dest_orig, archive, deletedest); }
int do_copy_file_to_device (const char *src, const char *dest, int64_t srcoffset, int64_t destoffset, int64_t size, int sparse, int append) { CLEANUP_FREE char *src_buf = sysroot_path (src); if (!src_buf) { reply_with_perror ("malloc"); return -1; } if ((optargs_bitmask & GUESTFS_COPY_FILE_TO_DEVICE_APPEND_BITMASK) && append) { reply_with_error ("the append flag cannot be set for this call"); return -1; } return copy (src_buf, src, dest, dest, DEST_DEVICE_FLAGS, 0, srcoffset, destoffset, size, sparse); }
int do_mkswap_file (const char *path) { CLEANUP_FREE char *buf = NULL, *err = NULL; int r; buf = sysroot_path (path); if (!buf) { reply_with_perror ("malloc"); return -1; } r = command (NULL, &err, "mkswap", "-f", buf, NULL); if (r == -1) { reply_with_error ("%s: %s", path, err); return -1; } return r; }
int do_scrub_freespace (const char *dir) { CLEANUP_FREE char *buf = NULL; CLEANUP_FREE char *err = NULL; int r; /* Make the path relative to /sysroot. */ buf = sysroot_path (dir); if (!buf) { reply_with_perror ("malloc"); return -1; } r = command (NULL, &err, str_scrub, "-X", buf, NULL); if (r == -1) { reply_with_error ("%s: %s", dir, err); return -1; } return 0; }
int64_t do_inotify_add_watch (const char *path, int mask) { int64_t r; CLEANUP_FREE char *buf = NULL; NEED_INOTIFY (-1); buf = sysroot_path (path); if (!buf) { reply_with_perror ("malloc"); return -1; } r = inotify_add_watch (inotify_fd, buf, mask); if (r == -1) { reply_with_perror ("%s", path); return -1; } return r; }
char * do_get_e2attrs (const char *filename) { int r; CLEANUP_FREE char *buf = NULL; char *out; CLEANUP_FREE char *err = NULL; size_t i, j; buf = sysroot_path (filename); if (!buf) { reply_with_perror ("malloc"); return NULL; } r = command (&out, &err, str_lsattr, "-d", "--", buf, NULL); if (r == -1) { reply_with_error ("%s: %s: %s", "lsattr", filename, err); free (out); return NULL; } /* Output looks like: * -------------e- filename * Remove the dashes and return everything up to the space. */ for (i = j = 0; out[j] != ' '; j++) { if (out[j] != '-') out[i++] = out[j]; } out[i] = '\0'; /* Sort the output, mainly to make testing simpler. */ qsort (out, i, sizeof (char), compare_chars); return out; }
/* Takes optional arguments, consult optargs_bitmask. */ int do_hivex_open (const char *filename, int verbose, int debug, int write) { CLEANUP_FREE char *buf = NULL; int flags = 0; if (h) { hivex_close (h); h = NULL; } buf = sysroot_path (filename); if (!buf) { reply_with_perror ("malloc"); return -1; } if (optargs_bitmask & GUESTFS_HIVEX_OPEN_VERBOSE_BITMASK) { if (verbose) flags |= HIVEX_OPEN_VERBOSE; } if (optargs_bitmask & GUESTFS_HIVEX_OPEN_DEBUG_BITMASK) { if (debug) flags |= HIVEX_OPEN_DEBUG; } if (optargs_bitmask & GUESTFS_HIVEX_OPEN_WRITE_BITMASK) { if (write) flags |= HIVEX_OPEN_WRITE; } h = hivex_open (buf, flags); if (!h) { reply_with_perror ("hivex failed to open %s", filename); return -1; } return 0; }
guestfs_int_xfsinfo * do_xfs_info (const char *pathordevice) { int r; char *buf; char *out = NULL, *err = NULL; char **lines = NULL; guestfs_int_xfsinfo *ret = NULL; int is_dev; is_dev = STREQLEN (pathordevice, "/dev/", 5); buf = is_dev ? strdup (pathordevice) : sysroot_path (pathordevice); if (buf == NULL) { reply_with_perror ("malloc"); return NULL; } r = command (&out, &err, "xfs_info", buf, NULL); free (buf); if (r == -1) { reply_with_error ("%s", err); goto error; } lines = split_lines (out); if (lines == NULL) goto error; ret = parse_xfs_info (lines); error: free (err); free (out); if (lines) free_strings (lines); return ret; }
char * do_llz (const char *path) { int r; char *out; CLEANUP_FREE char *err = NULL; CLEANUP_FREE char *spath = NULL; spath = sysroot_path (path); if (!spath) { reply_with_perror ("malloc"); return NULL; } r = command (&out, &err, str_ls, "-laZ", spath, NULL); if (r == -1) { reply_with_error ("%s", err); free (out); return NULL; } return out; /* caller frees */ }
int do_set_e2generation (const char *filename, int64_t generation) { int r; CLEANUP_FREE char *buf = NULL, *err = NULL; char generation_str[64]; buf = sysroot_path (filename); if (!buf) { reply_with_perror ("malloc"); return -1; } snprintf (generation_str, sizeof generation_str, "%" PRIu64, (uint64_t) generation); r = command (NULL, &err, str_chattr, "-v", generation_str, "--", buf, NULL); if (r == -1) { reply_with_error ("%s: %s: %s", "chattr", filename, err); return -1; } return 0; }
/* Has one FileOut parameter. */ int do_find0 (const char *dir) { struct stat statbuf; int r; FILE *fp; char *cmd; char *sysrootdir; size_t sysrootdirlen, len; char str[GUESTFS_MAX_CHUNK_SIZE]; sysrootdir = sysroot_path (dir); if (!sysrootdir) { reply_with_perror ("malloc"); return -1; } r = stat (sysrootdir, &statbuf); if (r == -1) { reply_with_perror ("%s", dir); free (sysrootdir); return -1; } if (!S_ISDIR (statbuf.st_mode)) { reply_with_error ("%s: not a directory", dir); free (sysrootdir); return -1; } sysrootdirlen = strlen (sysrootdir); if (asprintf_nowarn (&cmd, "%s %Q -print0", str_find, sysrootdir) == -1) { reply_with_perror ("asprintf"); free (sysrootdir); return -1; } free (sysrootdir); if (verbose) fprintf (stderr, "%s\n", cmd); fp = popen (cmd, "r"); if (fp == NULL) { reply_with_perror ("%s", cmd); free (cmd); return -1; } free (cmd); /* Now we must send the reply message, before the file contents. After * this there is no opportunity in the protocol to send any error * message back. Instead we can only cancel the transfer. */ reply (NULL, NULL); while ((r = input_to_nul (fp, str, GUESTFS_MAX_CHUNK_SIZE)) > 0) { len = strlen (str); if (len <= sysrootdirlen) continue; /* Remove the directory part of the path before sending it. */ if (send_file_write (str + sysrootdirlen, r - sysrootdirlen) < 0) { pclose (fp); return -1; } } if (ferror (fp)) { perror (dir); send_file_end (1); /* Cancel. */ pclose (fp); return -1; } if (pclose (fp) != 0) { perror (dir); send_file_end (1); /* Cancel. */ return -1; } if (send_file_end (0)) /* Normal end of file. */ return -1; return 0; }
/* Takes optional arguments, consult optargs_bitmask. */ int do_tar_out (const char *dir, const char *compress, int numericowner, char *const *excludes) { CLEANUP_FREE char *buf = NULL; struct stat statbuf; const char *filter; int r; FILE *fp; CLEANUP_FREE char *excludes_args = NULL; CLEANUP_FREE char *cmd = NULL; char buffer[GUESTFS_MAX_CHUNK_SIZE]; if ((optargs_bitmask & GUESTFS_TAR_OUT_COMPRESS_BITMASK)) { if (STREQ (compress, "compress")) filter = " --compress"; else if (STREQ (compress, "gzip")) filter = " --gzip"; else if (STREQ (compress, "bzip2")) filter = " --bzip2"; else if (STREQ (compress, "xz")) filter = " --xz"; else if (STREQ (compress, "lzop")) filter = " --lzop"; else { reply_with_error ("unknown compression type: %s", compress); return -1; } } else filter = ""; if (!(optargs_bitmask & GUESTFS_TAR_OUT_NUMERICOWNER_BITMASK)) numericowner = 0; if ((optargs_bitmask & GUESTFS_TAR_OUT_EXCLUDES_BITMASK)) { excludes_args = make_excludes_args (excludes); if (!excludes_args) return -1; } else { excludes_args = strdup (""); if (excludes_args == NULL) { reply_with_perror ("strdup"); return -1; } } /* Check the filename exists and is a directory (RHBZ#908322). */ buf = sysroot_path (dir); if (buf == NULL) { reply_with_perror ("malloc"); return -1; } if (stat (buf, &statbuf) == -1) { reply_with_perror ("stat: %s", dir); return -1; } if (! S_ISDIR (statbuf.st_mode)) { reply_with_error ("%s: not a directory", dir); return -1; } /* "tar -C /sysroot%s -cf - ." but we have to quote the dir. */ if (asprintf_nowarn (&cmd, "%s -C %s%s%s%s -cf - .", str_tar, buf, filter, numericowner ? " --numeric-owner" : "", excludes_args) == -1) { reply_with_perror ("asprintf"); return -1; } if (verbose) fprintf (stderr, "%s\n", cmd); fp = popen (cmd, "r"); if (fp == NULL) { reply_with_perror ("%s", cmd); return -1; } /* Now we must send the reply message, before the file contents. After * this there is no opportunity in the protocol to send any error * message back. Instead we can only cancel the transfer. */ reply (NULL, NULL); while ((r = fread (buffer, 1, sizeof buffer, fp)) > 0) { if (send_file_write (buffer, r) < 0) { pclose (fp); return -1; } } if (ferror (fp)) { fprintf (stderr, "fread: %s: %m\n", dir); send_file_end (1); /* Cancel. */ pclose (fp); return -1; } if (pclose (fp) != 0) { fprintf (stderr, "pclose: %s: %m\n", dir); send_file_end (1); /* Cancel. */ return -1; } if (send_file_end (0)) /* Normal end of file. */ return -1; return 0; }
/* Has one FileOut parameter. */ int do_checksums_out (const char *csumtype, const char *dir) { struct stat statbuf; int r; const char *program = program_of_csum (csumtype); if (program == NULL) return -1; CLEANUP_FREE char *sysrootdir = sysroot_path (dir); if (!sysrootdir) { reply_with_perror ("malloc"); return -1; } r = stat (sysrootdir, &statbuf); if (r == -1) { reply_with_perror ("%s", dir); return -1; } if (!S_ISDIR (statbuf.st_mode)) { reply_with_error ("%s: not a directory", dir); return -1; } CLEANUP_FREE char *cmd = NULL; if (asprintf_nowarn (&cmd, "cd %Q && %s -type f -print0 | %s -0 %s", sysrootdir, str_find, str_xargs, program) == -1) { reply_with_perror ("asprintf"); return -1; } if (verbose) fprintf (stderr, "%s\n", cmd); FILE *fp = popen (cmd, "r"); if (fp == NULL) { reply_with_perror ("%s", cmd); return -1; } /* Now we must send the reply message, before the file contents. After * this there is no opportunity in the protocol to send any error * message back. Instead we can only cancel the transfer. */ reply (NULL, NULL); char str[GUESTFS_MAX_CHUNK_SIZE]; while ((r = fread (str, 1, GUESTFS_MAX_CHUNK_SIZE, fp)) > 0) { if (send_file_write (str, r) < 0) { pclose (fp); return -1; } } if (ferror (fp)) { fprintf (stderr, "fread: %s: %m\n", dir); send_file_end (1); /* Cancel. */ pclose (fp); return -1; } if (pclose (fp) != 0) { fprintf (stderr, "pclose: %s: %m\n", dir); send_file_end (1); /* Cancel. */ return -1; } if (send_file_end (0)) /* Normal end of file. */ return -1; return 0; }