static gboolean
_ostree_bootloader_grub2_write_config (OstreeBootloader      *bootloader,
                                       int                    bootversion,
                                       GCancellable          *cancellable,
                                       GError               **error)
{
  OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (bootloader);
  gboolean ret = FALSE;
  gs_unref_object GFile *efi_new_config_temp = NULL;
  gs_unref_object GFile *efi_orig_config = NULL;
  gs_unref_object GFile *new_config_path = NULL;
  gs_unref_object GSSubprocessContext *procctx = NULL;
  gs_unref_object GSSubprocess *proc = NULL;
  gs_strfreev char **child_env = g_get_environ ();
  gs_free char *bootversion_str = g_strdup_printf ("%u", (guint)bootversion);
  gs_unref_object GFile *config_path_efi_dir = NULL;

  if (self->is_efi)
    {
      config_path_efi_dir = g_file_get_parent (self->config_path_efi);
      new_config_path = g_file_get_child (config_path_efi_dir, "grub.cfg.new");
      /* We use grub2-mkconfig to write to a temporary file first */
      if (!ot_gfile_ensure_unlinked (new_config_path, cancellable, error))
        goto out;
    }
  else
    {
      new_config_path = ot_gfile_resolve_path_printf (self->sysroot->path, "boot/loader.%d/grub.cfg",
                                                      bootversion);
    }

  procctx = gs_subprocess_context_newv ("grub2-mkconfig", "-o",
                                        gs_file_get_path_cached (new_config_path),
                                        NULL);
  child_env = g_environ_setenv (child_env, "_OSTREE_GRUB2_BOOTVERSION", bootversion_str, TRUE);
  /* We have to pass our state to the child */
  if (self->is_efi)
    child_env = g_environ_setenv (child_env, "_OSTREE_GRUB2_IS_EFI", "1", TRUE);
  gs_subprocess_context_set_environment (procctx, child_env);
  gs_subprocess_context_set_stdout_disposition (procctx, GS_SUBPROCESS_STREAM_DISPOSITION_NULL);
  if (g_getenv ("OSTREE_DEBUG_GRUB2"))
    gs_subprocess_context_set_stderr_disposition (procctx, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT);
  else
    gs_subprocess_context_set_stderr_disposition (procctx, GS_SUBPROCESS_STREAM_DISPOSITION_NULL);

  /* In the current Fedora grub2 package, this script doesn't even try
     to be atomic; it just does:

cat ${grub_cfg}.new > ${grub_cfg}
rm -f ${grub_cfg}.new

     Upstream is fixed though.
  */
  proc = gs_subprocess_new (procctx, cancellable, error);
  if (!proc)
    goto out;

  if (!gs_subprocess_wait_sync_check (proc, cancellable, error))
    goto out;

  /* Now let's fdatasync() for the new file */
  if (!gs_file_sync_data (new_config_path, cancellable, error))
    goto out;

  if (self->is_efi)
    {
      gs_unref_object GFile *config_path_efi_old = g_file_get_child (config_path_efi_dir, "grub.cfg.old");
      
      /* copy current to old */
      if (!ot_gfile_ensure_unlinked (config_path_efi_old, cancellable, error))
        goto out;
      if (!g_file_copy (self->config_path_efi, config_path_efi_old,
                        G_FILE_COPY_OVERWRITE, cancellable, NULL, NULL, error))
        goto out;
      if (!ot_gfile_ensure_unlinked (config_path_efi_old, cancellable, error))
        goto out;

      /* NOTE: NON-ATOMIC REPLACEMENT; WE can't do anything else on FAT;
       * see https://bugzilla.gnome.org/show_bug.cgi?id=724246
       */
      if (!ot_gfile_ensure_unlinked (self->config_path_efi, cancellable, error))
        goto out;
      if (!gs_file_rename (new_config_path, self->config_path_efi,
                           cancellable, error))
        goto out;
    }
  
  ret = TRUE;
 out:
  return ret;
}
Beispiel #2
0
static gboolean
linkcopy_internal_attempt (GFile          *src,
                           GFile          *dest,
                           GFile          *dest_parent,
                           GFileCopyFlags  flags,
                           gboolean        sync_data,
                           gboolean        enable_guestfs_fuse_workaround,
                           gboolean       *out_try_again,
                           GCancellable   *cancellable,
                           GError        **error)
{
    gboolean ret = FALSE;
    int res;
    char *tmp_name = NULL;
    GFile *tmp_dest = NULL;

    if (g_cancellable_set_error_if_cancelled (cancellable, error))
        goto out;

    tmp_name = gs_fileutil_gen_tmp_name (NULL, NULL);
    tmp_dest = g_file_get_child (dest_parent, tmp_name);

    res = link (gs_file_get_path_cached (src), gs_file_get_path_cached (tmp_dest));
    if (res == -1)
    {
        if (errno == EEXIST)
        {
            /* Nothing, fall through */
            *out_try_again = TRUE;
            ret = TRUE;
            goto out;
        }
        else if (errno == EXDEV || errno == EMLINK || errno == EPERM
                 || (enable_guestfs_fuse_workaround && errno == ENOENT))
        {
            if (!g_file_copy (src, tmp_dest, flags,
                              cancellable, NULL, NULL, error))
                goto out;
        }
        else
        {
            gs_set_prefix_error_from_errno (error, errno, "link");
            goto out;
        }
    }

    if (sync_data)
    {
        /* Now, we need to fsync */
        if (!gs_file_sync_data (tmp_dest, cancellable, error))
            goto out;
    }

    if (!gs_file_rename (tmp_dest, dest, cancellable, error))
        goto out;

    ret = TRUE;
    *out_try_again = FALSE;
out:
    g_clear_pointer (&tmp_name, g_free);
    g_clear_object (&tmp_dest);
    return ret;
}