Ejemplo n.º 1
0
/**
 * ot_gfile_atomic_symlink_swap:
 * @path: Replace the contents of this symbolic link
 * @target: New symbolic link target
 * @cancellable:
 * @error
 *
 * Create a new temporary symbolic link, then use the Unix rename()
 * function to atomically replace @path with the new symbolic link.
 * Do not use this function inside directories such as /tmp as it uses
 * a predicatable file name.
 */
gboolean
ot_gfile_atomic_symlink_swap (GFile          *path,
                              const char     *target,
                              GCancellable   *cancellable,
                              GError        **error)
{
  gboolean ret = FALSE;
  g_autoptr(GFile) parent = g_file_get_parent (path);
  g_autofree char *tmpname = g_strconcat (gs_file_get_basename_cached (path), ".tmp", NULL);
  g_autoptr(GFile) tmppath = g_file_get_child (parent, tmpname);
  int parent_dfd = -1;

  if (!ot_gfile_ensure_unlinked (tmppath, cancellable, error))
    goto out;
  
  if (!g_file_make_symbolic_link (tmppath, target, cancellable, error))
    goto out;

  if (!gs_file_open_dir_fd (parent, &parent_dfd, cancellable, error))
    goto out;

  /* Ensure the link has hit disk */
  if (fsync (parent_dfd) != 0)
    {
      gs_set_error_from_errno (error, errno);
      goto out;
    }

  if (!gs_file_rename (tmppath, path, cancellable, error))
    goto out;

  /* And sync again for good measure */
  if (fsync (parent_dfd) != 0)
    {
      gs_set_error_from_errno (error, errno);
      goto out;
    }

  ret = TRUE;
 out:
  if (parent_dfd != -1) (void) close (parent_dfd);
  return ret;
}
Ejemplo n.º 2
0
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;
}