/* * libosinfo: An installation media for a (guest) OS * * Copyright (C) 2009-2020 Red Hat, Inc. * * 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.1 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, see * . */ #include #include "osinfo_media_private.h" #include #include #include #include #include #include "osinfo_util_private.h" #define MAX_VOLUME 32 #define MAX_SYSTEM 32 #define MAX_PUBLISHER 128 #define MAX_APPLICATION 128 #define PVD_OFFSET 0x00008000 #define BOOTABLE_TAG "EL TORITO SPECIFICATION" #define PPC_BOOTINFO "/ppc/bootinfo.txt" enum { DIRECTORY_RECORD_FLAG_EXISTENCE = 1 << 0, DIRECTORY_RECORD_FLAG_DIRECTORY = 1 << 1, DIRECTORY_RECORD_FLAG_ASSOCIATED_FILE = 1 << 2, DIRECTORY_RECORD_FLAG_RECORD = 1 << 3, DIRECTORY_RECORD_FLAG_PROTECTION = 1 << 4, DIRECTORY_RECORD_FLAG_RESERVED5 = 1 << 5, DIRECTORY_RECORD_FLAG_RESERVED6 = 1 << 6, DIRECTORY_RECORD_FLAG_MULTIEXTENT = 1 << 7 }; typedef struct _DirectoryRecord DirectoryRecord; struct __attribute__((packed)) _DirectoryRecord { guint8 length; guint8 ignored; guint32 extent_location[2]; guint32 extent_size[2]; guint8 ignored2[7]; guint8 flags; guint8 ignored3[6]; guint8 filename_length; gchar filename[1]; }; G_STATIC_ASSERT(sizeof(struct _DirectoryRecord) == 34); typedef struct _PrimaryVolumeDescriptor PrimaryVolumeDescriptor; struct _PrimaryVolumeDescriptor { guint8 ignored[8]; gchar system[MAX_SYSTEM]; /* System ID */ gchar volume[MAX_VOLUME]; /* Volume ID */ guint8 ignored2[8]; guint32 volume_space_size[2]; guint8 ignored3[40]; guint16 logical_blk_size[2]; guint8 ignored4[24]; guint8 root_directory_entry[33]; guint8 ignored5[129]; gchar publisher[MAX_PUBLISHER]; /* Publisher ID */ guint8 ignored6[128]; gchar application[MAX_APPLICATION]; /* Application ID */ guint8 ignored7[1346]; }; /* the PrimaryVolumeDescriptor struct must exactly 2048 bytes long * since we expect the supplementary volume descriptor to be right * after it. */ G_STATIC_ASSERT(sizeof(struct _PrimaryVolumeDescriptor) == 2048); typedef struct _SupplementaryVolumeDescriptor SupplementaryVolumeDescriptor; struct _SupplementaryVolumeDescriptor { guint8 ignored[7]; gchar system[MAX_SYSTEM]; /* System ID */ }; typedef struct _SearchPPCBootinfoAsyncData SearchPPCBootinfoAsyncData; struct _SearchPPCBootinfoAsyncData { GTask *res; PrimaryVolumeDescriptor *pvd; guint8 *extent; gchar **filepath; gsize filepath_index; gsize filepath_index_max; gsize offset; gsize length; }; static void search_ppc_bootinfo_async_data_free(SearchPPCBootinfoAsyncData *data) { g_object_unref(data->res); g_strfreev(data->filepath); g_free(data->extent); g_slice_free(SearchPPCBootinfoAsyncData, data); } typedef struct _CreateFromLocationAsyncData CreateFromLocationAsyncData; struct _CreateFromLocationAsyncData { GFile *file; SoupSession *session; SoupMessage *message; gchar *uri; GTask *res; PrimaryVolumeDescriptor pvd; SupplementaryVolumeDescriptor svd; gsize offset; gsize length; gchar *volume; gchar *system; gchar *application; gchar *publisher; guint flags; gboolean bootable; }; static void create_from_location_async_data_free (CreateFromLocationAsyncData *data) { if (data->file != NULL) g_object_unref(data->file); if (data->session != NULL) g_object_unref(data->session); if (data->message != NULL) g_object_unref(data->message); g_object_unref(data->res); g_free(data->volume); g_free(data->system); g_free(data->application); g_free(data->publisher); g_slice_free(CreateFromLocationAsyncData, data); } typedef struct _CreateFromLocationData CreateFromLocationData; struct _CreateFromLocationData { GMainLoop *main_loop; GAsyncResult *res; }; static void create_from_location_data_free(CreateFromLocationData *data) { g_object_unref(data->res); g_main_loop_unref(data->main_loop); g_slice_free(CreateFromLocationData, data); } GQuark osinfo_media_error_quark(void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string("osinfo-media-error"); return quark; } /** * SECTION:osinfo_media * @short_description: An installation media for a (guest) OS * @see_also: #OsinfoOs * * #OsinfoMedia is an entity representing an installation media * a (guest) operating system. */ struct _OsinfoMediaPrivate { GWeakRef os; OsinfoInstallScriptList *scripts; }; G_DEFINE_TYPE_WITH_PRIVATE(OsinfoMedia, osinfo_media, OSINFO_TYPE_ENTITY); enum { PROP_0, PROP_ARCHITECTURE, PROP_URL, PROP_VOLUME_ID, PROP_PUBLISHER_ID, PROP_APPLICATION_ID, PROP_SYSTEM_ID, PROP_KERNEL_PATH, PROP_INITRD_PATH, PROP_INSTALLER, PROP_LIVE, PROP_INSTALLER_REBOOTS, PROP_OS, PROP_LANGUAGES, PROP_VOLUME_SIZE, PROP_EJECT_AFTER_INSTALL, PROP_INSTALLER_SCRIPT, LAST_PROP }; static GParamSpec *properties[LAST_PROP]; static void osinfo_media_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { OsinfoMedia *media = OSINFO_MEDIA(object); switch (property_id) { case PROP_ARCHITECTURE: g_value_set_string(value, osinfo_media_get_architecture(media)); break; case PROP_URL: g_value_set_string(value, osinfo_media_get_url(media)); break; case PROP_VOLUME_ID: g_value_set_string(value, osinfo_media_get_volume_id(media)); break; case PROP_PUBLISHER_ID: g_value_set_string(value, osinfo_media_get_publisher_id(media)); break; case PROP_APPLICATION_ID: g_value_set_string(value, osinfo_media_get_application_id(media)); break; case PROP_SYSTEM_ID: g_value_set_string(value, osinfo_media_get_system_id(media)); break; case PROP_KERNEL_PATH: g_value_set_string(value, osinfo_media_get_kernel_path(media)); break; case PROP_INITRD_PATH: g_value_set_string(value, osinfo_media_get_initrd_path(media)); break; case PROP_INSTALLER: g_value_set_boolean(value, osinfo_media_get_installer(media)); break; case PROP_LIVE: g_value_set_boolean(value, osinfo_media_get_live(media)); break; case PROP_INSTALLER_REBOOTS: g_value_set_int(value, osinfo_media_get_installer_reboots(media)); break; case PROP_OS: g_value_take_object(value, osinfo_media_get_os(media)); break; case PROP_LANGUAGES: g_value_set_pointer(value, osinfo_media_get_languages(media)); break; case PROP_VOLUME_SIZE: g_value_set_int64(value, osinfo_media_get_volume_size(media)); break; case PROP_EJECT_AFTER_INSTALL: g_value_set_boolean(value, osinfo_media_get_eject_after_install(media)); break; case PROP_INSTALLER_SCRIPT: g_value_set_boolean(value, osinfo_media_supports_installer_script(media)); break; default: /* We don't have any other property... */ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void osinfo_media_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { OsinfoMedia *media = OSINFO_MEDIA(object); switch (property_id) { case PROP_ARCHITECTURE: osinfo_entity_set_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_ARCHITECTURE, g_value_get_string(value)); break; case PROP_URL: osinfo_entity_set_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_URL, g_value_get_string(value)); break; case PROP_VOLUME_ID: osinfo_entity_set_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_VOLUME_ID, g_value_get_string(value)); break; case PROP_PUBLISHER_ID: osinfo_entity_set_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_PUBLISHER_ID, g_value_get_string(value)); break; case PROP_APPLICATION_ID: osinfo_entity_set_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_APPLICATION_ID, g_value_get_string(value)); break; case PROP_SYSTEM_ID: osinfo_entity_set_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_SYSTEM_ID, g_value_get_string(value)); break; case PROP_KERNEL_PATH: osinfo_entity_set_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_KERNEL, g_value_get_string(value)); break; case PROP_INITRD_PATH: osinfo_entity_set_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_INITRD, g_value_get_string(value)); break; case PROP_LIVE: osinfo_entity_set_param_boolean(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_LIVE, g_value_get_boolean(value)); break; case PROP_INSTALLER: osinfo_entity_set_param_boolean(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_INSTALLER, g_value_get_boolean(value)); break; case PROP_INSTALLER_REBOOTS: osinfo_entity_set_param_int64(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_INSTALLER_REBOOTS, g_value_get_int(value)); break; case PROP_OS: osinfo_media_set_os(media, g_value_get_object(value)); break; case PROP_LANGUAGES: osinfo_media_set_languages(media, g_value_get_pointer(value)); break; case PROP_VOLUME_SIZE: osinfo_entity_set_param_int64(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_VOLUME_SIZE, g_value_get_int64(value)); break; case PROP_EJECT_AFTER_INSTALL: osinfo_entity_set_param_boolean(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_EJECT_AFTER_INSTALL, g_value_get_boolean(value)); break; case PROP_INSTALLER_SCRIPT: osinfo_entity_set_param_boolean(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_INSTALLER_SCRIPT, g_value_get_boolean(value)); break; default: /* We don't have any other property... */ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void osinfo_media_finalize(GObject *object) { OsinfoMedia *media = OSINFO_MEDIA(object); g_object_unref(media->priv->scripts); /* Chain up to the parent class */ G_OBJECT_CLASS(osinfo_media_parent_class)->finalize(object); } static void osinfo_media_dispose(GObject *obj) { OsinfoMedia *media = OSINFO_MEDIA(obj); g_weak_ref_clear(&media->priv->os); G_OBJECT_CLASS(osinfo_media_parent_class)->dispose(obj); } /* Init functions */ static void osinfo_media_class_init(OsinfoMediaClass *klass) { GObjectClass *g_klass = G_OBJECT_CLASS(klass); g_klass->dispose = osinfo_media_dispose; g_klass->finalize = osinfo_media_finalize; g_klass->get_property = osinfo_media_get_property; g_klass->set_property = osinfo_media_set_property; /** * OsinfoMedia:architecture: * * The target hardware architecture of this media. */ properties[PROP_ARCHITECTURE] = g_param_spec_string("architecture", "ARCHITECTURE", _("CPU Architecture"), NULL /* default value */, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * OsinfoMedia:url: * * The URL to this media. */ properties[PROP_URL] = g_param_spec_string("url", "URL", _("The URL to this media"), NULL /* default value */, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * OsinfoMedia:volume-id: * * Expected volume ID (regular expression) for ISO9660 image/device. */ properties[PROP_VOLUME_ID] = g_param_spec_string("volume-id", "VolumeID", _("The expected ISO9660 volume ID"), NULL /* default value */, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * OsinfoMedia:publisher-id: * * Expected publisher ID (regular expression) for ISO9660 image/device. */ properties[PROP_PUBLISHER_ID] = g_param_spec_string("publisher-id", "PublisherID", _("The expected ISO9660 publisher ID"), NULL /* default value */, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * OsinfoMedia:application-id: * * Expected application ID (regular expression) for ISO9660 image/device. */ properties[PROP_APPLICATION_ID] = g_param_spec_string("application-id", "ApplicationID", _("The expected ISO9660 application ID"), NULL /* default value */, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * OsinfoMedia:system-id: * * Expected system ID (regular expression) for ISO9660 image/device. */ properties[PROP_SYSTEM_ID] = g_param_spec_string("system-id", "SystemID", _("The expected ISO9660 system ID"), NULL /* default value */, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * OsinfoMedia:kernel-path: * * The path to the kernel image in the install tree. */ properties[PROP_KERNEL_PATH] = g_param_spec_string("kernel-path", "KernelPath", _("The path to the kernel image"), NULL /* default value */, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * OsinfoMedia:initrd-path: * * The path to the initrd image in the install tree. */ properties[PROP_INITRD_PATH] = g_param_spec_string("initrd-path", "InitrdPath", _("The path to the initrd image"), NULL /* default value */, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * OsinfoMedia:installer: * * Whether media provides an installer for an OS. */ properties[PROP_INSTALLER] = g_param_spec_boolean("installer", "Installer", _("Media provides an installer"), TRUE /* default value */, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * OsinfoMedia:live: * * Whether media can boot directly an OS without any installations. */ properties[PROP_LIVE] = g_param_spec_boolean("live", "Live", _("Media can boot directly w/o installation"), FALSE /* default value */, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * OsinfoMedia:installer-reboots: * * If media is an installer, this property indicates the number of reboots * the installer takes before installation is complete. * * This property is not applicable to media that has no installer. You can * use #osinfo_media_get_installer(or OsinfoMedia::installer) to check * that. * * Warning: Some media allow you to install from live sessions, in which * case number of reboots *alone* is not a reliable method for tracking * installation. */ properties[PROP_INSTALLER_REBOOTS] = g_param_spec_int("installer-reboots", "InstallerReboots", _("Number of installer reboots"), G_MININT, G_MAXINT, 1 /* default value */, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * OsinfoMedia:os: * * Os information for the current media. For media stored in an * #OsinfoDb, it will be filled when the database is loaded, otherwise * the property will be filled after a successful call to * osinfo_db_identify_media(). */ properties[PROP_OS] = g_param_spec_object("os", "Os", _("Information about the operating system on this media"), OSINFO_TYPE_OS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * OsinfoMedia:languages: (type GLib.List(utf8)) (transfer container): * * If media is an installer, this property indicates the languages that * can be used during automatic installations. * * On media that are not installers, this property will indicate the * languages that the user interface can be displayed in. * Use #osinfo_media_get_installer(or OsinfoMedia::installer) to know * if the media is an installer or not. */ properties[PROP_LANGUAGES] = g_param_spec_pointer("languages", "Languages", _("Supported languages"), G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * OsinfoMedia:volume-size: * * Expected volume size, in bytes for ISO9660 image/device. */ properties[PROP_VOLUME_SIZE] = g_param_spec_int64("volume-size", "VolumeSize", _("Expected ISO9660 volume size, in bytes"), G_MININT, G_MAXINT64, -1 /* default value */, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * OsinfoMedia:eject-after-install: * * Whether the media should be ejected after the installation process. * * Some distros need their media to not be ejected after the final reboot * during its installation process as some packages are installed after the * reboot (which may cause the media to be ejected, depending on the * application). */ properties[PROP_EJECT_AFTER_INSTALL] = g_param_spec_boolean("eject-after-install", "EjectAfterInstall", _("Whether the media should be ejected after the installation process"), TRUE /* default value */, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * OsinfoMedia:installer-script: * * Whether the media supports installation via an install-script. * * Some distros provide a few different medias and not all the medias support * installation via an install script. */ properties[PROP_INSTALLER_SCRIPT] = g_param_spec_boolean("installer-script", "InstallerScript", _("Whether the media should be used for an installation using install scripts"), TRUE /* default value */, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties(g_klass, LAST_PROP, properties); } static void osinfo_media_init(OsinfoMedia *media) { media->priv = osinfo_media_get_instance_private(media); g_weak_ref_init(&media->priv->os, NULL); media->priv->scripts = osinfo_install_scriptlist_new(); } OsinfoMedia *osinfo_media_new(const gchar *id, const gchar *architecture) { OsinfoMedia *media; media = g_object_new(OSINFO_TYPE_MEDIA, "id", id, NULL); if (architecture) osinfo_entity_set_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_ARCHITECTURE, architecture); return media; } static void on_media_create_from_location_ready(GObject *source_object, GAsyncResult *res, gpointer user_data) { CreateFromLocationData *data = (CreateFromLocationData *)user_data; data->res = g_object_ref(res); g_main_loop_quit(data->main_loop); } /** * osinfo_media_create_from_location: * @location: the location of an installation media * @cancellable: (allow-none): a #GCancellable, or %NULL * @error: The location where to store any error, or %NULL * * Creates a new #OsinfoMedia for installation media at @location. The @location * could be a http:// or a https:// URI or a local path. * * NOTE: Currently this only works for ISO images/devices. * * Returns: (transfer full): a new #OsinfoMedia , or NULL on error */ OsinfoMedia *osinfo_media_create_from_location(const gchar *location, GCancellable *cancellable, GError **error) { return osinfo_media_create_from_location_with_flags(location, cancellable, OSINFO_MEDIA_DETECT_REQUIRE_BOOTABLE, error); } /** * osinfo_media_create_from_location_with_flags: * @location: the location of an installation media * @cancellable: (allow-none): a #GCancellable, or %NULL * @error: The location where to store any error, or %NULL * @flags: An #OsinfoMediaDetectFlag, or 0. * * Creates a new #OsinfoMedia for installation media at @location. The @location * could be a http:// or a https:// URI or a local path. * * NOTE: Currently this only works for ISO images/devices. * * Returns: (transfer full): a new #OsinfoMedia , or NULL on error * * Since: 1.6.0 */ OsinfoMedia *osinfo_media_create_from_location_with_flags(const gchar *location, GCancellable *cancellable, guint flags, GError **error) { CreateFromLocationData *data; OsinfoMedia *ret; data = g_slice_new0(CreateFromLocationData); data->main_loop = g_main_loop_new(g_main_context_get_thread_default(), FALSE); osinfo_media_create_from_location_with_flags_async(location, G_PRIORITY_DEFAULT, cancellable, on_media_create_from_location_ready, flags, data); /* Loop till we get a reply (or time out) */ g_main_loop_run(data->main_loop); ret = osinfo_media_create_from_location_with_flags_finish(data->res, error); create_from_location_data_free(data); return ret; } static gboolean is_str_empty(const gchar *str) { guint8 i; gboolean ret = TRUE; if ((str == NULL) || (*str == 0)) return TRUE; for (i = 0; i < strlen(str); i++) if (!g_ascii_isspace(str[i])) { ret = FALSE; break; } return ret; } static void set_non_bootable_media_error(GError **error) { g_set_error(error, OSINFO_MEDIA_ERROR, OSINFO_MEDIA_ERROR_NOT_BOOTABLE, _("Install media is not bootable")); } static OsinfoMedia * create_from_location_async_data(CreateFromLocationAsyncData *data) { OsinfoMedia *media; guint64 vol_size; guint8 index; media = g_object_new(OSINFO_TYPE_MEDIA, "id", data->uri, NULL); osinfo_entity_set_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_URL, data->uri); if (!is_str_empty(data->volume)) osinfo_entity_set_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_VOLUME_ID, data->volume); if (!is_str_empty(data->system)) osinfo_entity_set_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_SYSTEM_ID, data->system); if (!is_str_empty(data->publisher)) osinfo_entity_set_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_PUBLISHER_ID, data->publisher); if (!is_str_empty(data->application)) osinfo_entity_set_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_APPLICATION_ID, data->application); index = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? 0 : 1; vol_size = ((gint64) data->pvd.volume_space_size[index]) * data->pvd.logical_blk_size[index]; osinfo_entity_set_param_int64(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_VOLUME_SIZE, vol_size); osinfo_entity_set_param_boolean(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_BOOTABLE, data->bootable); return media; } static gboolean check_directory_record_entry_flags(guint8 flags, gboolean is_dir) { if (is_dir) return (flags & DIRECTORY_RECORD_FLAG_DIRECTORY) != 0; return (flags & DIRECTORY_RECORD_FLAG_DIRECTORY) == 0; } static void on_directory_record_extent_read(GObject *source, GAsyncResult *res, gpointer user_data) { GInputStream *stream = G_INPUT_STREAM(source); SearchPPCBootinfoAsyncData *data; DirectoryRecord *dr; gsize offset; gssize ret; gboolean is_dir; guint8 index = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? 0 : 1; GError *error = NULL; data = (SearchPPCBootinfoAsyncData *)user_data; ret = g_input_stream_read_finish(stream, res, &error); if (ret < 0) { g_prefix_error(&error, _("Failed to read \"%s\" directory record extent: "), data->filepath[data->filepath_index]); goto cleanup; } if (ret == 0) { g_set_error(&error, OSINFO_MEDIA_ERROR, OSINFO_MEDIA_ERROR_NO_DIRECTORY_RECORD_EXTENT, _("No \"%s\" directory record extent"), data->filepath[data->filepath_index]); goto cleanup; } data->offset += ret; if (data->offset < data->length) { g_input_stream_read_async(stream, ((gchar *)data->extent + data->offset), data->length - data->offset, g_task_get_priority(data->res), g_task_get_cancellable(data->res), on_directory_record_extent_read, data); return; } is_dir = data->filepath_index < data->filepath_index_max - 1; offset = 0; do { gboolean check; dr = (DirectoryRecord *)&data->extent[offset]; if (dr->length == 0) { offset++; continue; } check = check_directory_record_entry_flags(dr->flags, is_dir); if (check && g_ascii_strncasecmp(data->filepath[data->filepath_index], dr->filename, strlen(data->filepath[data->filepath_index])) == 0) { data->filepath_index++; break; } offset += dr->length; } while (offset < data->length); if (offset >= data->length) { set_non_bootable_media_error(&error); goto cleanup; } /* It just means that we walked through all the filepath entries and we * found the file we're looking for! Just return TRUE! */ if (data->filepath_index == data->filepath_index_max) goto cleanup; if (!G_IS_SEEKABLE(stream) || !g_seekable_seek(G_SEEKABLE(stream), dr->extent_location[index] * data->pvd->logical_blk_size[index], G_SEEK_SET, g_task_get_cancellable(data->res), &error)) goto cleanup; data->offset = 0; data->length = dr->extent_size[index]; g_free(data->extent); data->extent = g_malloc0(data->length); g_input_stream_read_async(stream, data->extent, data->length, g_task_get_priority(data->res), g_task_get_cancellable(data->res), on_directory_record_extent_read, data); return; cleanup: if (error != NULL) g_task_return_error(data->res, error); else g_task_return_boolean(data->res, TRUE); search_ppc_bootinfo_async_data_free(data); } static void search_ppc_bootinfo_async(GInputStream *stream, PrimaryVolumeDescriptor *pvd, gint priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { SearchPPCBootinfoAsyncData *data; DirectoryRecord *root_directory_entry = NULL; GError *error = NULL; guint8 index = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? 0 : 1; g_return_if_fail(G_IS_INPUT_STREAM(stream)); g_return_if_fail(pvd != NULL); data = g_slice_new0(SearchPPCBootinfoAsyncData); data->pvd = pvd; data->res = g_task_new(stream, cancellable, callback, user_data); g_task_set_priority(data->res, priority); root_directory_entry = (DirectoryRecord *)&data->pvd->root_directory_entry; if (!G_IS_SEEKABLE(stream) || !g_seekable_seek(G_SEEKABLE(stream), root_directory_entry->extent_location[index] * data->pvd->logical_blk_size[index], G_SEEK_SET, g_task_get_cancellable(data->res), &error)) goto cleanup; data->offset = 0; data->length = root_directory_entry->extent_size[index]; data->extent = g_malloc0(root_directory_entry->extent_size[index]); data->filepath = g_strsplit(PPC_BOOTINFO, "/", -1); /* As the path starts with "/", we can just ignore the first element of the * split entry. */ data->filepath_index = 1; data->filepath_index_max = g_strv_length(data->filepath); g_input_stream_read_async(stream, data->extent, data->length, g_task_get_priority(data->res), g_task_get_cancellable(data->res), on_directory_record_extent_read, data); return; cleanup: g_task_return_error(data->res, error); search_ppc_bootinfo_async_data_free(data); } static gboolean search_ppc_bootinfo_finish(GAsyncResult *res, GError **error) { GTask *task = G_TASK(res); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); return g_task_propagate_boolean(task, error); } static void search_ppc_bootinfo_callback(GObject *source, GAsyncResult *res, gpointer user_data) { OsinfoMedia *media = NULL; GInputStream *stream = G_INPUT_STREAM(source); GError *error = NULL; CreateFromLocationAsyncData *data; gboolean ret; data = (CreateFromLocationAsyncData *)user_data; data->bootable = TRUE; ret = search_ppc_bootinfo_finish(res, &error); if (!ret) { if (g_error_matches(error, OSINFO_MEDIA_ERROR, OSINFO_MEDIA_ERROR_NOT_BOOTABLE)) { if ((data->flags & OSINFO_MEDIA_DETECT_REQUIRE_BOOTABLE) != 0) { goto cleanup; } else { g_clear_error(&error); data->bootable = FALSE; } } } media = create_from_location_async_data(data); cleanup: if (error != NULL) g_task_return_error(data->res, error); else g_task_return_pointer(data->res, media, g_object_unref); g_object_unref(stream); create_from_location_async_data_free(data); } static void on_svd_read(GObject *source, GAsyncResult *res, gpointer user_data) { OsinfoMedia *media = NULL; GInputStream *stream = G_INPUT_STREAM(source); GError *error = NULL; CreateFromLocationAsyncData *data; gssize ret; data = (CreateFromLocationAsyncData *)user_data; ret = g_input_stream_read_finish(stream, res, &error); if (ret < 0) { g_prefix_error(&error, _("Failed to read supplementary volume descriptor: ")); goto cleanup; } if (ret == 0) { g_set_error(&error, OSINFO_MEDIA_ERROR, OSINFO_MEDIA_ERROR_NO_SVD, _("Supplementary volume descriptor was truncated")); goto cleanup; } data->offset += ret; if (data->offset < data->length) { g_input_stream_read_async(stream, ((gchar *)&data->svd + data->offset), data->length - data->offset, g_task_get_priority(data->res), g_task_get_cancellable(data->res), on_svd_read, data); return; } data->svd.system[MAX_SYSTEM - 1] = 0; g_strchomp(data->svd.system); if (strncmp(BOOTABLE_TAG, data->svd.system, sizeof(BOOTABLE_TAG)) != 0) { /* * In case we reached this point, there are basically 2 alternatives: * - the media is a PPC media and we should check for the existence of * "/ppc/bootinfo.txt" file * - the media is not bootable. * * Let's check for the existence of the "/ppc/bootinfo.txt" file and, * only after that, return whether the media is bootable or not. */ search_ppc_bootinfo_async(stream, &data->pvd, g_task_get_priority(data->res), g_task_get_cancellable(data->res), search_ppc_bootinfo_callback, data); return; } data->bootable = TRUE; media = create_from_location_async_data(data); cleanup: if (error != NULL) g_task_return_error(data->res, error); else g_task_return_pointer(data->res, media, g_object_unref); g_object_unref(stream); create_from_location_async_data_free(data); } static void on_pvd_read(GObject *source, GAsyncResult *res, gpointer user_data) { GInputStream *stream = G_INPUT_STREAM(source); CreateFromLocationAsyncData *data; GError *error = NULL; gssize ret; data = (CreateFromLocationAsyncData *)user_data; ret = g_input_stream_read_finish(stream, res, &error); if (ret < 0) { g_prefix_error(&error, _("Failed to read primary volume descriptor: ")); goto error; } if (ret == 0) { g_set_error(&error, OSINFO_MEDIA_ERROR, OSINFO_MEDIA_ERROR_NO_PVD, _("Primary volume descriptor was truncated")); goto error; } data->offset += ret; if (data->offset < data->length) { g_input_stream_read_async(stream, ((gchar*)&data->pvd) + data->offset, data->length - data->offset, g_task_get_priority(data->res), g_task_get_cancellable(data->res), on_pvd_read, data); return; } data->volume = g_strndup(data->pvd.volume, MAX_VOLUME); g_strchomp(data->volume); data->system = g_strndup(data->pvd.system, MAX_SYSTEM); g_strchomp(data->system); data->publisher = g_strndup(data->pvd.publisher, MAX_PUBLISHER); g_strchomp(data->publisher); data->application = g_strndup(data->pvd.application, MAX_APPLICATION); g_strchomp(data->application); if (is_str_empty(data->volume)) { g_set_error(&error, OSINFO_MEDIA_ERROR, OSINFO_MEDIA_ERROR_INSUFFICIENT_METADATA, _("Insufficient metadata on installation media")); goto error; } data->offset = 0; data->length = sizeof(data->svd); g_input_stream_read_async(stream, (gchar *)&data->svd, data->length, g_task_get_priority(data->res), g_task_get_cancellable(data->res), on_svd_read, data); return; error: g_object_unref(stream); g_task_return_error(data->res, error); create_from_location_async_data_free(data); } static void on_location_skipped(GObject *source, GAsyncResult *res, gpointer user_data) { GInputStream *stream = G_INPUT_STREAM(source); CreateFromLocationAsyncData *data; GError *error = NULL; data = (CreateFromLocationAsyncData *)user_data; if (g_input_stream_skip_finish(stream, res, &error) < PVD_OFFSET) { if (error) g_prefix_error(&error, _("Failed to skip %d bytes"), PVD_OFFSET); else g_set_error(&error, OSINFO_MEDIA_ERROR, OSINFO_MEDIA_ERROR_NO_DESCRIPTORS, _("No volume descriptors")); g_object_unref(stream); g_task_return_error(data->res, error); create_from_location_async_data_free(data); return; } data->offset = 0; data->length = sizeof(data->pvd); g_input_stream_read_async(stream, (gchar *)&data->pvd, data->length, g_task_get_priority(data->res), g_task_get_cancellable(data->res), on_pvd_read, data); } static void on_location_read(GObject *source, GAsyncResult *res, gpointer user_data) { GInputStream *stream; CreateFromLocationAsyncData *data; GError *error = NULL; data = (CreateFromLocationAsyncData *)user_data; if (data->file != NULL) { stream = G_INPUT_STREAM(g_file_read_finish(G_FILE(source), res, &error)); } else { stream = soup_session_send_finish(SOUP_SESSION(source), res, &error); if (!SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(data->message)) && error == NULL) { g_set_error_literal(&error, OSINFO_MEDIA_ERROR, OSINFO_MEDIA_ERROR_NO_DESCRIPTORS, soup_status_get_phrase(soup_message_get_status(data->message))); } } if (error != NULL) { g_prefix_error(&error, _("Failed to open file: ")); g_task_return_error(data->res, error); create_from_location_async_data_free(data); return; } g_input_stream_skip_async(stream, PVD_OFFSET, g_task_get_priority(data->res), g_task_get_cancellable(data->res), on_location_skipped, data); } /** * osinfo_media_create_from_location_async: * @location: the location of an installation media * @priority: the I/O priority of the request * @cancellable: (allow-none): a #GCancellable, or %NULL * @callback: Function to call when result of this call is ready * @user_data: The user data to pass to @callback, or %NULL * * Asynchronous variant of #osinfo_media_create_from_location. */ void osinfo_media_create_from_location_async(const gchar *location, gint priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { osinfo_media_create_from_location_with_flags_async(location, priority, cancellable, callback, OSINFO_MEDIA_DETECT_REQUIRE_BOOTABLE, user_data); } /** * osinfo_media_create_from_location_finish: * @res: a #GAsyncResult * @error: The location where to store any error, or %NULL * * Finishes an asynchronous media object creation process started with * #osinfo_media_create_from_location_async. * * Returns: (transfer full): a new #OsinfoMedia , or NULL on error * * Since: 1.6.0 */ OsinfoMedia *osinfo_media_create_from_location_finish(GAsyncResult *res, GError **error) { return osinfo_media_create_from_location_with_flags_finish(res, error); } /** * osinfo_media_create_from_location_with_flags_async: * @location: the location of an installation media * @priority: the I/O priority of the request * @cancellable: (allow-none): a #GCancellable, or %NULL * @callback: Function to call when result of this call is ready * @flags: An #OsinfoMediaDetectFlag, or 0. * @user_data: The user data to pass to @callback, or %NULL * * Asynchronous variant of #osinfo_media_create_from_location_with_flags. * * Since: 1.6.0 */ void osinfo_media_create_from_location_with_flags_async(const gchar *location, gint priority, GCancellable *cancellable, GAsyncReadyCallback callback, guint flags, gpointer user_data) { CreateFromLocationAsyncData *data; g_return_if_fail(location != NULL); data = g_slice_new0(CreateFromLocationAsyncData); data->res = g_task_new(NULL, cancellable, callback, user_data); g_task_set_priority(data->res, priority); data->flags = flags; data->uri = g_strdup(location); if (osinfo_util_requires_soup(location)) { data->session = soup_session_new_with_options( "user-agent", "Wget/1.0", NULL); data->message = soup_message_new("GET", location); soup_session_send_async(data->session, data->message, #if SOUP_MAJOR_VERSION > 2 G_PRIORITY_DEFAULT, #endif cancellable, on_location_read, data); } else { data->file = g_file_new_for_commandline_arg(location); g_file_read_async(data->file, priority, cancellable, on_location_read, data); } } /** * osinfo_media_create_from_location_with_flags_finish: * @res: a #GAsyncResult * @error: The location where to store any error, or %NULL * * Finishes an asynchronous media object creation process started with * #osinfo_media_create_from_location_async. * * Returns: (transfer full): a new #OsinfoMedia , or NULL on error */ OsinfoMedia *osinfo_media_create_from_location_with_flags_finish(GAsyncResult *res, GError **error) { GTask *task = G_TASK(res); g_return_val_if_fail(error == NULL || *error == NULL, NULL); return g_task_propagate_pointer(task, error); } /** * osinfo_media_get_architecture: * @media: an #OsinfoMedia instance * * Retrieves the target hardware architecture of the OS @media provides. * * Returns: (transfer none): the hardware architecture, or NULL */ const gchar *osinfo_media_get_architecture(OsinfoMedia *media) { return osinfo_entity_get_param_value(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_ARCHITECTURE); } /** * osinfo_media_get_url: * @media: an #OsinfoMedia instance * * The URL to the @media * * Returns: (transfer none): the URL, or NULL */ const gchar *osinfo_media_get_url(OsinfoMedia *media) { return osinfo_entity_get_param_value(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_URL); } /** * osinfo_media_get_volume_id: * @media: an #OsinfoMedia instance * * If @media is an ISO9660 image/device, this function retrieves the expected * volume ID. * * Note: In practice, this will usually not be the exact copy of the volume ID * string on the ISO image/device but rather a regular expression that matches * it. * * Returns: (transfer none): the volume id, or NULL */ const gchar *osinfo_media_get_volume_id(OsinfoMedia *media) { return osinfo_entity_get_param_value(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_VOLUME_ID); } /** * osinfo_media_get_system_id: * @media: an #OsinfoMedia instance * * If @media is an ISO9660 image/device, this function retrieves the expected * system ID. * * Note: In practice, this will usually not be the exact copy of the system ID * string on the ISO image/device but rather a regular expression that matches * it. * * Returns: (transfer none): the system id, or NULL */ const gchar *osinfo_media_get_system_id(OsinfoMedia *media) { return osinfo_entity_get_param_value(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_SYSTEM_ID); } /** * osinfo_media_get_publisher_id: * @media: an #OsinfoMedia instance * * If @media is an ISO9660 image/device, this function retrieves the expected * publisher ID. * * Note: In practice, this will usually not be the exact copy of the publisher * ID string on the ISO image/device but rather a regular expression that * matches it. * * Returns: (transfer none): the publisher id, or NULL */ const gchar *osinfo_media_get_publisher_id(OsinfoMedia *media) { return osinfo_entity_get_param_value(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_PUBLISHER_ID); } /** * osinfo_media_get_application_id: * @media: an #OsinfoMedia instance * * If @media is an ISO9660 image/device, this function retrieves the expected * application ID. * * Note: In practice, this will usually not be the exact copy of the application * ID string on the ISO image/device but rather a regular expression that * matches it. * * Returns: (transfer none): the application id, or NULL */ const gchar *osinfo_media_get_application_id(OsinfoMedia *media) { return osinfo_entity_get_param_value(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_APPLICATION_ID); } /** * osinfo_media_get_kernel_path: * @media: an #OsinfoMedia instance * * Retrieves the path to the kernel image in the install tree. * * Note: This only applies to installer medias of 'linux' OS family. * * Returns: (transfer none): the path to kernel image, or NULL */ const gchar *osinfo_media_get_kernel_path(OsinfoMedia *media) { return osinfo_entity_get_param_value(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_KERNEL); } /** * osinfo_media_get_initrd_path: * @media: an #OsinfoMedia instance * * Retrieves the path to the initrd image in the install tree. * * Note: This only applies to installer medias of 'linux' OS family. * * Returns: (transfer none): the path to initrd image, or NULL */ const gchar *osinfo_media_get_initrd_path(OsinfoMedia *media) { return osinfo_entity_get_param_value(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_INITRD); } /** * osinfo_media_get_installer: * @media: an #OsinfoMedia instance * * Whether @media provides an installer for an OS. * * Returns: #TRUE if media is installer, #FALSE otherwise * * Since: 0.0.3 */ gboolean osinfo_media_get_installer(OsinfoMedia *media) { return osinfo_entity_get_param_value_boolean_with_default (OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_INSTALLER, TRUE); } /** * osinfo_media_get_live: * @media: an #OsinfoMedia instance * * Whether @media can boot directly an OS without any installations. * * Returns: #TRUE if media is live, #FALSE otherwise * * Since: 0.0.3 */ gboolean osinfo_media_get_live(OsinfoMedia *media) { return osinfo_entity_get_param_value_boolean_with_default (OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_LIVE, FALSE); } /** * osinfo_media_get_installer_reboots: * @media: an #OsinfoMedia instance * * If media is an installer, this method retrieves the number of reboots the * installer takes before installation is complete. * * This function is not supposed to be called on media that has no installer. * You can use #osinfo_media_get_installer(or OsinfoMedia::installer) to check * that. * * Warning: Some media allow you to install from live sessions, in which case * number of reboots *alone* is not a reliable method for tracking installation. * * Returns: the number of installer reboots or -1 if media is not an installer * * Since: 0.2.1 */ gint osinfo_media_get_installer_reboots(OsinfoMedia *media) { g_return_val_if_fail(OSINFO_IS_MEDIA(media), -1); g_return_val_if_fail(osinfo_media_get_installer(media), -1); return (gint) osinfo_entity_get_param_value_int64_with_default (OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_INSTALLER_REBOOTS, 1); } /** * osinfo_media_get_os: * @media: an #OsinfoMedia instance * * Returns: (transfer full): the operating system, or NULL * * Since: 0.2.3 */ OsinfoOs *osinfo_media_get_os(OsinfoMedia *media) { g_return_val_if_fail(OSINFO_IS_MEDIA(media), NULL); return g_weak_ref_get(&media->priv->os); } void osinfo_media_set_os(OsinfoMedia *media, OsinfoOs *os) { g_return_if_fail(OSINFO_IS_MEDIA(media)); g_object_ref(os); g_weak_ref_set(&media->priv->os, os); g_object_unref(os); } /** * osinfo_media_get_os_variants: * @media: an #OsinfoMedia instance * * Gets the variants of the associated operating system. * * Returns: (transfer full): the operating system variant, or NULL * * Since: 0.2.9 */ OsinfoOsVariantList *osinfo_media_get_os_variants(OsinfoMedia *media) { OsinfoOs *os; OsinfoOsVariantList *os_variants; OsinfoOsVariantList *media_variants; GList *ids, *node; OsinfoFilter *filter; g_return_val_if_fail(OSINFO_IS_MEDIA(media), NULL); os = g_weak_ref_get(&media->priv->os); if (os == NULL) return NULL; os_variants = osinfo_os_get_variant_list(os); g_object_unref(os); ids = osinfo_entity_get_param_value_list(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_VARIANT); filter = osinfo_filter_new(); media_variants = osinfo_os_variantlist_new(); for (node = ids; node != NULL; node = node->next) { osinfo_filter_clear_constraints(filter); osinfo_filter_add_constraint(filter, OSINFO_ENTITY_PROP_ID, (const char *) node->data); osinfo_list_add_filtered(OSINFO_LIST(media_variants), OSINFO_LIST(os_variants), filter); } g_object_unref(os_variants); return media_variants; } /** * osinfo_media_get_languages: * @media: an #OsinfoMedia instance * * If media is an installer, this property indicates the languages that * can be used during automatic installations. * * On media that are not installers, this property will indicate the * languages that the user interface can be displayed in. * Use #osinfo_media_get_installer(or OsinfoMedia::installer) to know * if the media is an installer or not. * * Returns: (transfer container) (element-type utf8): a #GList * containing the list of the UI languages this media supports. The list * must be freed with g_list_free() when no longer needed. If the * supported languages are unknown, NULL will be returned. * * Since: 0.2.3 */ GList *osinfo_media_get_languages(OsinfoMedia *media) { g_return_val_if_fail(OSINFO_IS_MEDIA(media), NULL); return osinfo_entity_get_param_value_list(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_LANG); } /** * osinfo_media_set_languages: * @media: an #OsinfoMedia instance * @languages: (element-type utf8): a #GList containing the list of the UI * languages this media supports. * * Sets the #OSINFO_MEDIA_PROP_LANG parameter */ void osinfo_media_set_languages(OsinfoMedia *media, GList *languages) { GList *it; g_return_if_fail(OSINFO_IS_MEDIA(media)); osinfo_entity_clear_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_LANG); for (it = languages; it != NULL; it = it->next) osinfo_entity_add_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_LANG, it->data); } /** * osinfo_media_get_volume_size: * @media: an #OsinfoMedia instance * * Returns: the ISO9660 volume size, in bytes or -1 if size is * unknown or media is not an ISO9660 device/image. */ gint64 osinfo_media_get_volume_size(OsinfoMedia *media) { g_return_val_if_fail(OSINFO_IS_MEDIA(media), -1); return osinfo_entity_get_param_value_int64_with_default (OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_VOLUME_SIZE, -1); } /** * osinfo_media_get_eject_after_install: * @media: an #OsinfoMedia instance * * Whether @media should ejected after the installation procces. * * Returns: #TRUE if media should be ejected, #FALSE otherwise * * Since: 0.2.13 */ gboolean osinfo_media_get_eject_after_install(OsinfoMedia *media) { return osinfo_entity_get_param_value_boolean_with_default (OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_EJECT_AFTER_INSTALL, TRUE); } /** * osinfo_media_supports_installer_script: * @media: an #OsinfoMedia instance * * Whether @media supports installation using install scripts. * * Returns: #TRUE if install-scripts are supported by the media, * #FALSE otherwise * * Since: 1.3.0 */ gboolean osinfo_media_supports_installer_script(OsinfoMedia *media) { OsinfoOs *os; OsinfoInstallScriptList *list; gboolean ret; g_return_val_if_fail(OSINFO_IS_MEDIA(media), FALSE); os = osinfo_media_get_os(media); list = osinfo_os_get_install_script_list(os); if (osinfo_list_get_length(OSINFO_LIST(list)) == 0 && osinfo_list_get_length(OSINFO_LIST(media->priv->scripts)) == 0) { ret = FALSE; goto cleanup; } ret = osinfo_entity_get_param_value_boolean_with_default (OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_INSTALLER_SCRIPT, TRUE); cleanup: g_object_unref(list); g_object_unref(os); return ret; } /** * osinfo_media_add_install_script: * @media: an #OsinfoMedia instance * @script: an #OsinfoInstallScript instance * * Adds an @script to the specified @media * * Since: 1.4.0 */ void osinfo_media_add_install_script(OsinfoMedia *media, OsinfoInstallScript *script) { g_return_if_fail(OSINFO_IS_MEDIA(media)); osinfo_list_add(OSINFO_LIST(media->priv->scripts), OSINFO_ENTITY(script)); } /** * osinfo_media_get_install_script_list: * @media: an #OsinfoMedia instance * * Returns: (transfer full): a list of the install scripts for the specified media * * Since: 1.4.0 */ OsinfoInstallScriptList *osinfo_media_get_install_script_list(OsinfoMedia *media) { OsinfoList *new_list; g_return_val_if_fail(OSINFO_IS_MEDIA(media), NULL); new_list = osinfo_list_new_copy(OSINFO_LIST(media->priv->scripts)); return OSINFO_INSTALL_SCRIPTLIST(new_list); } /** * osinfo_media_is_bootable: * @media: and #OsinfoMedia instance * * Returns: #TRUE if the @media is bootable. #FALSE otherwise. * * Since: 1.6.0 */ gboolean osinfo_media_is_bootable(OsinfoMedia *media) { g_return_val_if_fail(OSINFO_IS_MEDIA(media), FALSE); return osinfo_entity_get_param_value_boolean(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_BOOTABLE); } #define match_regex(pattern, str) \ (((pattern) == NULL) || \ (((str) != NULL) && \ g_regex_match_simple((pattern), (str), 0, 0))) /** * osinfo_media_matches: * @media: an unidentified #OsinfoMedia instance * @reference: a reference #OsinfoMedia instance * * Determines whether the metadata for the unidentified @media is a match * for the @reference media. * * The metadata in the unidentified @media must be literal strings, * while the metadata in the @reference media must be regular expressions. * * Returns: #TRUE if @media is a match for @reference. #FALSE otherwise * * Since: 1.10.0 */ gboolean osinfo_media_matches(OsinfoMedia *media, OsinfoMedia *reference) { const gchar *media_arch = osinfo_media_get_architecture(media); const gchar *media_volume = osinfo_media_get_volume_id(media); const gchar *media_system = osinfo_media_get_system_id(media); const gchar *media_publisher = osinfo_media_get_publisher_id(media); const gchar *media_application = osinfo_media_get_application_id(media); gint64 media_vol_size = osinfo_media_get_volume_size(media); const gchar *reference_arch = osinfo_media_get_architecture(reference); const gchar *reference_volume = osinfo_media_get_volume_id(reference); const gchar *reference_system = osinfo_media_get_system_id(reference); const gchar *reference_publisher = osinfo_media_get_publisher_id(reference); const gchar *reference_application = osinfo_media_get_application_id(reference); gint64 reference_vol_size = osinfo_media_get_volume_size(reference); if (reference_volume == NULL && reference_system == NULL && reference_publisher == NULL && reference_application == NULL && reference_vol_size <= 0) return FALSE; if (reference_vol_size <= 0) reference_vol_size = media_vol_size; if ((!media_arch || g_str_equal(reference_arch, media_arch) || g_str_equal(reference_arch, "all")) && match_regex(reference_volume, media_volume) && match_regex(reference_application, media_application) && match_regex(reference_system, media_system) && match_regex(reference_publisher, media_publisher) && reference_vol_size == media_vol_size) return TRUE; return FALSE; }