1 /*
2  * libosinfo: An installation media for a (guest) OS
3  *
4  * Copyright (C) 2009-2020 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library. If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <osinfo/osinfo.h>
22 #include "osinfo_media_private.h"
23 #include "osinfo_util_private.h"
24 #include <gio/gio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <glib/gi18n-lib.h>
28 #include <libsoup/soup.h>
29 
30 #define MAX_VOLUME 32
31 #define MAX_SYSTEM 32
32 #define MAX_PUBLISHER 128
33 #define MAX_APPLICATION 128
34 
35 #define PVD_OFFSET 0x00008000
36 #define BOOTABLE_TAG "EL TORITO SPECIFICATION"
37 #define PPC_BOOTINFO "/ppc/bootinfo.txt"
38 
39 enum {
40     DIRECTORY_RECORD_FLAG_EXISTENCE       = 1 << 0,
41     DIRECTORY_RECORD_FLAG_DIRECTORY       = 1 << 1,
42     DIRECTORY_RECORD_FLAG_ASSOCIATED_FILE = 1 << 2,
43     DIRECTORY_RECORD_FLAG_RECORD          = 1 << 3,
44     DIRECTORY_RECORD_FLAG_PROTECTION      = 1 << 4,
45     DIRECTORY_RECORD_FLAG_RESERVED5       = 1 << 5,
46     DIRECTORY_RECORD_FLAG_RESERVED6       = 1 << 6,
47     DIRECTORY_RECORD_FLAG_MULTIEXTENT     = 1 << 7
48 };
49 
50 typedef struct _DirectoryRecord DirectoryRecord;
51 
52 struct __attribute__((packed)) _DirectoryRecord {
53     guint8 length;
54     guint8 ignored;
55     guint32 extent_location[2];
56     guint32 extent_size[2];
57     guint8 ignored2[7];
58     guint8 flags;
59     guint8 ignored3[6];
60     guint8 filename_length;
61     gchar filename[1];
62 };
63 
64 G_STATIC_ASSERT(sizeof(struct _DirectoryRecord) == 34);
65 
66 typedef struct _PrimaryVolumeDescriptor PrimaryVolumeDescriptor;
67 
68 struct _PrimaryVolumeDescriptor {
69     guint8 ignored[8];
70     gchar  system[MAX_SYSTEM];       /* System ID */
71     gchar  volume[MAX_VOLUME];       /* Volume ID */
72     guint8 ignored2[8];
73     guint32 volume_space_size[2];
74     guint8 ignored3[40];
75     guint16 logical_blk_size[2];
76     guint8 ignored4[24];
77     guint8 root_directory_entry[33];
78     guint8 ignored5[129];
79     gchar  publisher[MAX_PUBLISHER]; /* Publisher ID */
80     guint8 ignored6[128];
81     gchar  application[MAX_APPLICATION]; /* Application ID */
82     guint8 ignored7[1346];
83 };
84 
85 /* the PrimaryVolumeDescriptor struct must exactly 2048 bytes long
86  * since we expect the supplementary volume descriptor to be right
87  * after it.
88  */
89 G_STATIC_ASSERT(sizeof(struct _PrimaryVolumeDescriptor) == 2048);
90 
91 typedef struct _SupplementaryVolumeDescriptor SupplementaryVolumeDescriptor;
92 
93 struct _SupplementaryVolumeDescriptor {
94     guint8 ignored[7];
95     gchar  system[MAX_SYSTEM]; /* System ID */
96 };
97 
98 typedef struct _SearchPPCBootinfoAsyncData SearchPPCBootinfoAsyncData;
99 struct _SearchPPCBootinfoAsyncData {
100     GTask *res;
101 
102     PrimaryVolumeDescriptor *pvd;
103     guint8 *extent;
104 
105     gchar **filepath;
106     gsize filepath_index;
107     gsize filepath_index_max;
108 
109     gsize offset;
110     gsize length;
111 };
112 
search_ppc_bootinfo_async_data_free(SearchPPCBootinfoAsyncData * data)113 static void search_ppc_bootinfo_async_data_free(SearchPPCBootinfoAsyncData *data)
114 {
115     g_object_unref(data->res);
116 
117     g_strfreev(data->filepath);
118     g_free(data->extent);
119 
120     g_slice_free(SearchPPCBootinfoAsyncData, data);
121 
122 }
123 
124 typedef struct _CreateFromLocationAsyncData CreateFromLocationAsyncData;
125 struct _CreateFromLocationAsyncData {
126     GFile *file;
127     SoupSession *session;
128     SoupMessage *message;
129     gchar *uri;
130 
131     GTask *res;
132 
133     PrimaryVolumeDescriptor pvd;
134     SupplementaryVolumeDescriptor svd;
135 
136     gsize offset;
137     gsize length;
138 
139     gchar *volume;
140     gchar *system;
141     gchar *application;
142     gchar *publisher;
143 
144     guint flags;
145     gboolean bootable;
146 };
147 
create_from_location_async_data_free(CreateFromLocationAsyncData * data)148 static void create_from_location_async_data_free
149                                 (CreateFromLocationAsyncData *data)
150 {
151     if (data->file != NULL)
152         g_object_unref(data->file);
153     if (data->session != NULL)
154         g_object_unref(data->session);
155     if (data->message != NULL)
156         g_object_unref(data->message);
157     g_object_unref(data->res);
158     g_free(data->volume);
159     g_free(data->system);
160     g_free(data->application);
161     g_free(data->publisher);
162 
163     g_slice_free(CreateFromLocationAsyncData, data);
164 }
165 
166 typedef struct _CreateFromLocationData CreateFromLocationData;
167 struct _CreateFromLocationData {
168     GMainLoop *main_loop;
169 
170     GAsyncResult *res;
171 };
172 
create_from_location_data_free(CreateFromLocationData * data)173 static void create_from_location_data_free(CreateFromLocationData *data)
174 {
175    g_object_unref(data->res);
176    g_main_loop_unref(data->main_loop);
177 
178    g_slice_free(CreateFromLocationData, data);
179 }
180 
181 GQuark
osinfo_media_error_quark(void)182 osinfo_media_error_quark(void)
183 {
184     static GQuark quark = 0;
185 
186     if (!quark)
187         quark = g_quark_from_static_string("osinfo-media-error");
188 
189     return quark;
190 }
191 
192 /**
193  * SECTION:osinfo_media
194  * @short_description: An installation media for a (guest) OS
195  * @see_also: #OsinfoOs
196  *
197  * #OsinfoMedia is an entity representing an installation media
198  * a (guest) operating system.
199  */
200 
201 struct _OsinfoMediaPrivate
202 {
203     GWeakRef os;
204     OsinfoInstallScriptList *scripts;
205 };
206 
207 G_DEFINE_TYPE_WITH_PRIVATE(OsinfoMedia, osinfo_media, OSINFO_TYPE_ENTITY);
208 
209 enum {
210     PROP_0,
211 
212     PROP_ARCHITECTURE,
213     PROP_URL,
214     PROP_VOLUME_ID,
215     PROP_PUBLISHER_ID,
216     PROP_APPLICATION_ID,
217     PROP_SYSTEM_ID,
218     PROP_KERNEL_PATH,
219     PROP_INITRD_PATH,
220     PROP_INSTALLER,
221     PROP_LIVE,
222     PROP_INSTALLER_REBOOTS,
223     PROP_OS,
224     PROP_LANGUAGES,
225     PROP_VOLUME_SIZE,
226     PROP_EJECT_AFTER_INSTALL,
227     PROP_INSTALLER_SCRIPT,
228 
229     LAST_PROP
230 };
231 static GParamSpec *properties[LAST_PROP];
232 
233 static void
osinfo_media_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)234 osinfo_media_get_property(GObject    *object,
235                                guint       property_id,
236                                GValue     *value,
237                                GParamSpec *pspec)
238 {
239     OsinfoMedia *media = OSINFO_MEDIA(object);
240 
241     switch (property_id) {
242     case PROP_ARCHITECTURE:
243         g_value_set_string(value,
244                            osinfo_media_get_architecture(media));
245         break;
246 
247     case PROP_URL:
248         g_value_set_string(value,
249                            osinfo_media_get_url(media));
250         break;
251 
252     case PROP_VOLUME_ID:
253         g_value_set_string(value,
254                            osinfo_media_get_volume_id(media));
255         break;
256 
257     case PROP_PUBLISHER_ID:
258         g_value_set_string(value,
259                            osinfo_media_get_publisher_id(media));
260         break;
261 
262     case PROP_APPLICATION_ID:
263         g_value_set_string(value,
264                            osinfo_media_get_application_id(media));
265         break;
266 
267     case PROP_SYSTEM_ID:
268         g_value_set_string(value,
269                            osinfo_media_get_system_id(media));
270         break;
271 
272     case PROP_KERNEL_PATH:
273         g_value_set_string(value,
274                            osinfo_media_get_kernel_path(media));
275         break;
276 
277     case PROP_INITRD_PATH:
278         g_value_set_string(value,
279                            osinfo_media_get_initrd_path(media));
280         break;
281 
282     case PROP_INSTALLER:
283         g_value_set_boolean(value,
284                             osinfo_media_get_installer(media));
285         break;
286 
287     case PROP_LIVE:
288         g_value_set_boolean(value,
289                             osinfo_media_get_live(media));
290         break;
291 
292     case PROP_INSTALLER_REBOOTS:
293         g_value_set_int(value,
294                         osinfo_media_get_installer_reboots(media));
295         break;
296 
297     case PROP_OS:
298         g_value_take_object(value, osinfo_media_get_os(media));
299         break;
300 
301     case PROP_LANGUAGES:
302         g_value_set_pointer(value, osinfo_media_get_languages(media));
303         break;
304 
305     case PROP_VOLUME_SIZE:
306         g_value_set_int64(value,
307                           osinfo_media_get_volume_size(media));
308         break;
309 
310     case PROP_EJECT_AFTER_INSTALL:
311         g_value_set_boolean(value,
312                             osinfo_media_get_eject_after_install(media));
313         break;
314 
315     case PROP_INSTALLER_SCRIPT:
316         g_value_set_boolean(value,
317                             osinfo_media_supports_installer_script(media));
318         break;
319 
320     default:
321         /* We don't have any other property... */
322         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
323         break;
324     }
325 }
326 
327 static void
osinfo_media_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)328 osinfo_media_set_property(GObject      *object,
329                               guint         property_id,
330                               const GValue *value,
331                               GParamSpec   *pspec)
332 {
333     OsinfoMedia *media = OSINFO_MEDIA(object);
334 
335     switch (property_id) {
336     case PROP_ARCHITECTURE:
337         osinfo_entity_set_param(OSINFO_ENTITY(media),
338                                 OSINFO_MEDIA_PROP_ARCHITECTURE,
339                                 g_value_get_string(value));
340         break;
341 
342     case PROP_URL:
343         osinfo_entity_set_param(OSINFO_ENTITY(media),
344                                 OSINFO_MEDIA_PROP_URL,
345                                 g_value_get_string(value));
346         break;
347 
348     case PROP_VOLUME_ID:
349         osinfo_entity_set_param(OSINFO_ENTITY(media),
350                                 OSINFO_MEDIA_PROP_VOLUME_ID,
351                                 g_value_get_string(value));
352         break;
353 
354     case PROP_PUBLISHER_ID:
355         osinfo_entity_set_param(OSINFO_ENTITY(media),
356                                 OSINFO_MEDIA_PROP_PUBLISHER_ID,
357                                 g_value_get_string(value));
358         break;
359 
360     case PROP_APPLICATION_ID:
361         osinfo_entity_set_param(OSINFO_ENTITY(media),
362                                 OSINFO_MEDIA_PROP_APPLICATION_ID,
363                                 g_value_get_string(value));
364         break;
365 
366     case PROP_SYSTEM_ID:
367         osinfo_entity_set_param(OSINFO_ENTITY(media),
368                                 OSINFO_MEDIA_PROP_SYSTEM_ID,
369                                 g_value_get_string(value));
370         break;
371 
372     case PROP_KERNEL_PATH:
373         osinfo_entity_set_param(OSINFO_ENTITY(media),
374                                 OSINFO_MEDIA_PROP_KERNEL,
375                                 g_value_get_string(value));
376         break;
377 
378     case PROP_INITRD_PATH:
379         osinfo_entity_set_param(OSINFO_ENTITY(media),
380                                 OSINFO_MEDIA_PROP_INITRD,
381                                 g_value_get_string(value));
382         break;
383 
384     case PROP_LIVE:
385         osinfo_entity_set_param_boolean(OSINFO_ENTITY(media),
386                                         OSINFO_MEDIA_PROP_LIVE,
387                                         g_value_get_boolean(value));
388         break;
389 
390     case PROP_INSTALLER:
391         osinfo_entity_set_param_boolean(OSINFO_ENTITY(media),
392                                         OSINFO_MEDIA_PROP_INSTALLER,
393                                         g_value_get_boolean(value));
394         break;
395 
396     case PROP_INSTALLER_REBOOTS:
397         osinfo_entity_set_param_int64(OSINFO_ENTITY(media),
398                                       OSINFO_MEDIA_PROP_INSTALLER_REBOOTS,
399                                       g_value_get_int(value));
400         break;
401 
402     case PROP_OS:
403         osinfo_media_set_os(media, g_value_get_object(value));
404         break;
405 
406     case PROP_LANGUAGES:
407         osinfo_media_set_languages(media, g_value_get_pointer(value));
408         break;
409 
410     case PROP_VOLUME_SIZE:
411         osinfo_entity_set_param_int64(OSINFO_ENTITY(media),
412                                       OSINFO_MEDIA_PROP_VOLUME_SIZE,
413                                       g_value_get_int64(value));
414         break;
415 
416     case PROP_EJECT_AFTER_INSTALL:
417         osinfo_entity_set_param_boolean(OSINFO_ENTITY(media),
418                                         OSINFO_MEDIA_PROP_EJECT_AFTER_INSTALL,
419                                         g_value_get_boolean(value));
420         break;
421 
422     case PROP_INSTALLER_SCRIPT:
423         osinfo_entity_set_param_boolean(OSINFO_ENTITY(media),
424                                         OSINFO_MEDIA_PROP_INSTALLER_SCRIPT,
425                                         g_value_get_boolean(value));
426         break;
427 
428     default:
429         /* We don't have any other property... */
430         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
431         break;
432     }
433 }
434 
435 static void
osinfo_media_finalize(GObject * object)436 osinfo_media_finalize(GObject *object)
437 {
438     OsinfoMedia *media = OSINFO_MEDIA(object);
439 
440     g_object_unref(media->priv->scripts);
441 
442     /* Chain up to the parent class */
443     G_OBJECT_CLASS(osinfo_media_parent_class)->finalize(object);
444 }
445 
osinfo_media_dispose(GObject * obj)446 static void osinfo_media_dispose(GObject *obj)
447 {
448     OsinfoMedia *media = OSINFO_MEDIA(obj);
449 
450     g_weak_ref_clear(&media->priv->os);
451 
452     G_OBJECT_CLASS(osinfo_media_parent_class)->dispose(obj);
453 }
454 
455 
456 /* Init functions */
457 static void
osinfo_media_class_init(OsinfoMediaClass * klass)458 osinfo_media_class_init(OsinfoMediaClass *klass)
459 {
460     GObjectClass *g_klass = G_OBJECT_CLASS(klass);
461 
462     g_klass->dispose = osinfo_media_dispose;
463     g_klass->finalize = osinfo_media_finalize;
464     g_klass->get_property = osinfo_media_get_property;
465     g_klass->set_property = osinfo_media_set_property;
466 
467     /**
468      * OsinfoMedia:architecture:
469      *
470      * The target hardware architecture of this media.
471      */
472     properties[PROP_ARCHITECTURE] = g_param_spec_string("architecture",
473                                                         "ARCHITECTURE",
474                                                         _("CPU Architecture"),
475                                                         NULL /* default value */,
476                                                         G_PARAM_READWRITE |
477                                                         G_PARAM_STATIC_STRINGS);
478 
479     /**
480      * OsinfoMedia:url:
481      *
482      * The URL to this media.
483      */
484     properties[PROP_URL] = g_param_spec_string("url",
485                                                "URL",
486                                                _("The URL to this media"),
487                                                NULL /* default value */,
488                                                G_PARAM_READWRITE |
489                                                G_PARAM_STATIC_STRINGS);
490 
491     /**
492      * OsinfoMedia:volume-id:
493      *
494      * Expected volume ID (regular expression) for ISO9660 image/device.
495      */
496     properties[PROP_VOLUME_ID] = g_param_spec_string("volume-id",
497                                                      "VolumeID",
498                                                      _("The expected ISO9660 volume ID"),
499                                                      NULL /* default value */,
500                                                      G_PARAM_READWRITE |
501                                                      G_PARAM_STATIC_STRINGS);
502 
503     /**
504      * OsinfoMedia:publisher-id:
505      *
506      * Expected publisher ID (regular expression) for ISO9660 image/device.
507      */
508     properties[PROP_PUBLISHER_ID] = g_param_spec_string("publisher-id",
509                                                         "PublisherID",
510                                                         _("The expected ISO9660 publisher ID"),
511                                                         NULL /* default value */,
512                                                         G_PARAM_READWRITE |
513                                                         G_PARAM_STATIC_STRINGS);
514 
515     /**
516      * OsinfoMedia:application-id:
517      *
518      * Expected application ID (regular expression) for ISO9660 image/device.
519      */
520     properties[PROP_APPLICATION_ID] = g_param_spec_string("application-id",
521                                                           "ApplicationID",
522                                                           _("The expected ISO9660 application ID"),
523                                                           NULL /* default value */,
524                                                           G_PARAM_READWRITE |
525                                                           G_PARAM_STATIC_STRINGS);
526 
527     /**
528      * OsinfoMedia:system-id:
529      *
530      * Expected system ID (regular expression) for ISO9660 image/device.
531      */
532     properties[PROP_SYSTEM_ID] = g_param_spec_string("system-id",
533                                                      "SystemID",
534                                                      _("The expected ISO9660 system ID"),
535                                                      NULL /* default value */,
536                                                      G_PARAM_READWRITE |
537                                                      G_PARAM_STATIC_STRINGS);
538 
539     /**
540      * OsinfoMedia:kernel-path:
541      *
542      * The path to the kernel image in the install tree.
543      */
544     properties[PROP_KERNEL_PATH] = g_param_spec_string("kernel-path",
545                                                        "KernelPath",
546                                                        _("The path to the kernel image"),
547                                                        NULL /* default value */,
548                                                        G_PARAM_READWRITE |
549                                                        G_PARAM_STATIC_STRINGS);
550 
551     /**
552      * OsinfoMedia:initrd-path:
553      *
554      * The path to the initrd image in the install tree.
555      */
556     properties[PROP_INITRD_PATH] = g_param_spec_string("initrd-path",
557                                                        "InitrdPath",
558                                                        _("The path to the initrd image"),
559                                                        NULL /* default value */,
560                                                        G_PARAM_READWRITE |
561                                                        G_PARAM_STATIC_STRINGS);
562 
563     /**
564      * OsinfoMedia:installer:
565      *
566      * Whether media provides an installer for an OS.
567      */
568     properties[PROP_INSTALLER] = g_param_spec_boolean("installer",
569                                                       "Installer",
570                                                       _("Media provides an installer"),
571                                                       TRUE /* default value */,
572                                                       G_PARAM_READWRITE |
573                                                       G_PARAM_STATIC_STRINGS);
574 
575     /**
576      * OsinfoMedia:live:
577      *
578      * Whether media can boot directly an OS without any installations.
579      */
580     properties[PROP_LIVE] = g_param_spec_boolean("live",
581                                                  "Live",
582                                                  _("Media can boot directly w/o installation"),
583                                                  FALSE /* default value */,
584                                                  G_PARAM_READWRITE |
585                                                  G_PARAM_STATIC_STRINGS);
586 
587     /**
588      * OsinfoMedia:installer-reboots:
589      *
590      * If media is an installer, this property indicates the number of reboots
591      * the installer takes before installation is complete.
592      *
593      * This property is not applicable to media that has no installer. You can
594      * use #osinfo_media_get_installer(or OsinfoMedia::installer) to check
595      * that.
596      *
597      * Warning: Some media allow you to install from live sessions, in which
598      * case number of reboots *alone* is not a reliable method for tracking
599      * installation.
600      */
601     properties[PROP_INSTALLER_REBOOTS] = g_param_spec_int("installer-reboots",
602                                                           "InstallerReboots",
603                                                           _("Number of installer reboots"),
604                                                           G_MININT,
605                                                           G_MAXINT,
606                                                           1 /* default value */,
607                                                           G_PARAM_READWRITE |
608                                                           G_PARAM_STATIC_STRINGS);
609 
610     /**
611      * OsinfoMedia:os:
612      *
613      * Os information for the current media. For media stored in an
614      * #OsinfoDb, it will be filled when the database is loaded, otherwise
615      * the property will be filled after a successful call to
616      * osinfo_db_identify_media().
617      */
618     properties[PROP_OS] = g_param_spec_object("os",
619                                               "Os",
620                                               _("Information about the operating system on this media"),
621                                               OSINFO_TYPE_OS,
622                                               G_PARAM_READWRITE |
623                                               G_PARAM_STATIC_STRINGS);
624 
625     /**
626      * OsinfoMedia:languages: (type GLib.List(utf8)) (transfer container):
627      *
628      * If media is an installer, this property indicates the languages that
629      * can be used during automatic installations.
630      *
631      * On media that are not installers, this property will indicate the
632      * languages that the user interface can be displayed in.
633      * Use #osinfo_media_get_installer(or OsinfoMedia::installer) to know
634      * if the media is an installer or not.
635      */
636     properties[PROP_LANGUAGES] = g_param_spec_pointer("languages",
637                                                       "Languages",
638                                                       _("Supported languages"),
639                                                       G_PARAM_READABLE |
640                                                       G_PARAM_STATIC_STRINGS);
641 
642     /**
643      * OsinfoMedia:volume-size:
644      *
645      * Expected volume size, in bytes for ISO9660 image/device.
646      */
647     properties[PROP_VOLUME_SIZE] = g_param_spec_int64("volume-size",
648                                                       "VolumeSize",
649                                                       _("Expected ISO9660 volume size, in bytes"),
650                                                       G_MININT,
651                                                       G_MAXINT64,
652                                                       -1 /* default value */,
653                                                       G_PARAM_READWRITE |
654                                                       G_PARAM_STATIC_STRINGS);
655 
656     /**
657      * OsinfoMedia:eject-after-install:
658      *
659      * Whether the media should be ejected after the installation process.
660      *
661      * Some distros need their media to not be ejected after the final reboot
662      * during its installation process as some packages are installed after the
663      * reboot (which may cause the media to be ejected, depending on the
664      * application).
665      */
666     properties[PROP_EJECT_AFTER_INSTALL] = g_param_spec_boolean("eject-after-install",
667                                                                 "EjectAfterInstall",
668                                                                 _("Whether the media should be ejected after the installation process"),
669                                                                 TRUE /* default value */,
670                                                                 G_PARAM_READWRITE |
671                                                                 G_PARAM_STATIC_STRINGS);
672 
673     /**
674      * OsinfoMedia:installer-script:
675      *
676      * Whether the media supports installation via an install-script.
677      *
678      * Some distros provide a few different medias and not all the medias support
679      * installation via an install script.
680      */
681     properties[PROP_INSTALLER_SCRIPT] = g_param_spec_boolean("installer-script",
682                                                              "InstallerScript",
683                                                              _("Whether the media should be used for an installation using install scripts"),
684                                                              TRUE /* default value */,
685                                                              G_PARAM_READWRITE |
686                                                              G_PARAM_STATIC_STRINGS);
687 
688     g_object_class_install_properties(g_klass, LAST_PROP, properties);
689 }
690 
691 static void
osinfo_media_init(OsinfoMedia * media)692 osinfo_media_init(OsinfoMedia *media)
693 {
694     media->priv = osinfo_media_get_instance_private(media);
695     g_weak_ref_init(&media->priv->os, NULL);
696     media->priv->scripts = osinfo_install_scriptlist_new();
697 }
698 
osinfo_media_new(const gchar * id,const gchar * architecture)699 OsinfoMedia *osinfo_media_new(const gchar *id,
700                               const gchar *architecture)
701 {
702     OsinfoMedia *media;
703 
704     media = g_object_new(OSINFO_TYPE_MEDIA,
705                          "id", id,
706                          NULL);
707 
708     osinfo_entity_set_param(OSINFO_ENTITY(media),
709                             OSINFO_MEDIA_PROP_ARCHITECTURE,
710                             architecture);
711 
712     return media;
713 }
714 
on_media_create_from_location_ready(GObject * source_object,GAsyncResult * res,gpointer user_data)715 static void on_media_create_from_location_ready(GObject *source_object,
716                                                  GAsyncResult *res,
717                                                  gpointer user_data)
718 {
719     CreateFromLocationData *data = (CreateFromLocationData *)user_data;
720 
721     data->res = g_object_ref(res);
722 
723     g_main_loop_quit(data->main_loop);
724 }
725 
726 /**
727  * osinfo_media_create_from_location:
728  * @location: the location of an installation media
729  * @cancellable: (allow-none): a #GCancellable, or %NULL
730  * @error: The location where to store any error, or %NULL
731  *
732  * Creates a new #OsinfoMedia for installation media at @location. The @location
733  * could be a http:// or a https:// URI or a local path.
734  *
735  * NOTE: Currently this only works for ISO images/devices.
736  *
737  * Returns: (transfer full): a new #OsinfoMedia , or NULL on error
738  */
osinfo_media_create_from_location(const gchar * location,GCancellable * cancellable,GError ** error)739 OsinfoMedia *osinfo_media_create_from_location(const gchar *location,
740                                                GCancellable *cancellable,
741                                                GError **error)
742 {
743     return osinfo_media_create_from_location_with_flags(location,
744                                                         cancellable,
745                                                         OSINFO_MEDIA_DETECT_REQUIRE_BOOTABLE,
746                                                         error);
747 }
748 
749 /**
750  * osinfo_media_create_from_location_with_flags:
751  * @location: the location of an installation media
752  * @cancellable: (allow-none): a #GCancellable, or %NULL
753  * @error: The location where to store any error, or %NULL
754  * @flags: An #OsinfoMediaDetectFlag, or 0.
755  *
756  * Creates a new #OsinfoMedia for installation media at @location. The @location
757  * could be a http:// or a https:// URI or a local path.
758  *
759  * NOTE: Currently this only works for ISO images/devices.
760  *
761  * Returns: (transfer full): a new #OsinfoMedia , or NULL on error
762  *
763  * Since: 1.6.0
764  */
osinfo_media_create_from_location_with_flags(const gchar * location,GCancellable * cancellable,guint flags,GError ** error)765 OsinfoMedia *osinfo_media_create_from_location_with_flags(const gchar *location,
766                                                           GCancellable *cancellable,
767                                                           guint flags,
768                                                           GError **error)
769 {
770     CreateFromLocationData *data;
771     OsinfoMedia *ret;
772 
773     data = g_slice_new0(CreateFromLocationData);
774     data->main_loop = g_main_loop_new(g_main_context_get_thread_default(),
775                                       FALSE);
776 
777     osinfo_media_create_from_location_with_flags_async(location,
778                                                        G_PRIORITY_DEFAULT,
779                                                        cancellable,
780                                                        on_media_create_from_location_ready,
781                                                        flags,
782                                                        data);
783 
784     /* Loop till we get a reply (or time out) */
785     g_main_loop_run(data->main_loop);
786 
787     ret = osinfo_media_create_from_location_with_flags_finish(data->res, error);
788     create_from_location_data_free(data);
789 
790     return ret;
791 }
792 
is_str_empty(const gchar * str)793 static gboolean is_str_empty(const gchar *str) {
794     guint8 i;
795     gboolean ret = TRUE;
796 
797     if ((str == NULL) || (*str == 0))
798         return TRUE;
799 
800     for (i = 0; i < strlen(str); i++)
801         if (!g_ascii_isspace(str[i])) {
802            ret = FALSE;
803 
804            break;
805         }
806 
807     return ret;
808 }
809 
set_non_bootable_media_error(GError ** error)810 static void set_non_bootable_media_error(GError **error)
811 {
812     g_set_error(error,
813                 OSINFO_MEDIA_ERROR,
814                 OSINFO_MEDIA_ERROR_NOT_BOOTABLE,
815                 _("Install media is not bootable"));
816 }
817 
818 static OsinfoMedia *
create_from_location_async_data(CreateFromLocationAsyncData * data)819 create_from_location_async_data(CreateFromLocationAsyncData *data)
820 {
821     OsinfoMedia *media;
822     guint64 vol_size;
823     guint8 index;
824 
825     media = g_object_new(OSINFO_TYPE_MEDIA,
826                          "id", data->uri,
827                          NULL);
828     osinfo_entity_set_param(OSINFO_ENTITY(media),
829                             OSINFO_MEDIA_PROP_URL,
830                             data->uri);
831     if (!is_str_empty(data->volume))
832         osinfo_entity_set_param(OSINFO_ENTITY(media),
833                                 OSINFO_MEDIA_PROP_VOLUME_ID,
834                                 data->volume);
835     if (!is_str_empty(data->system))
836         osinfo_entity_set_param(OSINFO_ENTITY(media),
837                                 OSINFO_MEDIA_PROP_SYSTEM_ID,
838                                 data->system);
839     if (!is_str_empty(data->publisher))
840         osinfo_entity_set_param(OSINFO_ENTITY(media),
841                                 OSINFO_MEDIA_PROP_PUBLISHER_ID,
842                                 data->publisher);
843     if (!is_str_empty(data->application))
844         osinfo_entity_set_param(OSINFO_ENTITY(media),
845                                 OSINFO_MEDIA_PROP_APPLICATION_ID,
846                                 data->application);
847 
848     index = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? 0 : 1;
849     vol_size = ((gint64) data->pvd.volume_space_size[index]) *
850                data->pvd.logical_blk_size[index];
851     osinfo_entity_set_param_int64(OSINFO_ENTITY(media),
852                                   OSINFO_MEDIA_PROP_VOLUME_SIZE,
853                                   vol_size);
854 
855     osinfo_entity_set_param_boolean(OSINFO_ENTITY(media),
856                                     OSINFO_MEDIA_PROP_BOOTABLE,
857                                     data->bootable);
858 
859     return media;
860 }
861 
check_directory_record_entry_flags(guint8 flags,gboolean is_dir)862 static gboolean check_directory_record_entry_flags(guint8 flags,
863                                                    gboolean is_dir)
864 {
865     if (is_dir)
866         return (flags & DIRECTORY_RECORD_FLAG_DIRECTORY) != 0;
867 
868     return (flags & DIRECTORY_RECORD_FLAG_DIRECTORY) == 0;
869 }
870 
on_directory_record_extent_read(GObject * source,GAsyncResult * res,gpointer user_data)871 static void on_directory_record_extent_read(GObject *source,
872                                             GAsyncResult *res,
873                                             gpointer user_data)
874 {
875     GInputStream *stream = G_INPUT_STREAM(source);
876     SearchPPCBootinfoAsyncData *data;
877     DirectoryRecord *dr;
878     gsize offset;
879     gssize ret;
880     gboolean is_dir;
881     guint8 index = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? 0 : 1;
882     GError *error = NULL;
883 
884     data = (SearchPPCBootinfoAsyncData *)user_data;
885 
886     ret = g_input_stream_read_finish(stream, res, &error);
887     if (ret < 0) {
888         g_prefix_error(&error,
889                        _("Failed to read \"%s\" directory record extent: "),
890                        data->filepath[data->filepath_index]);
891         goto cleanup;
892     }
893 
894     if (ret == 0) {
895         g_set_error(&error,
896                     OSINFO_MEDIA_ERROR,
897                     OSINFO_MEDIA_ERROR_NO_DIRECTORY_RECORD_EXTENT,
898                     _("No \"%s\" directory record extent"),
899                     data->filepath[data->filepath_index]);
900         goto cleanup;
901     }
902 
903     data->offset += ret;
904     if (data->offset < data->length) {
905         g_input_stream_read_async(stream,
906                                   ((gchar *)data->extent + data->offset),
907                                   data->length - data->offset,
908                                   g_task_get_priority(data->res),
909                                   g_task_get_cancellable(data->res),
910                                   on_directory_record_extent_read,
911                                   data);
912         return;
913     }
914 
915 
916     is_dir = data->filepath_index < data->filepath_index_max - 1;
917     offset = 0;
918 
919     do {
920         gboolean check;
921 
922         dr = (DirectoryRecord *)&data->extent[offset];
923         if (dr->length == 0) {
924             offset++;
925             continue;
926         }
927 
928         check = check_directory_record_entry_flags(dr->flags, is_dir);
929         if (check &&
930             g_ascii_strncasecmp(data->filepath[data->filepath_index], dr->filename, strlen(data->filepath[data->filepath_index])) == 0) {
931             data->filepath_index++;
932             break;
933         }
934 
935         offset += dr->length;
936     } while (offset < data->length);
937 
938     if (offset >= data->length) {
939         set_non_bootable_media_error(&error);
940         goto cleanup;
941     }
942 
943     /* It just means that we walked through all the filepath entries and we
944      * found the file we're looking for! Just return TRUE! */
945     if (data->filepath_index == data->filepath_index_max)
946         goto cleanup;
947 
948     if (!G_IS_SEEKABLE(stream) ||
949         !g_seekable_seek(G_SEEKABLE(stream),
950                          dr->extent_location[index]  * data->pvd->logical_blk_size[index],
951                          G_SEEK_SET,
952                          g_task_get_cancellable(data->res),
953                          &error))
954         goto cleanup;
955 
956     data->offset = 0;
957     data->length = dr->extent_size[index];
958 
959     g_free(data->extent);
960     data->extent = g_malloc0(data->length);
961     g_input_stream_read_async(stream,
962                               data->extent,
963                               data->length,
964                               g_task_get_priority(data->res),
965                               g_task_get_cancellable(data->res),
966                               on_directory_record_extent_read,
967                               data);
968     return;
969 
970  cleanup:
971     if (error != NULL)
972         g_task_return_error(data->res, error);
973     else
974         g_task_return_boolean(data->res, TRUE);
975 
976     search_ppc_bootinfo_async_data_free(data);
977 }
978 
search_ppc_bootinfo_async(GInputStream * stream,PrimaryVolumeDescriptor * pvd,gint priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)979 static void search_ppc_bootinfo_async(GInputStream *stream,
980                                       PrimaryVolumeDescriptor *pvd,
981                                       gint priority,
982                                       GCancellable *cancellable,
983                                       GAsyncReadyCallback callback,
984                                       gpointer user_data)
985 {
986     SearchPPCBootinfoAsyncData *data;
987     DirectoryRecord *root_directory_entry = NULL;
988     GError *error = NULL;
989     guint8 index = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? 0 : 1;
990 
991     g_return_if_fail(G_IS_INPUT_STREAM(stream));
992     g_return_if_fail(pvd != NULL);
993 
994     data = g_slice_new0(SearchPPCBootinfoAsyncData);
995     data->pvd = pvd;
996     data->res = g_task_new(stream,
997                            cancellable,
998                            callback,
999                            user_data);
1000     g_task_set_priority(data->res, priority);
1001 
1002     root_directory_entry = (DirectoryRecord *)&data->pvd->root_directory_entry;
1003 
1004     if (!G_IS_SEEKABLE(stream) ||
1005         !g_seekable_seek(G_SEEKABLE(stream),
1006                          root_directory_entry->extent_location[index] * data->pvd->logical_blk_size[index],
1007                          G_SEEK_SET,
1008                          g_task_get_cancellable(data->res),
1009                          &error))
1010         goto cleanup;
1011 
1012     data->offset = 0;
1013     data->length = root_directory_entry->extent_size[index];
1014     data->extent = g_malloc0(root_directory_entry->extent_size[index]);
1015 
1016     data->filepath = g_strsplit(PPC_BOOTINFO, "/", -1);
1017     /* As the path starts with "/", we can just ignore the first element of the
1018      * split entry. */
1019     data->filepath_index = 1;
1020     data->filepath_index_max = g_strv_length(data->filepath);
1021 
1022     g_input_stream_read_async(stream,
1023                               data->extent,
1024                               data->length,
1025                               g_task_get_priority(data->res),
1026                               g_task_get_cancellable(data->res),
1027                               on_directory_record_extent_read,
1028                               data);
1029     return;
1030 
1031  cleanup:
1032     g_task_return_error(data->res, error);
1033     search_ppc_bootinfo_async_data_free(data);
1034 }
1035 
search_ppc_bootinfo_finish(GAsyncResult * res,GError ** error)1036 static gboolean search_ppc_bootinfo_finish(GAsyncResult *res,
1037                                            GError **error)
1038 {
1039     GTask *task = G_TASK(res);
1040 
1041     g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1042 
1043     return g_task_propagate_boolean(task, error);
1044 }
1045 
search_ppc_bootinfo_callback(GObject * source,GAsyncResult * res,gpointer user_data)1046 static void search_ppc_bootinfo_callback(GObject *source,
1047                                          GAsyncResult *res,
1048                                          gpointer user_data)
1049 {
1050     OsinfoMedia *media = NULL;
1051     GInputStream *stream = G_INPUT_STREAM(source);
1052     GError *error = NULL;
1053     CreateFromLocationAsyncData *data;
1054     gboolean ret;
1055 
1056     data = (CreateFromLocationAsyncData *)user_data;
1057 
1058     data->bootable = TRUE;
1059     ret = search_ppc_bootinfo_finish(res, &error);
1060     if (!ret) {
1061         if (g_error_matches(error,
1062                             OSINFO_MEDIA_ERROR,
1063                             OSINFO_MEDIA_ERROR_NOT_BOOTABLE)) {
1064             if ((data->flags & OSINFO_MEDIA_DETECT_REQUIRE_BOOTABLE) != 0) {
1065                 goto cleanup;
1066             } else {
1067                 g_clear_error(&error);
1068                 data->bootable = FALSE;
1069             }
1070         }
1071     }
1072 
1073     media = create_from_location_async_data(data);
1074 
1075  cleanup:
1076     if (error != NULL)
1077         g_task_return_error(data->res, error);
1078     else
1079         g_task_return_pointer(data->res, media, g_object_unref);
1080 
1081     g_object_unref(stream);
1082     create_from_location_async_data_free(data);
1083 }
1084 
on_svd_read(GObject * source,GAsyncResult * res,gpointer user_data)1085 static void on_svd_read(GObject *source,
1086                          GAsyncResult *res,
1087                          gpointer user_data)
1088 {
1089     OsinfoMedia *media = NULL;
1090     GInputStream *stream = G_INPUT_STREAM(source);
1091     GError *error = NULL;
1092     CreateFromLocationAsyncData *data;
1093     gssize ret;
1094 
1095     data = (CreateFromLocationAsyncData *)user_data;
1096 
1097     ret = g_input_stream_read_finish(stream,
1098                                      res,
1099                                      &error);
1100     if (ret < 0) {
1101         g_prefix_error(&error,
1102                        _("Failed to read supplementary volume descriptor: "));
1103         goto cleanup;
1104     }
1105     if (ret == 0) {
1106         g_set_error(&error,
1107                     OSINFO_MEDIA_ERROR,
1108                     OSINFO_MEDIA_ERROR_NO_SVD,
1109                     _("Supplementary volume descriptor was truncated"));
1110         goto cleanup;
1111     }
1112 
1113     data->offset += ret;
1114     if (data->offset < data->length) {
1115         g_input_stream_read_async(stream,
1116                                   ((gchar *)&data->svd + data->offset),
1117                                   data->length - data->offset,
1118                                   g_task_get_priority(data->res),
1119                                   g_task_get_cancellable(data->res),
1120                                   on_svd_read,
1121                                   data);
1122         return;
1123     }
1124 
1125 
1126     data->svd.system[MAX_SYSTEM - 1] = 0;
1127     g_strchomp(data->svd.system);
1128 
1129     if (strncmp(BOOTABLE_TAG, data->svd.system, sizeof(BOOTABLE_TAG)) != 0) {
1130         /*
1131          * In case we reached this point, there are basically 2 alternatives:
1132          * - the media is a PPC media and we should check for the existence of
1133          *   "/ppc/bootinfo.txt" file
1134          * - the media is not bootable.
1135          *
1136          * Let's check for the existence of the "/ppc/bootinfo.txt" file and,
1137          * only after that, return whether the media is bootable or not.
1138          */
1139         search_ppc_bootinfo_async(stream,
1140                                   &data->pvd,
1141                                   g_task_get_priority(data->res),
1142                                   g_task_get_cancellable(data->res),
1143                                   search_ppc_bootinfo_callback,
1144                                   data);
1145         return;
1146     }
1147 
1148     data->bootable = TRUE;
1149     media = create_from_location_async_data(data);
1150 
1151  cleanup:
1152     if (error != NULL)
1153         g_task_return_error(data->res, error);
1154     else
1155         g_task_return_pointer(data->res, media, g_object_unref);
1156 
1157     g_object_unref(stream);
1158     create_from_location_async_data_free(data);
1159 }
1160 
on_pvd_read(GObject * source,GAsyncResult * res,gpointer user_data)1161 static void on_pvd_read(GObject *source,
1162                          GAsyncResult *res,
1163                          gpointer user_data)
1164 {
1165     GInputStream *stream = G_INPUT_STREAM(source);
1166     CreateFromLocationAsyncData *data;
1167     GError *error = NULL;
1168     gssize ret;
1169 
1170     data = (CreateFromLocationAsyncData *)user_data;
1171 
1172     ret = g_input_stream_read_finish(stream,
1173                                      res,
1174                                      &error);
1175     if (ret < 0) {
1176         g_prefix_error(&error, _("Failed to read primary volume descriptor: "));
1177         goto error;
1178     }
1179     if (ret == 0) {
1180         g_set_error(&error,
1181                     OSINFO_MEDIA_ERROR,
1182                     OSINFO_MEDIA_ERROR_NO_PVD,
1183                     _("Primary volume descriptor was truncated"));
1184         goto error;
1185     }
1186 
1187     data->offset += ret;
1188     if (data->offset < data->length) {
1189         g_input_stream_read_async(stream,
1190                                   ((gchar*)&data->pvd) + data->offset,
1191                                   data->length - data->offset,
1192                                   g_task_get_priority(data->res),
1193                                   g_task_get_cancellable(data->res),
1194                                   on_pvd_read,
1195                                   data);
1196         return;
1197     }
1198 
1199     data->volume = g_strndup(data->pvd.volume, MAX_VOLUME);
1200     g_strchomp(data->volume);
1201 
1202     data->system = g_strndup(data->pvd.system, MAX_SYSTEM);
1203     g_strchomp(data->system);
1204 
1205     data->publisher = g_strndup(data->pvd.publisher, MAX_PUBLISHER);
1206     g_strchomp(data->publisher);
1207 
1208     data->application = g_strndup(data->pvd.application, MAX_APPLICATION);
1209     g_strchomp(data->application);
1210 
1211     if (is_str_empty(data->volume)) {
1212         g_set_error(&error,
1213                     OSINFO_MEDIA_ERROR,
1214                     OSINFO_MEDIA_ERROR_INSUFFICIENT_METADATA,
1215                     _("Insufficient metadata on installation media"));
1216 
1217         goto error;
1218     }
1219 
1220     data->offset = 0;
1221     data->length = sizeof(data->svd);
1222 
1223     g_input_stream_read_async(stream,
1224                               (gchar *)&data->svd,
1225                               data->length,
1226                               g_task_get_priority(data->res),
1227                               g_task_get_cancellable(data->res),
1228                               on_svd_read,
1229                               data);
1230     return;
1231 
1232  error:
1233     g_object_unref(stream);
1234     g_task_return_error(data->res, error);
1235     create_from_location_async_data_free(data);
1236 }
1237 
on_location_skipped(GObject * source,GAsyncResult * res,gpointer user_data)1238 static void on_location_skipped(GObject *source,
1239                                 GAsyncResult *res,
1240                                 gpointer user_data)
1241 {
1242     GInputStream *stream = G_INPUT_STREAM(source);
1243     CreateFromLocationAsyncData *data;
1244     GError *error = NULL;
1245 
1246     data = (CreateFromLocationAsyncData *)user_data;
1247 
1248     if (g_input_stream_skip_finish(stream, res, &error) < PVD_OFFSET) {
1249         if (error)
1250             g_prefix_error(&error, _("Failed to skip %d bytes"), PVD_OFFSET);
1251         else
1252             g_set_error(&error,
1253                         OSINFO_MEDIA_ERROR,
1254                         OSINFO_MEDIA_ERROR_NO_DESCRIPTORS,
1255                         _("No volume descriptors"));
1256         g_object_unref(stream);
1257         g_task_return_error(data->res, error);
1258         create_from_location_async_data_free(data);
1259 
1260         return;
1261     }
1262 
1263     data->offset = 0;
1264     data->length = sizeof(data->pvd);
1265 
1266     g_input_stream_read_async(stream,
1267                               (gchar *)&data->pvd,
1268                               data->length,
1269                               g_task_get_priority(data->res),
1270                               g_task_get_cancellable(data->res),
1271                               on_pvd_read,
1272                               data);
1273 }
1274 
on_location_read(GObject * source,GAsyncResult * res,gpointer user_data)1275 static void on_location_read(GObject *source,
1276                              GAsyncResult *res,
1277                              gpointer user_data)
1278 {
1279     GInputStream *stream;
1280     CreateFromLocationAsyncData *data;
1281     GError *error = NULL;
1282 
1283     data = (CreateFromLocationAsyncData *)user_data;
1284 
1285     if (data->file != NULL) {
1286         stream = G_INPUT_STREAM(g_file_read_finish(G_FILE(source), res, &error));
1287     } else {
1288         stream = soup_session_send_finish(SOUP_SESSION(source), res, &error);
1289         if (!SOUP_STATUS_IS_SUCCESSFUL(data->message->status_code) && error == NULL) {
1290             g_set_error_literal(&error,
1291                                 OSINFO_MEDIA_ERROR,
1292                                 OSINFO_MEDIA_ERROR_NO_DESCRIPTORS,
1293                                 soup_status_get_phrase(data->message->status_code));
1294         }
1295     }
1296     if (error != NULL) {
1297         g_prefix_error(&error, _("Failed to open file: "));
1298         g_task_return_error(data->res, error);
1299         create_from_location_async_data_free(data);
1300 
1301         return;
1302     }
1303 
1304     g_input_stream_skip_async(stream,
1305                               PVD_OFFSET,
1306                               g_task_get_priority(data->res),
1307                               g_task_get_cancellable(data->res),
1308                               on_location_skipped,
1309                               data);
1310 }
1311 
1312 /**
1313  * osinfo_media_create_from_location_async:
1314  * @location: the location of an installation media
1315  * @priority: the I/O priority of the request
1316  * @cancellable: (allow-none): a #GCancellable, or %NULL
1317  * @callback: Function to call when result of this call is ready
1318  * @user_data: The user data to pass to @callback, or %NULL
1319  *
1320  * Asynchronous variant of #osinfo_media_create_from_location.
1321  */
osinfo_media_create_from_location_async(const gchar * location,gint priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1322 void osinfo_media_create_from_location_async(const gchar *location,
1323                                              gint priority,
1324                                              GCancellable *cancellable,
1325                                              GAsyncReadyCallback callback,
1326                                              gpointer user_data)
1327 {
1328     osinfo_media_create_from_location_with_flags_async(location,
1329                                                        priority,
1330                                                        cancellable,
1331                                                        callback,
1332                                                        OSINFO_MEDIA_DETECT_REQUIRE_BOOTABLE,
1333                                                        user_data);
1334 }
1335 
1336 /**
1337  * osinfo_media_create_from_location_finish:
1338  * @res: a #GAsyncResult
1339  * @error: The location where to store any error, or %NULL
1340  *
1341  * Finishes an asynchronous media object creation process started with
1342  * #osinfo_media_create_from_location_async.
1343  *
1344  * Returns: (transfer full): a new #OsinfoMedia , or NULL on error
1345  *
1346  * Since: 1.6.0
1347  */
osinfo_media_create_from_location_finish(GAsyncResult * res,GError ** error)1348 OsinfoMedia *osinfo_media_create_from_location_finish(GAsyncResult *res,
1349                                                       GError **error)
1350 {
1351     return osinfo_media_create_from_location_with_flags_finish(res, error);
1352 }
1353 
1354 /**
1355  * osinfo_media_create_from_location_with_flags_async:
1356  * @location: the location of an installation media
1357  * @priority: the I/O priority of the request
1358  * @cancellable: (allow-none): a #GCancellable, or %NULL
1359  * @callback: Function to call when result of this call is ready
1360  * @flags: An #OsinfoMediaDetectFlag, or 0.
1361  * @user_data: The user data to pass to @callback, or %NULL
1362  *
1363  * Asynchronous variant of #osinfo_media_create_from_location_with_flags.
1364  *
1365  * Since: 1.6.0
1366  */
osinfo_media_create_from_location_with_flags_async(const gchar * location,gint priority,GCancellable * cancellable,GAsyncReadyCallback callback,guint flags,gpointer user_data)1367 void osinfo_media_create_from_location_with_flags_async(const gchar *location,
1368                                                         gint priority,
1369                                                         GCancellable *cancellable,
1370                                                         GAsyncReadyCallback callback,
1371                                                         guint flags,
1372                                                         gpointer user_data)
1373 {
1374     CreateFromLocationAsyncData *data;
1375 
1376     g_return_if_fail(location != NULL);
1377 
1378     data = g_slice_new0(CreateFromLocationAsyncData);
1379     data->res = g_task_new(NULL,
1380                            cancellable,
1381                            callback,
1382                            user_data);
1383     g_task_set_priority(data->res, priority);
1384     data->flags = flags;
1385 
1386     data->uri = g_strdup(location);
1387 
1388     if (osinfo_util_requires_soup(location)) {
1389         data->session = soup_session_new_with_options(
1390                 SOUP_SESSION_USER_AGENT, "Wget/1.0",
1391                 NULL);
1392         data->message = soup_message_new("GET", location);
1393 
1394         soup_session_send_async(data->session,
1395                                 data->message,
1396                                 cancellable,
1397                                 on_location_read,
1398                                 data);
1399     } else {
1400         data->file = g_file_new_for_commandline_arg(location);
1401         g_file_read_async(data->file,
1402                           priority,
1403                           cancellable,
1404                           on_location_read,
1405                           data);
1406     }
1407 }
1408 
1409 /**
1410  * osinfo_media_create_from_location_with_flags_finish:
1411  * @res: a #GAsyncResult
1412  * @error: The location where to store any error, or %NULL
1413  *
1414  * Finishes an asynchronous media object creation process started with
1415  * #osinfo_media_create_from_location_async.
1416  *
1417  * Returns: (transfer full): a new #OsinfoMedia , or NULL on error
1418  */
osinfo_media_create_from_location_with_flags_finish(GAsyncResult * res,GError ** error)1419 OsinfoMedia *osinfo_media_create_from_location_with_flags_finish(GAsyncResult *res,
1420                                                                  GError **error)
1421 {
1422     GTask *task = G_TASK(res);
1423 
1424     g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1425 
1426     return g_task_propagate_pointer(task, error);
1427 }
1428 
1429 /**
1430  * osinfo_media_get_architecture:
1431  * @media: an #OsinfoMedia instance
1432  *
1433  * Retrieves the target hardware architecture of the OS @media provides.
1434  *
1435  * Returns: (transfer none): the hardware architecture, or NULL
1436  */
osinfo_media_get_architecture(OsinfoMedia * media)1437 const gchar *osinfo_media_get_architecture(OsinfoMedia *media)
1438 {
1439     return osinfo_entity_get_param_value(OSINFO_ENTITY(media),
1440                                          OSINFO_MEDIA_PROP_ARCHITECTURE);
1441 }
1442 
1443 /**
1444  * osinfo_media_get_url:
1445  * @media: an #OsinfoMedia instance
1446  *
1447  * The URL to the @media
1448  *
1449  * Returns: (transfer none): the URL, or NULL
1450  */
osinfo_media_get_url(OsinfoMedia * media)1451 const gchar *osinfo_media_get_url(OsinfoMedia *media)
1452 {
1453     return osinfo_entity_get_param_value(OSINFO_ENTITY(media),
1454                                          OSINFO_MEDIA_PROP_URL);
1455 }
1456 
1457 /**
1458  * osinfo_media_get_volume_id:
1459  * @media: an #OsinfoMedia instance
1460  *
1461  * If @media is an ISO9660 image/device, this function retrieves the expected
1462  * volume ID.
1463  *
1464  * Note: In practice, this will usually not be the exact copy of the volume ID
1465  * string on the ISO image/device but rather a regular expression that matches
1466  * it.
1467  *
1468  * Returns: (transfer none): the volume id, or NULL
1469  */
osinfo_media_get_volume_id(OsinfoMedia * media)1470 const gchar *osinfo_media_get_volume_id(OsinfoMedia *media)
1471 {
1472     return osinfo_entity_get_param_value(OSINFO_ENTITY(media),
1473                                          OSINFO_MEDIA_PROP_VOLUME_ID);
1474 }
1475 
1476 /**
1477  * osinfo_media_get_system_id:
1478  * @media: an #OsinfoMedia instance
1479  *
1480  * If @media is an ISO9660 image/device, this function retrieves the expected
1481  * system ID.
1482  *
1483  * Note: In practice, this will usually not be the exact copy of the system ID
1484  * string on the ISO image/device but rather a regular expression that matches
1485  * it.
1486  *
1487  * Returns: (transfer none): the system id, or NULL
1488  */
osinfo_media_get_system_id(OsinfoMedia * media)1489 const gchar *osinfo_media_get_system_id(OsinfoMedia *media)
1490 {
1491     return osinfo_entity_get_param_value(OSINFO_ENTITY(media),
1492                                          OSINFO_MEDIA_PROP_SYSTEM_ID);
1493 }
1494 
1495 /**
1496  * osinfo_media_get_publisher_id:
1497  * @media: an #OsinfoMedia instance
1498  *
1499  * If @media is an ISO9660 image/device, this function retrieves the expected
1500  * publisher ID.
1501  *
1502  * Note: In practice, this will usually not be the exact copy of the publisher
1503  * ID string on the ISO image/device but rather a regular expression that
1504  * matches it.
1505  *
1506  * Returns: (transfer none): the publisher id, or NULL
1507  */
osinfo_media_get_publisher_id(OsinfoMedia * media)1508 const gchar *osinfo_media_get_publisher_id(OsinfoMedia *media)
1509 {
1510     return osinfo_entity_get_param_value(OSINFO_ENTITY(media),
1511                                          OSINFO_MEDIA_PROP_PUBLISHER_ID);
1512 }
1513 
1514 /**
1515  * osinfo_media_get_application_id:
1516  * @media: an #OsinfoMedia instance
1517  *
1518  * If @media is an ISO9660 image/device, this function retrieves the expected
1519  * application ID.
1520  *
1521  * Note: In practice, this will usually not be the exact copy of the application
1522  * ID string on the ISO image/device but rather a regular expression that
1523  * matches it.
1524  *
1525  * Returns: (transfer none): the application id, or NULL
1526  */
osinfo_media_get_application_id(OsinfoMedia * media)1527 const gchar *osinfo_media_get_application_id(OsinfoMedia *media)
1528 {
1529     return osinfo_entity_get_param_value(OSINFO_ENTITY(media),
1530                                          OSINFO_MEDIA_PROP_APPLICATION_ID);
1531 }
1532 
1533 /**
1534  * osinfo_media_get_kernel_path:
1535  * @media: an #OsinfoMedia instance
1536  *
1537  * Retrieves the path to the kernel image in the install tree.
1538  *
1539  * Note: This only applies to installer medias of 'linux' OS family.
1540  *
1541  * Returns: (transfer none): the path to kernel image, or NULL
1542  */
osinfo_media_get_kernel_path(OsinfoMedia * media)1543 const gchar *osinfo_media_get_kernel_path(OsinfoMedia *media)
1544 {
1545     return osinfo_entity_get_param_value(OSINFO_ENTITY(media),
1546                                          OSINFO_MEDIA_PROP_KERNEL);
1547 }
1548 
1549 /**
1550  * osinfo_media_get_initrd_path:
1551  * @media: an #OsinfoMedia instance
1552  *
1553  * Retrieves the path to the initrd image in the install tree.
1554  *
1555  * Note: This only applies to installer medias of 'linux' OS family.
1556  *
1557  * Returns: (transfer none): the path to initrd image, or NULL
1558  */
osinfo_media_get_initrd_path(OsinfoMedia * media)1559 const gchar *osinfo_media_get_initrd_path(OsinfoMedia *media)
1560 {
1561     return osinfo_entity_get_param_value(OSINFO_ENTITY(media),
1562                                          OSINFO_MEDIA_PROP_INITRD);
1563 }
1564 
1565 /**
1566  * osinfo_media_get_installer:
1567  * @media: an #OsinfoMedia instance
1568  *
1569  * Whether @media provides an installer for an OS.
1570  *
1571  * Returns: #TRUE if media is installer, #FALSE otherwise
1572  *
1573  * Since: 0.0.3
1574  */
osinfo_media_get_installer(OsinfoMedia * media)1575 gboolean osinfo_media_get_installer(OsinfoMedia *media)
1576 {
1577     return osinfo_entity_get_param_value_boolean_with_default
1578             (OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_INSTALLER, TRUE);
1579 }
1580 
1581 /**
1582  * osinfo_media_get_live:
1583  * @media: an #OsinfoMedia instance
1584  *
1585  * Whether @media can boot directly an OS without any installations.
1586  *
1587  * Returns: #TRUE if media is live, #FALSE otherwise
1588  *
1589  * Since: 0.0.3
1590  */
osinfo_media_get_live(OsinfoMedia * media)1591 gboolean osinfo_media_get_live(OsinfoMedia *media)
1592 {
1593     return osinfo_entity_get_param_value_boolean_with_default
1594             (OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_LIVE, FALSE);
1595 }
1596 
1597 /**
1598  * osinfo_media_get_installer_reboots:
1599  * @media: an #OsinfoMedia instance
1600  *
1601  * If media is an installer, this method retrieves the number of reboots the
1602  * installer takes before installation is complete.
1603  *
1604  * This function is not supposed to be called on media that has no installer.
1605  * You can use #osinfo_media_get_installer(or OsinfoMedia::installer) to check
1606  * that.
1607  *
1608  * Warning: Some media allow you to install from live sessions, in which case
1609  * number of reboots *alone* is not a reliable method for tracking installation.
1610  *
1611  * Returns: the number of installer reboots or -1 if media is not an installer
1612  *
1613  * Since: 0.2.1
1614  */
osinfo_media_get_installer_reboots(OsinfoMedia * media)1615 gint osinfo_media_get_installer_reboots(OsinfoMedia *media)
1616 {
1617     g_return_val_if_fail(OSINFO_IS_MEDIA(media), -1);
1618     g_return_val_if_fail(osinfo_media_get_installer(media), -1);
1619 
1620     return (gint) osinfo_entity_get_param_value_int64_with_default
1621             (OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_INSTALLER_REBOOTS, 1);
1622 }
1623 
1624 /**
1625  * osinfo_media_get_os:
1626  * @media: an #OsinfoMedia instance
1627  *
1628  * Returns: (transfer full): the operating system, or NULL
1629  *
1630  * Since: 0.2.3
1631  */
osinfo_media_get_os(OsinfoMedia * media)1632 OsinfoOs *osinfo_media_get_os(OsinfoMedia *media)
1633 {
1634     g_return_val_if_fail(OSINFO_IS_MEDIA(media), NULL);
1635 
1636     return g_weak_ref_get(&media->priv->os);
1637 }
1638 
osinfo_media_set_os(OsinfoMedia * media,OsinfoOs * os)1639 void osinfo_media_set_os(OsinfoMedia *media, OsinfoOs *os)
1640 {
1641     g_return_if_fail(OSINFO_IS_MEDIA(media));
1642 
1643     g_object_ref(os);
1644     g_weak_ref_set(&media->priv->os, os);
1645     g_object_unref(os);
1646 }
1647 
1648 /**
1649  * osinfo_media_get_os_variants:
1650  * @media: an #OsinfoMedia instance
1651  *
1652  * Gets the variants of the associated operating system.
1653  *
1654  * Returns: (transfer full): the operating system variant, or NULL
1655  *
1656  * Since: 0.2.9
1657  */
osinfo_media_get_os_variants(OsinfoMedia * media)1658 OsinfoOsVariantList *osinfo_media_get_os_variants(OsinfoMedia *media)
1659 {
1660     OsinfoOs *os;
1661     OsinfoOsVariantList *os_variants;
1662     OsinfoOsVariantList *media_variants;
1663     GList *ids, *node;
1664     OsinfoFilter *filter;
1665 
1666     g_return_val_if_fail(OSINFO_IS_MEDIA(media), NULL);
1667 
1668     os = g_weak_ref_get(&media->priv->os);
1669     if (os == NULL)
1670         return NULL;
1671 
1672     os_variants = osinfo_os_get_variant_list(os);
1673     g_object_unref(os);
1674 
1675     ids = osinfo_entity_get_param_value_list(OSINFO_ENTITY(media),
1676                                              OSINFO_MEDIA_PROP_VARIANT);
1677     filter = osinfo_filter_new();
1678     media_variants = osinfo_os_variantlist_new();
1679     for (node = ids; node != NULL; node = node->next) {
1680         osinfo_filter_clear_constraints(filter);
1681         osinfo_filter_add_constraint(filter,
1682                                      OSINFO_ENTITY_PROP_ID,
1683                                      (const char *) node->data);
1684         osinfo_list_add_filtered(OSINFO_LIST(media_variants),
1685                                  OSINFO_LIST(os_variants),
1686                                  filter);
1687     }
1688     g_object_unref(os_variants);
1689 
1690     return media_variants;
1691 }
1692 
1693 /**
1694  * osinfo_media_get_languages:
1695  * @media: an #OsinfoMedia instance
1696  *
1697  * If media is an installer, this property indicates the languages that
1698  * can be used during automatic installations.
1699  *
1700  * On media that are not installers, this property will indicate the
1701  * languages that the user interface can be displayed in.
1702  * Use #osinfo_media_get_installer(or OsinfoMedia::installer) to know
1703  * if the media is an installer or not.
1704  *
1705  * Returns: (transfer container) (element-type utf8): a #GList
1706  * containing the list of the UI languages this media supports. The list
1707  * must be freed with g_list_free() when no longer needed. If the
1708  * supported languages are unknown, NULL will be returned.
1709  *
1710  * Since: 0.2.3
1711  */
osinfo_media_get_languages(OsinfoMedia * media)1712 GList *osinfo_media_get_languages(OsinfoMedia *media)
1713 {
1714     g_return_val_if_fail(OSINFO_IS_MEDIA(media), NULL);
1715     return osinfo_entity_get_param_value_list(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_LANG);
1716 }
1717 
1718 /**
1719  * osinfo_media_set_languages:
1720  * @media: an #OsinfoMedia instance
1721  * @languages: (element-type utf8): a #GList containing the list of the UI
1722  * languages this media supports.
1723  *
1724  * Sets the #OSINFO_MEDIA_PROP_LANG parameter
1725  */
osinfo_media_set_languages(OsinfoMedia * media,GList * languages)1726 void osinfo_media_set_languages(OsinfoMedia *media, GList *languages)
1727 {
1728     GList *it;
1729 
1730     g_return_if_fail(OSINFO_IS_MEDIA(media));
1731 
1732     osinfo_entity_clear_param(OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_LANG);
1733     for (it = languages; it != NULL; it = it->next)
1734         osinfo_entity_add_param(OSINFO_ENTITY(media),
1735                                 OSINFO_MEDIA_PROP_LANG,
1736                                 it->data);
1737 }
1738 
1739 /**
1740  * osinfo_media_get_volume_size:
1741  * @media: an #OsinfoMedia instance
1742  *
1743  * Returns: the ISO9660 volume size, in bytes or -1 if size is
1744  * unknown or media is not an ISO9660 device/image.
1745  */
osinfo_media_get_volume_size(OsinfoMedia * media)1746 gint64 osinfo_media_get_volume_size(OsinfoMedia *media)
1747 {
1748     g_return_val_if_fail(OSINFO_IS_MEDIA(media), -1);
1749 
1750     return osinfo_entity_get_param_value_int64_with_default
1751         (OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_VOLUME_SIZE, -1);
1752 }
1753 
1754 /**
1755  * osinfo_media_get_eject_after_install:
1756  * @media: an #OsinfoMedia instance
1757  *
1758  * Whether @media should ejected after the installation procces.
1759  *
1760  * Returns: #TRUE if media should be ejected, #FALSE otherwise
1761  *
1762  * Since: 0.2.13
1763  */
osinfo_media_get_eject_after_install(OsinfoMedia * media)1764 gboolean osinfo_media_get_eject_after_install(OsinfoMedia *media)
1765 {
1766     return osinfo_entity_get_param_value_boolean_with_default
1767         (OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_EJECT_AFTER_INSTALL, TRUE);
1768 }
1769 
1770 /**
1771  * osinfo_media_supports_installer_script:
1772  * @media: an #OsinfoMedia instance
1773  *
1774  * Whether @media supports installation using install scripts.
1775  *
1776  * Returns: #TRUE if install-scripts are supported by the media,
1777  * #FALSE otherwise
1778  *
1779  * Since: 1.3.0
1780  */
osinfo_media_supports_installer_script(OsinfoMedia * media)1781 gboolean osinfo_media_supports_installer_script(OsinfoMedia *media)
1782 {
1783     OsinfoOs *os;
1784     OsinfoInstallScriptList *list;
1785     gboolean ret;
1786 
1787     g_return_val_if_fail(OSINFO_IS_MEDIA(media), FALSE);
1788 
1789     os = osinfo_media_get_os(media);
1790     list = osinfo_os_get_install_script_list(os);
1791 
1792     if (osinfo_list_get_length(OSINFO_LIST(list)) == 0 &&
1793         osinfo_list_get_length(OSINFO_LIST(media->priv->scripts)) == 0) {
1794         ret = FALSE;
1795         goto cleanup;
1796     }
1797 
1798     ret = osinfo_entity_get_param_value_boolean_with_default
1799             (OSINFO_ENTITY(media), OSINFO_MEDIA_PROP_INSTALLER_SCRIPT, TRUE);
1800 
1801  cleanup:
1802     g_object_unref(list);
1803     g_object_unref(os);
1804 
1805     return ret;
1806 }
1807 
1808 /**
1809  * osinfo_media_add_install_script:
1810  * @media: an #OsinfoMedia instance
1811  * @script: an #OsinfoInstallScript instance
1812  *
1813  * Adds an @script to the specified @media
1814  *
1815  * Since: 1.4.0
1816  */
osinfo_media_add_install_script(OsinfoMedia * media,OsinfoInstallScript * script)1817 void osinfo_media_add_install_script(OsinfoMedia *media, OsinfoInstallScript *script)
1818 {
1819     g_return_if_fail(OSINFO_IS_MEDIA(media));
1820 
1821     osinfo_list_add(OSINFO_LIST(media->priv->scripts), OSINFO_ENTITY(script));
1822 }
1823 
1824 /**
1825  * osinfo_media_get_install_script_list:
1826  * @media: an #OsinfoMedia instance
1827  *
1828  * Returns: (transfer full): a list of the install scripts for the specified media
1829  *
1830  * Since: 1.4.0
1831  */
osinfo_media_get_install_script_list(OsinfoMedia * media)1832 OsinfoInstallScriptList *osinfo_media_get_install_script_list(OsinfoMedia *media)
1833 {
1834     OsinfoList *new_list;
1835 
1836     g_return_val_if_fail(OSINFO_IS_MEDIA(media), NULL);
1837     new_list = osinfo_list_new_copy(OSINFO_LIST(media->priv->scripts));
1838 
1839     return OSINFO_INSTALL_SCRIPTLIST(new_list);
1840 }
1841 
1842 /**
1843  * osinfo_media_is_bootable:
1844  * @media: and #OsinfoMedia instance
1845  *
1846  * Returns: #TRUE if the @media is bootable. #FALSE otherwise.
1847  *
1848  * Since: 1.6.0
1849  */
osinfo_media_is_bootable(OsinfoMedia * media)1850 gboolean osinfo_media_is_bootable(OsinfoMedia *media)
1851 {
1852     g_return_val_if_fail(OSINFO_IS_MEDIA(media), FALSE);
1853 
1854     return osinfo_entity_get_param_value_boolean(OSINFO_ENTITY(media),
1855                                                  OSINFO_MEDIA_PROP_BOOTABLE);
1856 }
1857