Exemple #1
0
static int
write_or_append (guestfs_h *g, const char *path,
                 const char *content, size_t size,
                 int append)
{
  CLEANUP_UNLINK_FREE char *tmpfile = NULL;
  int fd = -1;
  int64_t filesize;

  /* If the content is small enough, use guestfs_internal_write{,_append}
   * since that call is more efficient.
   */
  if (size <= 2*1024*1024)
    return
      (!append ? guestfs_internal_write : guestfs_internal_write_append)
      (g, path, content, size);

  if (guestfs_int_lazy_make_tmpdir (g) == -1)
    goto err;

  /* Write the content out to a temporary file. */
  tmpfile = safe_asprintf (g, "%s/write%d", g->tmpdir, ++g->unique);

  fd = open (tmpfile, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0600);
  if (fd == -1) {
    perrorf (g, "open: %s", tmpfile);
    goto err;
  }

  if (full_write (fd, content, size) != size) {
    perrorf (g, "write: %s", tmpfile);
    goto err;
  }

  if (close (fd) == -1) {
    perrorf (g, "close: %s", tmpfile);
    goto err;
  }
  fd = -1;

  if (!append) {
    if (guestfs_upload (g, tmpfile, path) == -1)
      goto err;
  }
  else {
    /* XXX Should have an 'upload-append' call to make this atomic. */
    filesize = guestfs_filesize (g, path);
    if (filesize == -1)
      goto err;
    if (guestfs_upload_offset (g, tmpfile, path, filesize) == -1)
      goto err;
  }

  return 0;

 err:
  if (fd >= 0)
    close (fd);
  return -1;
}
Exemple #2
0
static void
edit (const char *filename, const char *root)
{
  char *filename_to_free = NULL;
  const char *tmpdir = guestfs_tmpdir ();
  char tmpfile[strlen (tmpdir) + 32];
  sprintf (tmpfile, "%s/virteditXXXXXX", tmpdir);
  int fd;
  char fdbuf[32];
  char *upload_from = NULL;
  char *newname = NULL;
  char *backupname = NULL;

  /* Windows?  Special handling is required. */
  if (is_windows (g, root))
    filename = filename_to_free = windows_path (g, root, filename);

  /* Download the file to a temporary. */
  fd = mkstemp (tmpfile);
  if (fd == -1) {
    perror ("mkstemp");
    exit (EXIT_FAILURE);
  }

  snprintf (fdbuf, sizeof fdbuf, "/dev/fd/%d", fd);

  if (guestfs_download (g, filename, fdbuf) == -1)
    goto error;

  if (close (fd) == -1) {
    perror (tmpfile);
    goto error;
  }

  if (!perl_expr)
    upload_from = edit_interactively (tmpfile);
  else
    upload_from = edit_non_interactively (tmpfile);

  /* We don't always need to upload: upload_from could be NULL because
   * the user closed the editor without changing the file.
   */
  if (upload_from) {
    /* Upload to a new file in the same directory, so if it fails we
     * don't end up with a partially written file.  Give the new file
     * a completely random name so we have only a tiny chance of
     * overwriting some existing file.
     */
    newname = generate_random_name (filename);

    if (guestfs_upload (g, upload_from, newname) == -1)
      goto error;

    /* Backup or overwrite the file. */
    if (backup_extension) {
      backupname = generate_backup_name (filename);
      if (guestfs_mv (g, filename, backupname) == -1)
        goto error;
    }
    if (guestfs_mv (g, newname, filename) == -1)
      goto error;
  }

  unlink (tmpfile);
  free (filename_to_free);
  free (upload_from);
  free (newname);
  free (backupname);
  return;

 error:
  unlink (tmpfile);
  exit (EXIT_FAILURE);
}
Exemple #3
0
int
run_edit (const char *cmd, size_t argc, char *argv[])
{
  CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g);
  CLEANUP_UNLINK_FREE char *filename = NULL;
  char buf[256];
  const char *editor;
  CLEANUP_FREE char *remotefilename = NULL, *newname = NULL;
  struct stat oldstat, newstat;
  int r, fd;

  if (argc != 1) {
    fprintf (stderr, _("use '%s filename' to edit a file\n"), cmd);
    return -1;
  }

  /* Choose an editor. */
  if (STRCASEEQ (cmd, "vi"))
    editor = "vi";
  else if (STRCASEEQ (cmd, "emacs"))
    editor = "emacs -nw";
  else {
    editor = getenv ("EDITOR");
    if (editor == NULL)
      editor = "vi"; /* could be cruel here and choose ed(1) */
  }

  /* Handle 'win:...' prefix. */
  remotefilename = win_prefix (argv[0]);
  if (remotefilename == NULL)
    return -1;

  /* Download the file and write it to a temporary. */
  if (asprintf (&filename, "%s/guestfishXXXXXX", tmpdir) == -1) {
    perror ("asprintf");
    return -1;
  }

  fd = mkstemp (filename);
  if (fd == -1) {
    perror ("mkstemp");
    return -1;
  }

  snprintf (buf, sizeof buf, "/dev/fd/%d", fd);

  if (guestfs_download (g, remotefilename, buf) == -1) {
    close (fd);
    return -1;
  }

  if (close (fd) == -1) {
    perror (filename);
    return -1;
  }

  /* Get the old stat. */
  if (stat (filename, &oldstat) == -1) {
    perror (filename);
    return -1;
  }

  /* Edit it. */
  /* XXX Safe? */
  snprintf (buf, sizeof buf, "%s %s", editor, filename);

  r = system (buf);
  if (r != 0) {
    perror (buf);
    return -1;
  }

  /* Get the new stat. */
  if (stat (filename, &newstat) == -1) {
    perror (filename);
    return -1;
  }

  /* Changed? */
  if (oldstat.st_ctime == newstat.st_ctime &&
      oldstat.st_size == newstat.st_size)
    return 0;

  /* Upload to a new file in the same directory, so if it fails we
   * don't end up with a partially written file.  Give the new file
   * a completely random name so we have only a tiny chance of
   * overwriting some existing file.
   */
  newname = generate_random_name (remotefilename);
  if (!newname)
    return -1;

  /* Write new content. */
  if (guestfs_upload (g, filename, newname) == -1)
    return -1;

  /* Set the permissions, UID, GID and SELinux context of the new
   * file to match the old file (RHBZ#788641).
   */
  if (guestfs_copy_attributes (g, remotefilename, newname,
      GUESTFS_COPY_ATTRIBUTES_ALL, 1, -1) == -1)
    return -1;

  if (guestfs_mv (g, newname, remotefilename) == -1)
    return -1;

  return 0;
}
static void
test_virtio_serial (void)
{
  int fd, r, eh;
  char tmpfile[] = "/tmp/speedtestXXXXXX";
  struct sigaction sa, old_sa;

  if (!virtio_serial_upload && !virtio_serial_download)
    return;

  /* Create a sparse file.  We could upload from /dev/zero, but we
   * won't get progress messages because libguestfs tests if the
   * source file is a regular file.
   */
  fd = mkstemp (tmpfile);
  if (fd == -1)
    error (EXIT_FAILURE, errno, "mkstemp: %s", tmpfile);
  if (ftruncate (fd, TEST_SERIAL_MAX_SIZE) == -1)
    error (EXIT_FAILURE, errno, "ftruncate");
  if (close (fd) == -1)
    error (EXIT_FAILURE, errno, "close");

  g = guestfs_create ();
  if (!g)
    error (EXIT_FAILURE, errno, "guestfs_create");

  if (guestfs_add_drive_scratch (g, INT64_C (100*1024*1024), -1) == -1)
    exit (EXIT_FAILURE);

  if (guestfs_launch (g) == -1)
    exit (EXIT_FAILURE);

  /* Make and mount a filesystem which will be used by the download test. */
  if (guestfs_mkfs (g, "ext4", "/dev/sda") == -1)
    exit (EXIT_FAILURE);
  if (guestfs_mount (g, "/dev/sda", "/") == -1)
    exit (EXIT_FAILURE);

  /* Time out the upload after TEST_SERIAL_MAX_TIME seconds have passed. */
  memset (&sa, 0, sizeof sa);
  sa.sa_handler = stop_transfer;
  sa.sa_flags = SA_RESTART;
  sigaction (SIGALRM, &sa, &old_sa);

  /* Get progress messages, which will tell us how much data has been
   * transferred.
   */
  eh = guestfs_set_event_callback (g, progress_cb, GUESTFS_EVENT_PROGRESS,
                                   0, NULL);
  if (eh == -1)
    exit (EXIT_FAILURE);

  if (virtio_serial_upload) {
    gettimeofday (&start, NULL);
    rate = -1;
    operation = "upload";
    alarm (max_time_override > 0 ? max_time_override : TEST_SERIAL_MAX_TIME);

    /* For the upload test, upload the sparse file to /dev/null in the
     * appliance.  Hopefully this is mostly testing just virtio-serial.
     */
    guestfs_push_error_handler (g, NULL, NULL);
    r = guestfs_upload (g, tmpfile, "/dev/null");
    alarm (0);
    unlink (tmpfile);
    guestfs_pop_error_handler (g);

    /* It's possible that the upload will finish before the alarm fires,
     * or that the upload will be stopped by the alarm.
     */
    if (r == -1 && guestfs_last_errno (g) != EINTR) {
      fprintf (stderr,
               "%s: expecting upload command to return EINTR\n%s\n",
               guestfs_int_program_name, guestfs_last_error (g));
      exit (EXIT_FAILURE);
    }

    if (rate == -1) {
    rate_error:
      fprintf (stderr, "%s: internal error: progress callback was not called! (r=%d, errno=%d)\n",
               guestfs_int_program_name,
               r, guestfs_last_errno (g));
      exit (EXIT_FAILURE);
    }

    print_rate ("virtio-serial upload rate:", rate);
  }

  if (virtio_serial_download) {
    /* For the download test, download a sparse file within the
     * appliance to /dev/null on the host.
     */
    if (guestfs_touch (g, "/sparse") == -1)
      exit (EXIT_FAILURE);
    if (guestfs_truncate_size (g, "/sparse", TEST_SERIAL_MAX_SIZE) == -1)
      exit (EXIT_FAILURE);

    gettimeofday (&start, NULL);
    rate = -1;
    operation = "download";
    alarm (max_time_override > 0 ? max_time_override : TEST_SERIAL_MAX_TIME);
    guestfs_push_error_handler (g, NULL, NULL);
    r = guestfs_download (g, "/sparse", "/dev/null");
    alarm (0);
    guestfs_pop_error_handler (g);

    if (r == -1 && guestfs_last_errno (g) != EINTR) {
      fprintf (stderr,
               "%s: expecting download command to return EINTR\n%s\n",
               guestfs_int_program_name, guestfs_last_error (g));
      exit (EXIT_FAILURE);
    }

    if (rate == -1)
      goto rate_error;

    print_rate ("virtio-serial download rate:", rate);
  }

  if (guestfs_shutdown (g) == -1)
    exit (EXIT_FAILURE);

  guestfs_close (g);

  /* Restore SIGALRM signal handler. */
  sigaction (SIGALRM, &old_sa, NULL);
}