libostree: Move a lot more sysroot API here

OstreeBootloader is temporarily public API.
This commit is contained in:
Colin Walters committed 2013-09-15 18:08:06 -04:00
1 parent 6f929ca5af
commit 95f07d486a
25 files changed
+978 -851

No files matched your search

+295
View File
@@ -0,0 +1,295 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2013 Colin Walters <walters@verbum.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include "ostree-sysroot-private.h"
#include "ostree-bootloader-syslinux.h"
#include "otutil.h"
#include "libgsystem.h"
#include <string.h>
struct _OstreeBootloaderSyslinux
{
GObject parent_instance;
OstreeSysroot *sysroot;
GFile *config_path;
};
typedef GObjectClass OstreeBootloaderSyslinuxClass;
static void ostree_bootloader_syslinux_bootloader_iface_init (OstreeBootloaderInterface *iface);
G_DEFINE_TYPE_WITH_CODE (OstreeBootloaderSyslinux, ostree_bootloader_syslinux, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (OSTREE_TYPE_BOOTLOADER, ostree_bootloader_syslinux_bootloader_iface_init));
static gboolean
ostree_bootloader_syslinux_query (OstreeBootloader *bootloader)
{
OstreeBootloaderSyslinux *self = OSTREE_BOOTLOADER_SYSLINUX (bootloader);
return g_file_query_file_type (self->config_path, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) == G_FILE_TYPE_SYMBOLIC_LINK;
}
static const char *
ostree_bootloader_syslinux_get_name (OstreeBootloader *bootloader)
{
return "syslinux";
}
static gboolean
append_config_from_boostree_loader_entries (OstreeBootloaderSyslinux *self,
gboolean regenerate_default,
int bootversion,
GPtrArray *new_lines,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_ptrarray GPtrArray *boostree_loader_configs = NULL;
guint i;
if (!_ostree_sysroot_read_boot_loader_configs (self->sysroot, bootversion, &boostree_loader_configs,
cancellable, error))
goto out;
for (i = 0; i < boostree_loader_configs->len; i++)
{
OstreeBootconfigParser *config = boostree_loader_configs->pdata[i];
const char *val;
val = ostree_bootconfig_parser_get (config, "title");
if (!val)
val = "(Untitled)";
if (regenerate_default && i == 0)
{
g_ptr_array_add (new_lines, g_strdup_printf ("DEFAULT %s", val));
}
g_ptr_array_add (new_lines, g_strdup_printf ("LABEL %s", val));
val = ostree_bootconfig_parser_get (config, "linux");
if (!val)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"No \"linux\" key in bootloader config");
goto out;
}
g_ptr_array_add (new_lines, g_strdup_printf ("\tKERNEL %s", val));
val = ostree_bootconfig_parser_get (config, "initrd");
if (val)
g_ptr_array_add (new_lines, g_strdup_printf ("\tINITRD %s", val));
val = ostree_bootconfig_parser_get (config, "options");
if (val)
g_ptr_array_add (new_lines, g_strdup_printf ("\tAPPEND %s", val));
}
ret = TRUE;
out:
return ret;
}
static gboolean
ostree_bootloader_syslinux_write_config (OstreeBootloader *bootloader,
int bootversion,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
OstreeBootloaderSyslinux *self = OSTREE_BOOTLOADER_SYSLINUX (bootloader);
gs_unref_object GFile *new_config_path = NULL;
gs_free char *config_contents = NULL;
gs_free char *new_config_contents = NULL;
gs_unref_ptrarray GPtrArray *new_lines = NULL;
gs_unref_ptrarray GPtrArray *tmp_lines = NULL;
gs_free char *kernel_arg = NULL;
gboolean saw_default = FALSE;
gboolean regenerate_default = FALSE;
gboolean parsing_label = FALSE;
char **lines = NULL;
char **iter;
guint i;
new_config_path = ot_gfile_resolve_path_printf (self->sysroot->path, "boot/loader.%d/syslinux.cfg",
bootversion);
/* This should follow the symbolic link to the current bootversion. */
config_contents = gs_file_load_contents_utf8 (self->config_path, cancellable, error);
if (!config_contents)
goto out;
lines = g_strsplit (config_contents, "\n", -1);
new_lines = g_ptr_array_new_with_free_func (g_free);
tmp_lines = g_ptr_array_new_with_free_func (g_free);
/* Note special iteration condition here; we want to also loop one
* more time at the end where line = NULL to ensure we finish off
* processing the last LABEL.
*/
iter = lines;
while (TRUE)
{
char *line = *iter;
gboolean skip = FALSE;
if (parsing_label &&
(line == NULL || !g_str_has_prefix (line, "\t")))
{
parsing_label = FALSE;
if (kernel_arg == NULL)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"No KERNEL argument found after LABEL");
goto out;
}
/* If this is a non-ostree kernel, just emit the lines
* we saw.
*/
if (!g_str_has_prefix (kernel_arg, "/ostree/"))
{
for (i = 0; i < tmp_lines->len; i++)
{
g_ptr_array_add (new_lines, tmp_lines->pdata[i]);
tmp_lines->pdata[i] = NULL; /* Transfer ownership */
}
}
else
{
/* Otherwise, we drop the config on the floor - it
* will be regenerated.
*/
g_ptr_array_set_size (tmp_lines, 0);
}
}
if (line == NULL)
break;
if (!parsing_label &&
(g_str_has_prefix (line, "LABEL ")))
{
parsing_label = TRUE;
g_ptr_array_set_size (tmp_lines, 0);
}
else if (parsing_label && g_str_has_prefix (line, "\tKERNEL "))
{
g_free (kernel_arg);
kernel_arg = g_strdup (line + strlen ("\tKERNEL "));
}
else if (!parsing_label &&
(g_str_has_prefix (line, "DEFAULT ")))
{
saw_default = TRUE;
if (g_str_has_prefix (line, "DEFAULT ostree:"))
regenerate_default = TRUE;
skip = TRUE;
}
if (skip)
{
g_free (line);
}
else
{
if (parsing_label)
{
g_ptr_array_add (tmp_lines, line);
}
else
{
g_ptr_array_add (new_lines, line);
}
}
/* Transfer ownership */
*iter = NULL;
iter++;
}
if (!saw_default)
regenerate_default = TRUE;
if (!append_config_from_boostree_loader_entries (self, regenerate_default,
bootversion, new_lines,
cancellable, error))
goto out;
new_config_contents = _ostree_sysroot_join_lines (new_lines);
if (strcmp (new_config_contents, config_contents) != 0)
{
if (!g_file_replace_contents (new_config_path, new_config_contents,
strlen (new_config_contents),
NULL, FALSE, G_FILE_CREATE_NONE,
NULL, cancellable, error))
goto out;
g_print ("Saved new version of %s\n", gs_file_get_path_cached (self->config_path));
}
ret = TRUE;
out:
g_free (lines); /* Note we freed elements individually */
return ret;
}
static void
ostree_bootloader_syslinux_finalize (GObject *object)
{
OstreeBootloaderSyslinux *self = OSTREE_BOOTLOADER_SYSLINUX (object);
g_clear_object (&self->sysroot);
g_clear_object (&self->config_path);
G_OBJECT_CLASS (ostree_bootloader_syslinux_parent_class)->finalize (object);
}
void
ostree_bootloader_syslinux_init (OstreeBootloaderSyslinux *self)
{
}
static void
ostree_bootloader_syslinux_bootloader_iface_init (OstreeBootloaderInterface *iface)
{
iface->query = ostree_bootloader_syslinux_query;
iface->get_name = ostree_bootloader_syslinux_get_name;
iface->write_config = ostree_bootloader_syslinux_write_config;
}
void
ostree_bootloader_syslinux_class_init (OstreeBootloaderSyslinuxClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = ostree_bootloader_syslinux_finalize;
}
OstreeBootloaderSyslinux *
ostree_bootloader_syslinux_new (OstreeSysroot *sysroot)
{
OstreeBootloaderSyslinux *self = g_object_new (OSTREE_TYPE_BOOTLOADER_SYSLINUX, NULL);
self->sysroot = g_object_ref (sysroot);
self->config_path = g_file_resolve_relative_path (self->sysroot->path, "boot/syslinux/syslinux.cfg");
return self;
}
@@ -0,0 +1,38 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2013 Colin Walters <walters@verbum.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*/
#pragma once
#include "ostree-bootloader.h"
G_BEGIN_DECLS
#define OSTREE_TYPE_BOOTLOADER_SYSLINUX (ostree_bootloader_syslinux_get_type ())
#define OSTREE_BOOTLOADER_SYSLINUX(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), OSTREE_TYPE_BOOTLOADER_SYSLINUX, OstreeBootloaderSyslinux))
#define OSTREE_IS_BOOTLOADER_SYSLINUX(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), OSTREE_TYPE_BOOTLOADER_SYSLINUX))
typedef struct _OstreeBootloaderSyslinux OstreeBootloaderSyslinux;
GType ostree_bootloader_syslinux_get_type (void) G_GNUC_CONST;
OstreeBootloaderSyslinux * ostree_bootloader_syslinux_new (OstreeSysroot *sysroot);
G_END_DECLS
+180
View File
@@ -0,0 +1,180 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2013 Collabora Ltd
*
* Based on ot-bootloader-syslinux.c by Colin Walters <walters@verbum.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
*/
#include "config.h"
#include "ostree-sysroot-private.h"
#include "ostree-bootloader-uboot.h"
#include "otutil.h"
#include "libgsystem.h"
#include <string.h>
struct _OstreeBootloaderUboot
{
GObject parent_instance;
OstreeSysroot *sysroot;
GFile *config_path;
};
typedef GObjectClass OstreeBootloaderUbootClass;
static void ostree_bootloader_uboot_bootloader_iface_init (OstreeBootloaderInterface *iface);
G_DEFINE_TYPE_WITH_CODE (OstreeBootloaderUboot, ostree_bootloader_uboot, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (OSTREE_TYPE_BOOTLOADER, ostree_bootloader_uboot_bootloader_iface_init));
static gboolean
ostree_bootloader_uboot_query (OstreeBootloader *bootloader)
{
OstreeBootloaderUboot *self = OSTREE_BOOTLOADER_UBOOT (bootloader);
return g_file_query_file_type (self->config_path, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) == G_FILE_TYPE_SYMBOLIC_LINK;
}
static const char *
ostree_bootloader_uboot_get_name (OstreeBootloader *bootloader)
{
return "U-Boot";
}
static gboolean
create_config_from_boot_loader_entries (OstreeBootloaderUboot *self,
int bootversion,
GPtrArray *new_lines,
GCancellable *cancellable,
GError **error)
{
gs_unref_ptrarray GPtrArray *boot_loader_configs = NULL;
OstreeBootconfigParser *config;
const char *val;
if (!_ostree_sysroot_read_boot_loader_configs (self->sysroot, bootversion, &boot_loader_configs,
cancellable, error))
return FALSE;
/* U-Boot doesn't support a menu so just pick the first one since the list is ordered */
config = boot_loader_configs->pdata[0];
val = ostree_bootconfig_parser_get (config, "linux");
if (!val)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"No \"linux\" key in bootloader config");
return FALSE;
}
g_ptr_array_add (new_lines, g_strdup_printf ("kernel_image=%s", val));
val = ostree_bootconfig_parser_get (config, "initrd");
if (val)
g_ptr_array_add (new_lines, g_strdup_printf ("ramdisk_image=%s", val));
val = ostree_bootconfig_parser_get (config, "options");
if (val)
g_ptr_array_add (new_lines, g_strdup_printf ("bootargs=%s", val));
return TRUE;
}
static gboolean
ostree_bootloader_uboot_write_config (OstreeBootloader *bootloader,
int bootversion,
GCancellable *cancellable,
GError **error)
{
OstreeBootloaderUboot *self = OSTREE_BOOTLOADER_UBOOT (bootloader);
gs_unref_object GFile *new_config_path = NULL;
gs_free char *config_contents = NULL;
gs_free char *new_config_contents = NULL;
gs_unref_ptrarray GPtrArray *new_lines = NULL;
/* This should follow the symbolic link to the current bootversion. */
config_contents = gs_file_load_contents_utf8 (self->config_path, cancellable, error);
if (!config_contents)
return FALSE;
new_config_path = ot_gfile_resolve_path_printf (self->sysroot->path, "boot/loader.%d/uEnv.txt",
bootversion);
new_lines = g_ptr_array_new_with_free_func (g_free);
if (!create_config_from_boot_loader_entries (self, bootversion, new_lines,
cancellable, error))
return FALSE;
new_config_contents = _ostree_sysroot_join_lines (new_lines);
if (strcmp (new_config_contents, config_contents) != 0)
{
if (!g_file_replace_contents (new_config_path, new_config_contents,
strlen (new_config_contents),
NULL, FALSE, G_FILE_CREATE_NONE,
NULL, cancellable, error))
return FALSE;
g_print ("Saved new version of %s\n", gs_file_get_path_cached (self->config_path));
}
return TRUE;
}
static void
ostree_bootloader_uboot_finalize (GObject *object)
{
OstreeBootloaderUboot *self = OSTREE_BOOTLOADER_UBOOT (object);
g_clear_object (&self->sysroot);
g_clear_object (&self->config_path);
G_OBJECT_CLASS (ostree_bootloader_uboot_parent_class)->finalize (object);
}
void
ostree_bootloader_uboot_init (OstreeBootloaderUboot *self)
{
}
static void
ostree_bootloader_uboot_bootloader_iface_init (OstreeBootloaderInterface *iface)
{
iface->query = ostree_bootloader_uboot_query;
iface->get_name = ostree_bootloader_uboot_get_name;
iface->write_config = ostree_bootloader_uboot_write_config;
}
void
ostree_bootloader_uboot_class_init (OstreeBootloaderUbootClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = ostree_bootloader_uboot_finalize;
}
OstreeBootloaderUboot *
ostree_bootloader_uboot_new (OstreeSysroot *sysroot)
{
OstreeBootloaderUboot *self = g_object_new (OSTREE_TYPE_BOOTLOADER_UBOOT, NULL);
self->sysroot = g_object_ref (sysroot);
self->config_path = g_file_resolve_relative_path (self->sysroot->path, "boot/uEnv.txt");
return self;
}
+41
View File
@@ -0,0 +1,41 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2013 Collabora Ltd
*
* Based on ot-bootloader-syslinux.h by Colin Walters <walters@verbum.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
*/
#pragma once
#include "ostree-bootloader.h"
G_BEGIN_DECLS
#define OSTREE_TYPE_BOOTLOADER_UBOOT (ostree_bootloader_uboot_get_type ())
#define OSTREE_BOOTLOADER_UBOOT(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), OSTREE_TYPE_BOOTLOADER_UBOOT, OstreeBootloaderUboot))
#define OSTREE_IS_BOOTLOADER_UBOOT(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), OSTREE_TYPE_BOOTLOADER_UBOOT))
typedef struct _OstreeBootloaderUboot OstreeBootloaderUboot;
GType ostree_bootloader_uboot_get_type (void) G_GNUC_CONST;
OstreeBootloaderUboot * ostree_bootloader_uboot_new (OstreeSysroot *sysroot);
G_END_DECLS
+63
View File
@@ -0,0 +1,63 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2013 Colin Walters <walters@verbum.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include "ostree-bootloader.h"
#include "libgsystem.h"
G_DEFINE_INTERFACE (OstreeBootloader, ostree_bootloader, G_TYPE_OBJECT)
static void
ostree_bootloader_default_init (OstreeBootloaderInterface *iface)
{
}
gboolean
ostree_bootloader_query (OstreeBootloader *self)
{
g_return_val_if_fail (OSTREE_IS_BOOTLOADER (self), FALSE);
return OSTREE_BOOTLOADER_GET_IFACE (self)->query (self);
}
/**
* ostree_bootloader_get_name:
*
* Returns: (transfer none): Name of this bootloader
*/
const char *
ostree_bootloader_get_name (OstreeBootloader *self)
{
g_return_val_if_fail (OSTREE_IS_BOOTLOADER (self), NULL);
return OSTREE_BOOTLOADER_GET_IFACE (self)->get_name (self);
}
gboolean
ostree_bootloader_write_config (OstreeBootloader *self,
int bootversion,
GCancellable *cancellable,
GError **error)
{
g_return_val_if_fail (OSTREE_IS_BOOTLOADER (self), FALSE);
return OSTREE_BOOTLOADER_GET_IFACE (self)->write_config (self, bootversion,
cancellable, error);
}
+60
View File
@@ -0,0 +1,60 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2013 Colin Walters <walters@verbum.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*/
#pragma once
#include <gio/gio.h>
G_BEGIN_DECLS
#define OSTREE_TYPE_BOOTLOADER (ostree_bootloader_get_type ())
#define OSTREE_BOOTLOADER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), OSTREE_TYPE_BOOTLOADER, OstreeBootloader))
#define OSTREE_IS_BOOTLOADER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), OSTREE_TYPE_BOOTLOADER))
#define OSTREE_BOOTLOADER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), OSTREE_TYPE_BOOTLOADER, OstreeBootloaderInterface))
typedef struct _OstreeBootloader OstreeBootloader;
typedef struct _OstreeBootloaderInterface OstreeBootloaderInterface;
struct _OstreeBootloaderInterface
{
GTypeInterface g_iface;
/* virtual functions */
gboolean (* query) (OstreeBootloader *self);
const char * (* get_name) (OstreeBootloader *self);
gboolean (* write_config) (OstreeBootloader *self,
int bootversion,
GCancellable *cancellable,
GError **error);
};
GType ostree_bootloader_get_type (void) G_GNUC_CONST;
gboolean ostree_bootloader_query (OstreeBootloader *self);
const char *ostree_bootloader_get_name (OstreeBootloader *self);
gboolean ostree_bootloader_write_config (OstreeBootloader *self,
int bootversion,
GCancellable *cancellable,
GError **error);
G_END_DECLS
+532
View File
@@ -0,0 +1,532 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2013 Colin Walters <walters@verbum.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include "otutil.h"
#include "libgsystem.h"
#include "ostree-sysroot-private.h"
static gboolean
list_deployment_dirs_for_os (GFile *osdir,
GPtrArray *inout_deployments,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
const char *osname = gs_file_get_basename_cached (osdir);
gs_unref_object GFileEnumerator *dir_enum = NULL;
gs_unref_object GFile *osdeploy_dir = NULL;
GError *temp_error = NULL;
osdeploy_dir = g_file_get_child (osdir, "deploy");
dir_enum = g_file_enumerate_children (osdeploy_dir, OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
NULL, &temp_error);
if (!dir_enum)
{
if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
g_clear_error (&temp_error);
goto done;
}
else
{
g_propagate_error (error, temp_error);
goto out;
}
}
while (TRUE)
{
const char *name;
GFileInfo *file_info = NULL;
GFile *child = NULL;
gs_unref_object OstreeDeployment *deployment = NULL;
gs_free char *csum = NULL;
gint deployserial;
if (!gs_file_enumerator_iterate (dir_enum, &file_info, &child,
cancellable, error))
goto out;
if (file_info == NULL)
break;
name = g_file_info_get_name (file_info);
if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY)
continue;
if (!_ostree_sysroot_parse_deploy_path_name (name, &csum, &deployserial, error))
goto out;
deployment = ostree_deployment_new (-1, osname, csum, deployserial, NULL, -1);
g_ptr_array_add (inout_deployments, g_object_ref (deployment));
}
done:
ret = TRUE;
out:
return ret;
}
static gboolean
list_all_deployment_directories (OstreeSysroot *self,
GPtrArray **out_deployments,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_object GFileEnumerator *dir_enum = NULL;
gs_unref_object GFile *deploydir = NULL;
gs_unref_ptrarray GPtrArray *ret_deployments = NULL;
GError *temp_error = NULL;
deploydir = g_file_resolve_relative_path (self->path, "ostree/deploy");
ret_deployments = g_ptr_array_new_with_free_func (g_object_unref);
dir_enum = g_file_enumerate_children (deploydir, OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable, &temp_error);
if (!dir_enum)
{
if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
g_clear_error (&temp_error);
goto done;
}
else
{
g_propagate_error (error, temp_error);
goto out;
}
}
while (TRUE)
{
GFileInfo *file_info = NULL;
GFile *child = NULL;
if (!gs_file_enumerator_iterate (dir_enum, &file_info, &child,
NULL, error))
goto out;
if (file_info == NULL)
break;
if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY)
continue;
if (!list_deployment_dirs_for_os (child, ret_deployments, cancellable, error))
goto out;
}
done:
ret = TRUE;
ot_transfer_out_value (out_deployments, &ret_deployments);
out:
return ret;
}
static gboolean
parse_bootdir_name (const char *name,
char **out_osname,
char **out_csum)
{
const char *lastdash;
if (out_osname)
*out_osname = NULL;
if (out_csum)
*out_csum = NULL;
lastdash = strrchr (name, '-');
if (!lastdash)
return FALSE;
if (!ostree_validate_checksum_string (lastdash + 1, NULL))
return FALSE;
if (out_osname)
*out_osname = g_strndup (name, lastdash - name);
if (out_csum)
*out_csum = g_strdup (lastdash + 1);
return TRUE;
}
static gboolean
list_all_boot_directories (OstreeSysroot *self,
GPtrArray **out_bootdirs,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_object GFileEnumerator *dir_enum = NULL;
gs_unref_object GFile *boot_ostree = NULL;
gs_unref_ptrarray GPtrArray *ret_bootdirs = NULL;
GError *temp_error = NULL;
boot_ostree = g_file_resolve_relative_path (self->path, "boot/ostree");
ret_bootdirs = g_ptr_array_new_with_free_func (g_object_unref);
dir_enum = g_file_enumerate_children (boot_ostree, OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable, &temp_error);
if (!dir_enum)
{
if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
g_clear_error (&temp_error);
goto done;
}
else
{
g_propagate_error (error, temp_error);
goto out;
}
}
while (TRUE)
{
GFileInfo *file_info = NULL;
GFile *child = NULL;
const char *name;
if (!gs_file_enumerator_iterate (dir_enum, &file_info, &child,
NULL, error))
goto out;
if (file_info == NULL)
break;
if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY)
continue;
/* Only look at directories ending in -CHECKSUM; nothing else
* should be in here, but let's be conservative.
*/
name = g_file_info_get_name (file_info);
if (!parse_bootdir_name (name, NULL, NULL))
continue;
g_ptr_array_add (ret_bootdirs, g_object_ref (child));
}
done:
ret = TRUE;
ot_transfer_out_value (out_bootdirs, &ret_bootdirs);
out:
return ret;
}
static gboolean
cleanup_other_bootversions (OstreeSysroot *self,
int bootversion,
int subbootversion,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
int cleanup_bootversion;
int cleanup_subbootversion;
gs_unref_object GFile *cleanup_boot_dir = NULL;
cleanup_bootversion = bootversion == 0 ? 1 : 0;
cleanup_subbootversion = subbootversion == 0 ? 1 : 0;
cleanup_boot_dir = ot_gfile_resolve_path_printf (self->path, "boot/loader.%d", cleanup_bootversion);
if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error))
goto out;
g_clear_object (&cleanup_boot_dir);
cleanup_boot_dir = ot_gfile_resolve_path_printf (self->path, "ostree/boot.%d", cleanup_bootversion);
if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error))
goto out;
g_clear_object (&cleanup_boot_dir);
cleanup_boot_dir = ot_gfile_resolve_path_printf (self->path, "ostree/boot.%d.0", cleanup_bootversion);
if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error))
goto out;
g_clear_object (&cleanup_boot_dir);
cleanup_boot_dir = ot_gfile_resolve_path_printf (self->path, "ostree/boot.%d.1", cleanup_bootversion);
if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error))
goto out;
g_clear_object (&cleanup_boot_dir);
cleanup_boot_dir = ot_gfile_resolve_path_printf (self->path, "ostree/boot.%d.%d", bootversion,
cleanup_subbootversion);
if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error))
goto out;
g_clear_object (&cleanup_boot_dir);
ret = TRUE;
out:
return ret;
}
static gboolean
cleanup_old_deployments (OstreeSysroot *self,
GPtrArray *deployments,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
guint32 root_device;
guint64 root_inode;
guint i;
gs_unref_object GFile *active_root = g_file_new_for_path ("/");
gs_unref_hashtable GHashTable *active_deployment_dirs = NULL;
gs_unref_hashtable GHashTable *active_boot_checksums = NULL;
gs_unref_ptrarray GPtrArray *all_deployment_dirs = NULL;
gs_unref_ptrarray GPtrArray *all_boot_dirs = NULL;
if (!_ostree_sysroot_get_devino (active_root, &root_device, &root_inode,
cancellable, error))
goto out;
active_deployment_dirs = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, NULL, g_object_unref);
active_boot_checksums = g_hash_table_new_full (g_str_hash, (GEqualFunc)g_str_equal, g_free, NULL);
for (i = 0; i < deployments->len; i++)
{
OstreeDeployment *deployment = deployments->pdata[i];
GFile *deployment_path = ostree_sysroot_get_deployment_directory (self, deployment);
char *bootcsum = g_strdup (ostree_deployment_get_bootcsum (deployment));
/* Transfer ownership */
g_hash_table_insert (active_deployment_dirs, deployment_path, deployment_path);
g_hash_table_insert (active_boot_checksums, bootcsum, bootcsum);
}
if (!list_all_deployment_directories (self, &all_deployment_dirs,
cancellable, error))
goto out;
for (i = 0; i < all_deployment_dirs->len; i++)
{
OstreeDeployment *deployment = all_deployment_dirs->pdata[i];
gs_unref_object GFile *deployment_path = ostree_sysroot_get_deployment_directory (self, deployment);
gs_unref_object GFile *origin_path = ostree_sysroot_get_deployment_origin_path (deployment_path);
if (!g_hash_table_lookup (active_deployment_dirs, deployment_path))
{
guint32 device;
guint64 inode;
if (!_ostree_sysroot_get_devino (deployment_path, &device, &inode,
cancellable, error))
goto out;
/* This shouldn't happen, because higher levels should
* disallow having the booted deployment not in the active
* deployment list, but let's be extra safe. */
if (device == root_device && inode == root_inode)
continue;
g_print ("ostadmin: Deleting deployment %s\n", gs_file_get_path_cached (deployment_path));
if (!gs_shutil_rm_rf (deployment_path, cancellable, error))
goto out;
if (!gs_shutil_rm_rf (origin_path, cancellable, error))
goto out;
}
}
if (!list_all_boot_directories (self, &all_boot_dirs,
cancellable, error))
goto out;
for (i = 0; i < all_boot_dirs->len; i++)
{
GFile *bootdir = all_boot_dirs->pdata[i];
gs_free char *osname = NULL;
gs_free char *bootcsum = NULL;
if (!parse_bootdir_name (gs_file_get_basename_cached (bootdir),
&osname, &bootcsum))
g_assert_not_reached ();
if (g_hash_table_lookup (active_boot_checksums, bootcsum))
continue;
g_print ("ostadmin: Deleting bootdir %s\n", gs_file_get_path_cached (bootdir));
if (!gs_shutil_rm_rf (bootdir, cancellable, error))
goto out;
}
ret = TRUE;
out:
return ret;
}
static gboolean
cleanup_ref_prefix (OstreeRepo *repo,
int bootversion,
int subbootversion,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_free char *prefix = NULL;
gs_unref_hashtable GHashTable *refs = NULL;
GHashTableIter hashiter;
gpointer hashkey, hashvalue;
prefix = g_strdup_printf ("ostree/%d/%d", bootversion, subbootversion);
if (!ostree_repo_list_refs (repo, prefix, &refs, cancellable, error))
goto out;
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
goto out;
g_hash_table_iter_init (&hashiter, refs);
while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
{
const char *suffix = hashkey;
gs_free char *ref = g_strconcat (prefix, "/", suffix, NULL);
ostree_repo_transaction_set_refspec (repo, ref, NULL);
}
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
goto out;
ret = TRUE;
out:
ostree_repo_abort_transaction (repo, cancellable, NULL);
return ret;
}
static gboolean
generate_deployment_refs_and_prune (OstreeSysroot *self,
OstreeRepo *repo,
int bootversion,
int subbootversion,
GPtrArray *deployments,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
int cleanup_bootversion;
int cleanup_subbootversion;
guint i;
gint n_objects_total, n_objects_pruned;
guint64 freed_space;
cleanup_bootversion = (bootversion == 0) ? 1 : 0;
cleanup_subbootversion = (subbootversion == 0) ? 1 : 0;
if (!cleanup_ref_prefix (repo, cleanup_bootversion, 0,
cancellable, error))
goto out;
if (!cleanup_ref_prefix (repo, cleanup_bootversion, 1,
cancellable, error))
goto out;
if (!cleanup_ref_prefix (repo, bootversion, cleanup_subbootversion,
cancellable, error))
goto out;
for (i = 0; i < deployments->len; i++)
{
OstreeDeployment *deployment = deployments->pdata[i];
gs_free char *refname = g_strdup_printf ("ostree/%d/%d/%u",
bootversion, subbootversion,
i);
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
goto out;
ostree_repo_transaction_set_refspec (repo, refname, ostree_deployment_get_csum (deployment));
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
goto out;
}
if (!ostree_repo_prune (repo, OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY, 0,
&n_objects_total, &n_objects_pruned, &freed_space,
cancellable, error))
goto out;
if (freed_space > 0)
{
char *freed_space_str = g_format_size_full (freed_space, 0);
g_print ("Freed objects: %s\n", freed_space_str);
}
ret = TRUE;
out:
ostree_repo_abort_transaction (repo, cancellable, NULL);
return ret;
}
/**
* ostree_sysroot_cleanup:
* @self: Sysroot
* @cancellable: Cancellable
* @error: Error
*
* Delete any state that resulted from a partially completed
* transaction, such as incomplete deployments.
*/
gboolean
ostree_sysroot_cleanup (OstreeSysroot *self,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_ptrarray GPtrArray *deployments = NULL;
gs_unref_object OstreeRepo *repo = NULL;
int bootversion;
int subbootversion;
if (!ostree_sysroot_list_deployments (self, &bootversion, &deployments,
cancellable, error))
goto out;
if (!ostree_sysroot_read_current_subbootversion (self, bootversion, &subbootversion,
cancellable, error))
goto out;
if (!cleanup_other_bootversions (self, bootversion, subbootversion,
cancellable, error))
goto out;
if (!cleanup_old_deployments (self, deployments,
cancellable, error))
goto out;
if (deployments->len > 0)
{
if (!ostree_sysroot_get_repo (self, &repo, cancellable, error))
goto out;
if (!generate_deployment_refs_and_prune (self, repo, bootversion,
subbootversion, deployments,
cancellable, error))
goto out;
}
ret = TRUE;
out:
return ret;
}
+57
View File
@@ -0,0 +1,57 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2012,2013 Colin Walters <walters@verbum.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#pragma once
#include "ostree.h"
G_BEGIN_DECLS
struct OstreeSysroot {
GObject parent;
GFile *path;
int sysroot_fd;
};
gboolean
_ostree_sysroot_read_boot_loader_configs (OstreeSysroot *self,
int bootversion,
GPtrArray **out_loader_configs,
GCancellable *cancellable,
GError **error);
gboolean
_ostree_sysroot_parse_deploy_path_name (const char *name,
char **out_csum,
int *out_serial,
GError **error);
gboolean
_ostree_sysroot_get_devino (GFile *path,
guint32 *out_device,
guint64 *out_inode,
GCancellable *cancellable,
GError **error);
char *_ostree_sysroot_join_lines (GPtrArray *lines);
G_END_DECLS
+625 -9
View File
@@ -23,7 +23,9 @@
#include "otutil.h"
#include "libgsystem.h"
#include "ostree-sysroot.h"
#include "ostree-sysroot-private.h"
#include "ostree-bootloader-uboot.h"
#include "ostree-bootloader-syslinux.h"
/**
* SECTION:libostree-sysroot
@@ -35,14 +37,6 @@
* Inside this directory is an #OstreeRepo in /ostree/repo, plus a set
* of deployments in /ostree/deploy.
*/
struct OstreeSysroot {
GObject parent;
GFile *path;
int sysroot_fd;
};
typedef struct {
GObjectClass parent_class;
} OstreeSysrootClass;
@@ -175,6 +169,28 @@ ostree_sysroot_get_path (OstreeSysroot *self)
return self->path;
}
gboolean
_ostree_sysroot_get_devino (GFile *path,
guint32 *out_device,
guint64 *out_inode,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_object GFileInfo *finfo = g_file_query_info (path, "unix::device,unix::inode",
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable, error);
if (!finfo)
goto out;
ret = TRUE;
*out_device = g_file_info_get_attribute_uint32 (finfo, "unix::device");
*out_inode = g_file_info_get_attribute_uint64 (finfo, "unix::inode");
out:
return ret;
}
/**
* ostree_sysroot_ensure_initialized:
* @self:
@@ -223,3 +239,603 @@ ostree_sysroot_ensure_initialized (OstreeSysroot *self,
out:
return ret;
}
static void
match_info_cleanup (void *loc)
{
GMatchInfo **match = (GMatchInfo**)loc;
if (*match) g_match_info_unref (*match);
}
gboolean
_ostree_sysroot_parse_deploy_path_name (const char *name,
char **out_csum,
int *out_serial,
GError **error)
{
gboolean ret = FALSE;
__attribute__((cleanup(match_info_cleanup))) GMatchInfo *match = NULL;
gs_free char *serial_str = NULL;
static gsize regex_initialized;
static GRegex *regex;
if (g_once_init_enter (&regex_initialized))
{
regex = g_regex_new ("^([0-9a-f]+)\\.([0-9]+)$", 0, 0, NULL);
g_assert (regex);
g_once_init_leave (&regex_initialized, 1);
}
if (!g_regex_match (regex, name, 0, &match))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Invalid deploy name '%s', expected CHECKSUM.TREESERIAL", name);
goto out;
}
*out_csum = g_match_info_fetch (match, 1);
serial_str = g_match_info_fetch (match, 2);
*out_serial = (int)g_ascii_strtoll (serial_str, NULL, 10);
ret = TRUE;
out:
return ret;
}
/**
* ostree_sysroot_read_current_subbootversion:
* @self: Sysroot
* @bootversion: Current boot version
* @out_subbootversion: (out): The active subbootversion
* @cancellable: Cancellable
* @error: Error
*
* Determine the active subbootversion.
*/
gboolean
ostree_sysroot_read_current_subbootversion (OstreeSysroot *self,
int bootversion,
int *out_subbootversion,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_object GFile *ostree_dir = g_file_get_child (self->path, "ostree");
gs_free char *ostree_bootdir_name = g_strdup_printf ("boot.%d", bootversion);
gs_unref_object GFile *ostree_bootdir = g_file_resolve_relative_path (ostree_dir, ostree_bootdir_name);
gs_unref_object GFile *ostree_subbootdir = NULL;
if (!ot_gfile_query_symlink_target_allow_noent (ostree_bootdir, &ostree_subbootdir,
cancellable, error))
goto out;
if (ostree_subbootdir == NULL)
{
*out_subbootversion = 0;
}
else
{
const char *current_subbootdir_name = gs_file_get_basename_cached (ostree_subbootdir);
if (g_str_has_suffix (current_subbootdir_name, ".0"))
*out_subbootversion = 0;
else if (g_str_has_suffix (current_subbootdir_name, ".1"))
*out_subbootversion = 1;
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Invalid target '%s' in %s",
gs_file_get_path_cached (ostree_subbootdir),
gs_file_get_path_cached (ostree_bootdir));
goto out;
}
}
ret = TRUE;
out:
return ret;
}
gboolean
_ostree_sysroot_read_boot_loader_configs (OstreeSysroot *self,
int bootversion,
GPtrArray **out_loader_configs,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_object GFileEnumerator *dir_enum = NULL;
gs_unref_object GFile *loader_entries_dir = NULL;
gs_unref_ptrarray GPtrArray *ret_loader_configs = NULL;
GError *temp_error = NULL;
loader_entries_dir = ot_gfile_resolve_path_printf (self->path, "boot/loader.%d/entries",
bootversion);
ret_loader_configs = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
dir_enum = g_file_enumerate_children (loader_entries_dir, OSTREE_GIO_FAST_QUERYINFO,
0, NULL, &temp_error);
if (!dir_enum)
{
if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
g_clear_error (&temp_error);
goto done;
}
else
{
g_propagate_error (error, temp_error);
goto out;
}
}
while (TRUE)
{
GFileInfo *file_info;
GFile *child;
const char *name;
if (!gs_file_enumerator_iterate (dir_enum, &file_info, &child,
cancellable, error))
goto out;
if (file_info == NULL)
break;
name = g_file_info_get_name (file_info);
if (g_str_has_prefix (name, "ostree-") &&
g_str_has_suffix (name, ".conf") &&
g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR)
{
gs_unref_object OstreeBootconfigParser *config = ostree_bootconfig_parser_new ();
if (!ostree_bootconfig_parser_parse (config, child, cancellable, error))
{
g_prefix_error (error, "Parsing %s: ", gs_file_get_path_cached (child));
goto out;
}
g_ptr_array_add (ret_loader_configs, g_object_ref (config));
}
}
done:
gs_transfer_out_value (out_loader_configs, &ret_loader_configs);
ret = TRUE;
out:
return ret;
}
static gboolean
read_current_bootversion (OstreeSysroot *self,
int *out_bootversion,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_object GFile *boot_loader_path = g_file_resolve_relative_path (self->path, "boot/loader");
gs_unref_object GFileInfo *info = NULL;
const char *target;
int ret_bootversion;
if (!ot_gfile_query_info_allow_noent (boot_loader_path, OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
&info,
cancellable, error))
goto out;
if (info == NULL)
ret_bootversion = 0;
else
{
if (g_file_info_get_file_type (info) != G_FILE_TYPE_SYMBOLIC_LINK)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Not a symbolic link: %s", gs_file_get_path_cached (boot_loader_path));
goto out;
}
target = g_file_info_get_symlink_target (info);
if (g_strcmp0 (target, "loader.0") == 0)
ret_bootversion = 0;
else if (g_strcmp0 (target, "loader.1") == 0)
ret_bootversion = 1;
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Invalid target '%s' in %s", target, gs_file_get_path_cached (boot_loader_path));
goto out;
}
}
ret = TRUE;
*out_bootversion = ret_bootversion;
out:
return ret;
}
static gboolean
parse_origin (OstreeSysroot *self,
GFile *deployment_path,
GKeyFile **out_origin,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
GKeyFile *ret_origin = NULL;
gs_unref_object GFile *origin_path = ostree_sysroot_get_deployment_origin_path (deployment_path);
gs_free char *origin_contents = NULL;
if (!ot_gfile_load_contents_utf8_allow_noent (origin_path, &origin_contents,
cancellable, error))
goto out;
if (origin_contents)
{
ret_origin = g_key_file_new ();
if (!g_key_file_load_from_data (ret_origin, origin_contents, -1, 0, error))
goto out;
}
ret = TRUE;
gs_transfer_out_value (out_origin, &ret_origin);
out:
if (error)
g_prefix_error (error, "Parsing %s: ", gs_file_get_path_cached (origin_path));
if (ret_origin)
g_key_file_unref (ret_origin);
return ret;
}
static gboolean
parse_bootlink (const char *bootlink,
int *out_entry_bootversion,
char **out_osname,
char **out_bootcsum,
int *out_treebootserial,
GError **error)
{
gboolean ret = FALSE;
__attribute__((cleanup(match_info_cleanup))) GMatchInfo *match = NULL;
gs_free char *bootversion_str = NULL;
gs_free char *treebootserial_str = NULL;
static gsize regex_initialized;
static GRegex *regex;
if (g_once_init_enter (&regex_initialized))
{
regex = g_regex_new ("^/ostree/boot.([01])/([^/]+)/([^/]+)/([0-9]+)$", 0, 0, NULL);
g_assert (regex);
g_once_init_leave (&regex_initialized, 1);
}
if (!g_regex_match (regex, bootlink, 0, &match))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Invalid ostree= argument '%s', expected ostree=/ostree/boot.BOOTVERSION/OSNAME/BOOTCSUM/TREESERIAL", bootlink);
goto out;
}
bootversion_str = g_match_info_fetch (match, 1);
*out_entry_bootversion = (int)g_ascii_strtoll (bootversion_str, NULL, 10);
*out_osname = g_match_info_fetch (match, 2);
*out_bootcsum = g_match_info_fetch (match, 3);
treebootserial_str = g_match_info_fetch (match, 4);
*out_treebootserial = (int)g_ascii_strtoll (treebootserial_str, NULL, 10);
ret = TRUE;
out:
return ret;
}
static gboolean
parse_deployment (OstreeSysroot *self,
const char *boot_link,
OstreeDeployment **out_deployment,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
const char *relative_boot_link;
gs_unref_object OstreeDeployment *ret_deployment = NULL;
int entry_boot_version;
int treebootserial = -1;
int deployserial = -1;
gs_free char *osname = NULL;
gs_free char *bootcsum = NULL;
gs_free char *treecsum = NULL;
gs_unref_object GFile *treebootserial_link = NULL;
gs_unref_object GFileInfo *treebootserial_info = NULL;
gs_unref_object GFile *treebootserial_target = NULL;
GKeyFile *origin = NULL;
if (!parse_bootlink (boot_link, &entry_boot_version,
&osname, &bootcsum, &treebootserial,
error))
goto out;
relative_boot_link = boot_link;
if (*relative_boot_link == '/')
relative_boot_link++;
treebootserial_link = g_file_resolve_relative_path (self->path, relative_boot_link);
treebootserial_info = g_file_query_info (treebootserial_link, OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable, error);
if (!treebootserial_info)
goto out;
if (!ot_gfile_get_symlink_target_from_info (treebootserial_link, treebootserial_info,
&treebootserial_target, cancellable, error))
goto out;
if (!_ostree_sysroot_parse_deploy_path_name (gs_file_get_basename_cached (treebootserial_target),
&treecsum, &deployserial, error))
goto out;
if (!parse_origin (self, treebootserial_target, &origin,
cancellable, error))
goto out;
ret_deployment = ostree_deployment_new (-1, osname, treecsum, deployserial,
bootcsum, treebootserial);
if (origin)
ostree_deployment_set_origin (ret_deployment, origin);
ret = TRUE;
gs_transfer_out_value (out_deployment, &ret_deployment);
out:
if (origin)
g_key_file_unref (origin);
return ret;
}
static char *
get_ostree_kernel_arg_from_config (OstreeBootconfigParser *config)
{
const char *options;
char *ret = NULL;
char **opts, **iter;
options = ostree_bootconfig_parser_get (config, "options");
if (!options)
return NULL;
opts = g_strsplit (options, " ", -1);
for (iter = opts; *iter; iter++)
{
const char *opt = *iter;
if (g_str_has_prefix (opt, "ostree="))
{
ret = g_strdup (opt + strlen ("ostree="));
break;
}
}
g_strfreev (opts);
return ret;
}
static gboolean
list_deployments_process_one_boot_entry (OstreeSysroot *self,
OstreeBootconfigParser *config,
GPtrArray *inout_deployments,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_free char *ostree_arg = NULL;
gs_unref_object OstreeDeployment *deployment = NULL;
ostree_arg = get_ostree_kernel_arg_from_config (config);
if (ostree_arg == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"No ostree= kernel argument found");
goto out;
}
if (!parse_deployment (self, ostree_arg, &deployment,
cancellable, error))
goto out;
ostree_deployment_set_bootconfig (deployment, config);
g_ptr_array_add (inout_deployments, g_object_ref (deployment));
ret = TRUE;
out:
return ret;
}
static gint
compare_deployments_by_boot_loader_version_reversed (gconstpointer a_pp,
gconstpointer b_pp)
{
OstreeDeployment *a = *((OstreeDeployment**)a_pp);
OstreeDeployment *b = *((OstreeDeployment**)b_pp);
OstreeBootconfigParser *a_bootconfig = ostree_deployment_get_bootconfig (a);
OstreeBootconfigParser *b_bootconfig = ostree_deployment_get_bootconfig (b);
const char *a_version = ostree_bootconfig_parser_get (a_bootconfig, "version");
const char *b_version = ostree_bootconfig_parser_get (b_bootconfig, "version");
if (a_version && b_version)
{
int r = strverscmp (a_version, b_version);
/* Reverse */
return -r;
}
else if (a_version)
return -1;
else
return 1;
}
/**
* ostree_sysroot_list_deployments:
* @sysroot: Sysroot
* @out_current_bootversion: (out): Current bootversion
* @out_deployments: (out) (element-type OstreeDeployment): Deployment list
* @cancellable: Cancellable
* @error: Error
*
* Enumerate all deployments, in the boot order. Also returns the
* active bootversion.
*/
gboolean
ostree_sysroot_list_deployments (OstreeSysroot *self,
int *out_current_bootversion,
GPtrArray **out_deployments,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_ptrarray GPtrArray *boot_loader_configs = NULL;
gs_unref_ptrarray GPtrArray *ret_deployments = NULL;
guint i;
int bootversion = -1;
if (!read_current_bootversion (self, &bootversion, cancellable, error))
goto out;
if (!_ostree_sysroot_read_boot_loader_configs (self, bootversion, &boot_loader_configs,
cancellable, error))
goto out;
ret_deployments = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
for (i = 0; i < boot_loader_configs->len; i++)
{
OstreeBootconfigParser *config = boot_loader_configs->pdata[i];
if (!list_deployments_process_one_boot_entry (self, config, ret_deployments,
cancellable, error))
goto out;
}
g_ptr_array_sort (ret_deployments, compare_deployments_by_boot_loader_version_reversed);
for (i = 0; i < ret_deployments->len; i++)
{
OstreeDeployment *deployment = ret_deployments->pdata[i];
ostree_deployment_set_index (deployment, i);
}
ret = TRUE;
*out_current_bootversion = bootversion;
gs_transfer_out_value (out_deployments, &ret_deployments);
out:
return ret;
}
/**
* ostree_sysroot_get_deployment_directory:
* @self: Sysroot
* @deployment: A deployment
*
* Returns: (transfer full): Path to deployment root directory
*/
GFile *
ostree_sysroot_get_deployment_directory (OstreeSysroot *self,
OstreeDeployment *deployment)
{
gs_free char *path = g_strdup_printf ("ostree/deploy/%s/deploy/%s.%d",
ostree_deployment_get_osname (deployment),
ostree_deployment_get_csum (deployment),
ostree_deployment_get_deployserial (deployment));
return g_file_resolve_relative_path (self->path, path);
}
/**
* ostree_sysroot_get_deployment_origin_path:
* @self: Sysroot
* @deployment: A deployment
*
* Returns: (transfer full): Path to deployment origin file
*/
GFile *
ostree_sysroot_get_deployment_origin_path (GFile *deployment_path)
{
gs_unref_object GFile *deployment_parent = g_file_get_parent (deployment_path);
return ot_gfile_resolve_path_printf (deployment_parent,
"%s.origin",
gs_file_get_path_cached (deployment_path));
}
/**
* ostree_sysroot_get_repo:
* @self: Sysroot
* @out_repo: (out): Repository in sysroot @self
* @cancellable: Cancellable
* @error: Error
*
* Retrieve the OSTree repository in sysroot @self.
*/
gboolean
ostree_sysroot_get_repo (OstreeSysroot *self,
OstreeRepo **out_repo,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_object OstreeRepo *ret_repo = NULL;
gs_unref_object GFile *repo_path = g_file_resolve_relative_path (self->path, "ostree/repo");
ret_repo = ostree_repo_new (repo_path);
if (!ostree_repo_open (ret_repo, cancellable, error))
goto out;
ret = TRUE;
ot_transfer_out_value (out_repo, &ret_repo);
out:
return ret;
}
/**
* ostree_sysroot_query_bootloader:
* @sysroot: Sysroot
*
* Returns: (transfer full): Currently active bootloader in @sysroot
*/
OstreeBootloader *
ostree_sysroot_query_bootloader (OstreeSysroot *self)
{
OstreeBootloaderSyslinux *syslinux;
OstreeBootloaderUboot *uboot;
syslinux = ostree_bootloader_syslinux_new (self);
if (ostree_bootloader_query ((OstreeBootloader*)syslinux))
return (OstreeBootloader*) (syslinux);
uboot = ostree_bootloader_uboot_new (self);
if (ostree_bootloader_query ((OstreeBootloader*)uboot))
return (OstreeBootloader*) (uboot);
return NULL;
}
char *
_ostree_sysroot_join_lines (GPtrArray *lines)
{
GString *buf = g_string_new ("");
guint i;
gboolean prev_was_empty = FALSE;
for (i = 0; i < lines->len; i++)
{
const char *line = lines->pdata[i];
/* Special bit to remove extraneous empty lines */
if (*line == '\0')
{
if (prev_was_empty || i == 0)
continue;
else
prev_was_empty = TRUE;
}
g_string_append (buf, line);
g_string_append_c (buf, '\n');
}
return g_string_free (buf, FALSE);
}
+31
View File
@@ -21,6 +21,8 @@
#pragma once
#include "ostree-repo.h"
#include "ostree-deployment.h"
#include "ostree-bootloader.h"
G_BEGIN_DECLS
@@ -42,5 +44,34 @@ gboolean ostree_sysroot_ensure_initialized (OstreeSysroot *self,
GCancellable *cancellable,
GError **error);
gboolean ostree_sysroot_read_current_subbootversion (OstreeSysroot *self,
int bootversion,
int *out_subbootversion,
GCancellable *cancellable,
GError **error);
gboolean ostree_sysroot_list_deployments (OstreeSysroot *self,
int *out_bootversion,
GPtrArray **out_deployments,
GCancellable *cancellable,
GError **error);
GFile *ostree_sysroot_get_deployment_directory (OstreeSysroot *self,
OstreeDeployment *deployment);
GFile * ostree_sysroot_get_deployment_origin_path (GFile *deployment_path);
gboolean ostree_sysroot_cleanup (OstreeSysroot *self,
GCancellable *cancellable,
GError **error);
gboolean ostree_sysroot_get_repo (OstreeSysroot *self,
OstreeRepo **out_repo,
GCancellable *cancellable,
GError **error);
OstreeBootloader *ostree_sysroot_query_bootloader (OstreeSysroot *sysroot);
G_END_DECLS