Esempio n. 1
0
/* Turn list 'excludes' into list of " --excludes=..." strings, all
 * properly quoted.  Caller must free the returned string.
 */
static char *
make_excludes_args (char *const *excludes)
{
  DECLARE_STRINGSBUF (strings);
  size_t i;
  char *s, *ret;

  for (i = 0; excludes[i] != NULL; ++i) {
    if (asprintf_nowarn (&s, " --exclude=%Q", excludes[i]) == -1) {
      reply_with_perror ("asprintf");
      free_stringslen (strings.argv, strings.size);
      return NULL;
    }
    if (add_string_nodup (&strings, s) == -1) {
      free (s);
      return NULL;
    }
  }

  if (end_stringsbuf (&strings) == -1)
    return NULL;

  ret = concat_strings (strings.argv);
  if (!ret) {
    reply_with_perror ("concat");
    free_stringslen (strings.argv, strings.size);
    return NULL;
  }

  free_stringslen (strings.argv, strings.size);

  return ret;
}
Esempio n. 2
0
/* Has one FileIn parameter. */
int
do_base64_in (const char *file)
{
  int err, r;
  FILE *fp;
  CLEANUP_FREE char *cmd = NULL;
  int fd;

  if (asprintf_nowarn (&cmd, "%s -d -i > %R", str_base64, file) == -1) {
    err = errno;
    cancel_receive ();
    errno = err;
    reply_with_perror ("asprintf");
    return -1;
  }

  if (verbose)
    fprintf (stderr, "%s\n", cmd);

  fp = popen (cmd, "w");
  if (fp == NULL) {
    err = errno;
    cancel_receive ();
    errno = err;
    reply_with_perror ("%s", cmd);
    return -1;
  }

  /* The semantics of fwrite are too undefined, so write to the
   * file descriptor directly instead.
   */
  fd = fileno (fp);

  r = receive_file (write_cb, &fd);
  if (r == -1) {		/* write error */
    cancel_receive ();
    reply_with_error ("write error on file: %s", file);
    pclose (fp);
    return -1;
  }
  if (r == -2) {		/* cancellation from library */
    /* This error is ignored by the library since it initiated the
     * cancel.  Nevertheless we must send an error reply here.
     */
    reply_with_error ("file upload cancelled");
    pclose (fp);
    return -1;
  }

  if (pclose (fp) != 0) {
    reply_with_error ("base64 subcommand failed on file: %s", file);
    return -1;
  }

  return 0;
}
Esempio n. 3
0
/* zcat | file */
char *
do_zfile (const char *method, const char *path)
{
  int len;
  const char *zcat;
  char *cmd;
  FILE *fp;
  char line[256];

  if (STREQ (method, "gzip") || STREQ (method, "compress"))
    zcat = "zcat";
  else if (STREQ (method, "bzip2"))
    zcat = "bzcat";
  else {
    reply_with_error ("unknown method");
    return NULL;
  }

  if (asprintf_nowarn (&cmd, "%s %R | file -bsL -", zcat, path) == -1) {
    reply_with_perror ("asprintf");
    return NULL;
  }

  if (verbose)
    fprintf (stderr, "%s\n", cmd);

  fp = popen (cmd, "r");
  if (fp == NULL) {
    reply_with_perror ("%s", cmd);
    free (cmd);
    return NULL;
  }

  free (cmd);

  if (fgets (line, sizeof line, fp) == NULL) {
    reply_with_perror ("fgets");
    fclose (fp);
    return NULL;
  }

  if (fclose (fp) == -1) {
    reply_with_perror ("fclose");
    return NULL;
  }

  len = strlen (line);
  if (len > 0 && line[len-1] == '\n')
    line[len-1] = '\0';

  return strdup (line);
}
Esempio n. 4
0
int
do_grub_install (const char *root, const char *device)
{
  int r;
  CLEANUP_FREE char *err = NULL, *buf = NULL;

  if (asprintf_nowarn (&buf, "--root-directory=%R", root) == -1) {
    reply_with_perror ("asprintf");
    return -1;
  }

  r = command (NULL, &err, str_grub_install, buf, device, NULL);

  if (r == -1) {
    reply_with_error ("%s", err);
    return -1;
  }

  return 0;
}
Esempio n. 5
0
int
do_mklost_and_found (const char *mountpoint)
{
    CLEANUP_FREE char *cmd = NULL;
    int r;

    if (asprintf_nowarn (&cmd, "cd %R && mklost+found", mountpoint) == -1) {
        reply_with_perror ("asprintf");
        return -1;
    }

    r = system (cmd);
    if (r == -1) {
        reply_with_perror ("system");
        return -1;
    }
    if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
        reply_with_error ("%s: command failed", cmd);
        return -1;
    }

    return 0;
}
Esempio n. 6
0
int
do_grub_install (const char *root, const char *device)
{
  int r;
  char *err;
  char *buf;

  if (asprintf_nowarn (&buf, "--root-directory=%R", root) == -1) {
    reply_with_perror ("asprintf");
    return -1;
  }

  r = command (NULL, &err, "grub-install", buf, device, NULL);
  free (buf);

  if (r == -1) {
    reply_with_error ("%s", err);
    free (err);
    return -1;
  }

  free (err);
  return 0;
}
Esempio n. 7
0
char *
do_initrd_cat (const char *path, const char *filename, size_t *size_r)
{
  char tmpdir[] = "/tmp/initrd-cat-XXXXXX";
  if (mkdtemp (tmpdir) == NULL) {
    reply_with_perror ("mkdtemp");
    return NULL;
  }

  /* "zcat /sysroot/<path> | cpio --quiet -id file", but paths must be quoted */
  char *cmd;
  if (asprintf_nowarn (&cmd, "cd %Q && zcat %R | cpio --quiet -id %Q",
                       tmpdir, path, filename) == -1) {
    reply_with_perror ("asprintf");
    rmdir (tmpdir);
    return NULL;
  }

  /* Extract file into temporary directory.  This may create subdirs.
   * It's also possible that this doesn't create anything at all
   * (eg. if the named file does not exist in the cpio archive) --
   * cpio is silent in this case.
   */
  int r = system (cmd);
  if (r == -1) {
    reply_with_perror ("command failed: %s", cmd);
    free (cmd);
    rmdir (tmpdir);
    return NULL;
  }
  free (cmd);
  if (WEXITSTATUS (r) != 0) {
    reply_with_perror ("command failed with return code %d",
                       WEXITSTATUS (r));
    rmdir (tmpdir);
    return NULL;
  }

  /* See if we got a file. */
  char fullpath[PATH_MAX];
  snprintf (fullpath, sizeof fullpath, "%s/%s", tmpdir, filename);

  struct stat statbuf;
  int fd;

  fd = open (fullpath, O_RDONLY);
  if (fd == -1) {
    reply_with_perror ("open: %s:%s", path, filename);
    rmdir (tmpdir);
    return NULL;
  }

  /* From this point, we know the file exists, so we require full
   * cleanup.
   */
  char *ret = NULL;

  if (fstat (fd, &statbuf) == -1) {
    reply_with_perror ("fstat: %s:%s", path, filename);
    goto cleanup;
  }

  /* The actual limit on messages is smaller than this.  This
   * check just limits the amount of memory we'll try and allocate
   * here.  If the message is larger than the real limit, that will
   * be caught later when we try to serialize the message.
   */
  if (statbuf.st_size >= GUESTFS_MESSAGE_MAX) {
    reply_with_error ("%s:%s: file is too large for the protocol",
                      path, filename);
    goto cleanup;
  }

  ret = malloc (statbuf.st_size);
  if (ret == NULL) {
    reply_with_perror ("malloc");
    goto cleanup;
  }

  if (xread (fd, ret, statbuf.st_size) == -1) {
    reply_with_perror ("read: %s:%s", path, filename);
    free (ret);
    ret = NULL;
    goto cleanup;
  }

  if (close (fd) == -1) {
    reply_with_perror ("close: %s:%s", path, filename);
    free (ret);
    ret = NULL;
    goto cleanup;
  }
  fd = -1;

  /* Mustn't touch *size_r until we are sure that we won't return any
   * error (RHBZ#589039).
   */
  *size_r = statbuf.st_size;

 cleanup:
  if (fd >= 0)
    close (fd);

  /* Remove the file. */
  if (unlink (fullpath) == -1) {
    fprintf (stderr, "unlink: ");
    perror (fullpath);
    /* non-fatal */
  }

  /* Remove the directories up to and including the temp directory. */
  do {
    char *p = strrchr (fullpath, '/');
    if (!p) break;
    *p = '\0';
    if (rmdir (fullpath) == -1) {
      fprintf (stderr, "rmdir: ");
      perror (fullpath);
      /* non-fatal */
    }
  } while (STRNEQ (fullpath, tmpdir));

  return ret;
}
Esempio n. 8
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;
}
Esempio n. 9
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;
}
Esempio n. 10
0
/* Takes optional arguments, consult optargs_bitmask. */
int
do_tar_in (const char *dir, const char *compress)
{
  const char *filter;
  int err, r;
  FILE *fp;
  CLEANUP_FREE char *cmd = NULL;
  char error_file[] = "/tmp/tarXXXXXX";
  int fd, chown_supported;

  chown_supported = is_chown_supported (dir);
  if (chown_supported == -1)
    return -1;

  if ((optargs_bitmask & GUESTFS_TAR_IN_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 = "";

  fd = mkstemp (error_file);
  if (fd == -1) {
    reply_with_perror ("mkstemp");
    return -1;
  }

  close (fd);

  /* "tar -C /sysroot%s -xf -" but we have to quote the dir. */
  if (asprintf_nowarn (&cmd, "%s -C %R%s -xf - %s2> %s",
                       str_tar,
                       dir, filter,
                       chown_supported ? "" : "--no-same-owner ",
                       error_file) == -1) {
    err = errno;
    r = cancel_receive ();
    errno = err;
    reply_with_perror ("asprintf");
    unlink (error_file);
    return -1;
  }

  if (verbose)
    fprintf (stderr, "%s\n", cmd);

  fp = popen (cmd, "w");
  if (fp == NULL) {
    err = errno;
    r = cancel_receive ();
    errno = err;
    reply_with_perror ("%s", cmd);
    unlink (error_file);
    return -1;
  }

  /* The semantics of fwrite are too undefined, so write to the
   * file descriptor directly instead.
   */
  fd = fileno (fp);

  r = receive_file (write_cb, &fd);
  if (r == -1) {		/* write error */
    cancel_receive ();
    CLEANUP_FREE char *errstr = read_error_file (error_file);
    reply_with_error ("write error on directory: %s: %s", dir, errstr);
    unlink (error_file);
    pclose (fp);
    return -1;
  }
  if (r == -2) {		/* cancellation from library */
    /* This error is ignored by the library since it initiated the
     * cancel.  Nevertheless we must send an error reply here.
     */
    reply_with_error ("file upload cancelled");
    pclose (fp);
    unlink (error_file);
    return -1;
  }

  if (pclose (fp) != 0) {
    CLEANUP_FREE char *errstr = read_error_file (error_file);
    reply_with_error ("tar subcommand failed on directory: %s: %s",
                      dir, errstr);
    unlink (error_file);
    return -1;
  }

  unlink (error_file);

  return 0;
}
Esempio n. 11
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;
}
Esempio n. 12
0
/* Has one FileOut parameter. */
int
do_find0 (const char *dir)
{
  struct stat statbuf;
  int r;
  FILE *fp;
  CLEANUP_FREE char *cmd = NULL;
  CLEANUP_FREE char *sysrootdir = NULL;
  size_t sysrootdirlen;
  CLEANUP_FREE char *str = NULL;

  str = malloc (GUESTFS_MAX_CHUNK_SIZE);
  if (str == NULL) {
    reply_with_perror ("malloc");
    return -1;
  }

  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;
  }

  sysrootdirlen = strlen (sysrootdir);

  if (asprintf_nowarn (&cmd, "%s %Q -print0", str_find, sysrootdir) == -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);

  /* The code below assumes each path returned can fit into a protocol
   * chunk (if not you'll get a runtime protocol error).  If this
   * turns out not to be a problem at some point in the future then
   * we'll need to modify the code to handle it.  XXX
   */
  while ((r = input_to_nul (fp, str, GUESTFS_MAX_CHUNK_SIZE)) > 0) {
    size_t 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)) {
    fprintf (stderr, "fgetc: %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;
}
Esempio n. 13
0
/* Has one FileOut parameter. */
static int
do_compressX_out (const char *file, const char *filter, int is_device)
{
  int r;
  FILE *fp;
  CLEANUP_FREE char *cmd = NULL;
  char buf[GUESTFS_MAX_CHUNK_SIZE];

  /* The command will look something like:
   *   gzip -c /sysroot%s     # file
   * or:
   *   gzip -c < %s           # device
   *
   * We have to quote the file or device name.
   *
   * The unnecessary redirect for devices is there because lzop
   * unhelpfully refuses to compress anything that isn't a regular
   * file.
   */
  if (!is_device) {
    if (asprintf_nowarn (&cmd, "%s %R", filter, file) == -1) {
      reply_with_perror ("asprintf");
      return -1;
    }
  } else {
    if (asprintf_nowarn (&cmd, "%s < %Q", filter, file) == -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 (buf, 1, sizeof buf, fp)) > 0) {
    if (send_file_write (buf, r) < 0) {
      pclose (fp);
      return -1;
    }
  }

  if (ferror (fp)) {
    fprintf (stderr, "fread: %s: %m\n", file);
    send_file_end (1);		/* Cancel. */
    pclose (fp);
    return -1;
  }

  if (pclose (fp) != 0) {
    fprintf (stderr, "pclose: %s: %m\n", file);
    send_file_end (1);		/* Cancel. */
    return -1;
  }

  if (send_file_end (0))	/* Normal end of file. */
    return -1;

  return 0;
}
Esempio n. 14
0
/* Has one FileOut parameter. */
int
do_base64_out (const char *file)
{
  CLEANUP_FREE char *buf = NULL;
  struct stat statbuf;
  int r;
  FILE *fp;
  CLEANUP_FREE char *cmd = NULL;
  char buffer[GUESTFS_MAX_CHUNK_SIZE];

  /* Check the filename exists and is not a directory (RHBZ#908322). */
  buf = sysroot_path (file);
  if (buf == NULL) {
    reply_with_perror ("malloc");
    return -1;
  }

  if (stat (buf, &statbuf) == -1) {
    reply_with_perror ("stat: %s", file);
    return -1;
  }

  if (S_ISDIR (statbuf.st_mode)) {
    reply_with_error ("%s: is a directory", file);
    return -1;
  }

  /* Construct the command. */
  if (asprintf_nowarn (&cmd, "%s %Q", str_base64, buf) == -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", file);
    send_file_end (1);		/* Cancel. */
    pclose (fp);
    return -1;
  }

  if (pclose (fp) != 0) {
    fprintf (stderr, "pclose: %s: %m\n", file);
    send_file_end (1);		/* Cancel. */
    return -1;
  }

  if (send_file_end (0))	/* Normal end of file. */
    return -1;

  return 0;
}
Esempio n. 15
0
/* Has one FileIn parameter. */
int
do_ntfsclone_in (const char *device)
{
  int err, r;
  FILE *fp;
  char *cmd;
  char error_file[] = "/tmp/ntfscloneXXXXXX";
  int fd;

  fd = mkstemp (error_file);
  if (fd == -1) {
    reply_with_perror ("mkstemp");
    return -1;
  }

  close (fd);

  /* Construct the command. */
  if (asprintf_nowarn (&cmd, "ntfsclone -O %s --restore-image - 2> %s",
                       device, error_file) == -1) {
    err = errno;
    r = cancel_receive ();
    errno = err;
    reply_with_perror ("asprintf");
    unlink (error_file);
    return -1;
  }

  if (verbose)
    fprintf (stderr, "%s\n", cmd);

  fp = popen (cmd, "w");
  if (fp == NULL) {
    err = errno;
    r = cancel_receive ();
    errno = err;
    reply_with_perror ("%s", cmd);
    unlink (error_file);
    free (cmd);
    return -1;
  }
  free (cmd);

  /* The semantics of fwrite are too undefined, so write to the
   * file descriptor directly instead.
   */
  fd = fileno (fp);

  r = receive_file (write_cb, &fd);
  if (r == -1) {		/* write error */
    cancel_receive ();
    char *errstr = read_error_file (error_file);
    reply_with_error ("write error on device: %s: %s", device, errstr);
    free (errstr);
    unlink (error_file);
    pclose (fp);
    return -1;
  }
  if (r == -2) {		/* cancellation from library */
    /* This error is ignored by the library since it initiated the
     * cancel.  Nevertheless we must send an error reply here.
     */
    reply_with_error ("ntfsclone cancelled");
    pclose (fp);
    unlink (error_file);
    return -1;
  }

  if (pclose (fp) != 0) {
    char *errstr = read_error_file (error_file);
    reply_with_error ("ntfsclone subcommand failed on device: %s: %s",
                      device, errstr);
    free (errstr);
    unlink (error_file);
    return -1;
  }

  unlink (error_file);

  return 0;
}