1313#include "discover-image.h"
1414#include "dissect-image.h"
1515#include "env-file.h"
16+ #include "env-util.h"
1617#include "errno-list.h"
1718#include "escape.h"
1819#include "extension-release.h"
@@ -509,20 +510,20 @@ static int extract_image_and_extensions(
509510 OrderedHashmap * * ret_extension_images ,
510511 PortableMetadata * * ret_os_release ,
511512 Hashmap * * ret_unit_files ,
513+ char * * * ret_valid_prefixes ,
512514 sd_bus_error * error ) {
513515
514516 _cleanup_free_ char * id = NULL , * version_id = NULL , * sysext_level = NULL ;
515517 _cleanup_ (portable_metadata_unrefp ) PortableMetadata * os_release = NULL ;
516518 _cleanup_ordered_hashmap_free_ OrderedHashmap * extension_images = NULL ;
517519 _cleanup_hashmap_free_ Hashmap * unit_files = NULL ;
520+ _cleanup_strv_free_ char * * valid_prefixes = NULL ;
518521 _cleanup_ (image_unrefp ) Image * image = NULL ;
519522 Image * ext ;
520523 int r ;
521524
522525 assert (name_or_path );
523526 assert (matches );
524- assert (ret_image );
525- assert (ret_extension_images );
526527
527528 r = image_find_harder (IMAGE_PORTABLE , name_or_path , NULL , & image );
528529 if (r < 0 )
@@ -553,10 +554,12 @@ static int extract_image_and_extensions(
553554 if (r < 0 )
554555 return r ;
555556
556- /* If we are layering extension images on top of a runtime image, check that the os-release and extension-release metadata
557- * match, otherwise reject it immediately as invalid, or it will fail when the units are started. */
558- if (validate_sysext ) {
557+ /* If we are layering extension images on top of a runtime image, check that the os-release and
558+ * extension-release metadata match, otherwise reject it immediately as invalid, or it will fail when
559+ * the units are started. Also, collect valid portable prefixes if caller requested that. */
560+ if (validate_sysext || ret_valid_prefixes ) {
559561 _cleanup_fclose_ FILE * f = NULL ;
562+ _cleanup_free_ char * prefixes = NULL ;
560563
561564 r = take_fdopen_unlocked (& os_release -> fd , "r" , & f );
562565 if (r < 0 )
@@ -565,16 +568,24 @@ static int extract_image_and_extensions(
565568 r = parse_env_file (f , os_release -> name ,
566569 "ID" , & id ,
567570 "VERSION_ID" , & version_id ,
568- "SYSEXT_LEVEL" , & sysext_level );
571+ "SYSEXT_LEVEL" , & sysext_level ,
572+ "PORTABLE_PREFIXES" , & prefixes );
569573 if (r < 0 )
570574 return r ;
575+
576+ if (prefixes ) {
577+ valid_prefixes = strv_split (prefixes , WHITESPACE );
578+ if (!valid_prefixes )
579+ return - ENOMEM ;
580+ }
571581 }
572582
573583 ORDERED_HASHMAP_FOREACH (ext , extension_images ) {
574584 _cleanup_ (portable_metadata_unrefp ) PortableMetadata * extension_release_meta = NULL ;
575585 _cleanup_hashmap_free_ Hashmap * extra_unit_files = NULL ;
576586 _cleanup_strv_free_ char * * extension_release = NULL ;
577587 _cleanup_fclose_ FILE * f = NULL ;
588+ const char * e ;
578589
579590 r = portable_extract_by_path (ext -> path , /* path_is_extension= */ true, matches , & extension_release_meta , & extra_unit_files , error );
580591 if (r < 0 )
@@ -584,7 +595,7 @@ static int extract_image_and_extensions(
584595 if (r < 0 )
585596 return r ;
586597
587- if (!validate_sysext )
598+ if (!validate_sysext && ! ret_valid_prefixes )
588599 continue ;
589600
590601 r = take_fdopen_unlocked (& extension_release_meta -> fd , "r" , & f );
@@ -595,19 +606,40 @@ static int extract_image_and_extensions(
595606 if (r < 0 )
596607 return r ;
597608
598- r = extension_release_validate (ext -> path , id , version_id , sysext_level , "portable" , extension_release );
599- if (r == 0 )
600- return sd_bus_error_set_errnof (error , SYNTHETIC_ERRNO (ESTALE ), "Image %s extension-release metadata does not match the root's" , ext -> path );
601- if (r < 0 )
602- return sd_bus_error_set_errnof (error , r , "Failed to compare image %s extension-release metadata with the root's os-release: %m" , ext -> path );
609+ if (validate_sysext ) {
610+ r = extension_release_validate (ext -> path , id , version_id , sysext_level , "portable" , extension_release );
611+ if (r == 0 )
612+ return sd_bus_error_set_errnof (error , SYNTHETIC_ERRNO (ESTALE ), "Image %s extension-release metadata does not match the root's" , ext -> path );
613+ if (r < 0 )
614+ return sd_bus_error_set_errnof (error , r , "Failed to compare image %s extension-release metadata with the root's os-release: %m" , ext -> path );
615+ }
616+
617+ e = strv_env_pairs_get (extension_release , "PORTABLE_PREFIXES" );
618+ if (e ) {
619+ _cleanup_strv_free_ char * * l = NULL ;
620+
621+ l = strv_split (e , WHITESPACE );
622+ if (!l )
623+ return - ENOMEM ;
624+
625+ r = strv_extend_strv (& valid_prefixes , l , true);
626+ if (r < 0 )
627+ return r ;
628+ }
603629 }
604630
605- * ret_image = TAKE_PTR (image );
606- * ret_extension_images = TAKE_PTR (extension_images );
631+ strv_sort (valid_prefixes );
632+
633+ if (ret_image )
634+ * ret_image = TAKE_PTR (image );
635+ if (ret_extension_images )
636+ * ret_extension_images = TAKE_PTR (extension_images );
607637 if (ret_os_release )
608638 * ret_os_release = TAKE_PTR (os_release );
609639 if (ret_unit_files )
610640 * ret_unit_files = TAKE_PTR (unit_files );
641+ if (ret_valid_prefixes )
642+ * ret_valid_prefixes = TAKE_PTR (valid_prefixes );
611643
612644 return 0 ;
613645}
@@ -618,23 +650,29 @@ int portable_extract(
618650 char * * extension_image_paths ,
619651 PortableMetadata * * ret_os_release ,
620652 Hashmap * * ret_unit_files ,
653+ char * * * ret_valid_prefixes ,
621654 sd_bus_error * error ) {
622655
623656 _cleanup_ (portable_metadata_unrefp ) PortableMetadata * os_release = NULL ;
624657 _cleanup_ordered_hashmap_free_ OrderedHashmap * extension_images = NULL ;
625658 _cleanup_hashmap_free_ Hashmap * unit_files = NULL ;
659+ _cleanup_ (strv_freep ) char * * valid_prefixes = NULL ;
626660 _cleanup_ (image_unrefp ) Image * image = NULL ;
627661 int r ;
628662
629- r = extract_image_and_extensions (name_or_path ,
630- matches ,
631- extension_image_paths ,
632- /* validate_sysext= */ false,
633- & image ,
634- & extension_images ,
635- & os_release ,
636- & unit_files ,
637- error );
663+ assert (name_or_path );
664+
665+ r = extract_image_and_extensions (
666+ name_or_path ,
667+ matches ,
668+ extension_image_paths ,
669+ /* validate_sysext= */ false,
670+ & image ,
671+ & extension_images ,
672+ & os_release ,
673+ & unit_files ,
674+ ret_valid_prefixes ? & valid_prefixes : NULL ,
675+ error );
638676 if (r < 0 )
639677 return r ;
640678
@@ -651,8 +689,12 @@ int portable_extract(
651689 isempty (extensions ) ? "" : extensions );
652690 }
653691
654- * ret_os_release = TAKE_PTR (os_release );
655- * ret_unit_files = TAKE_PTR (unit_files );
692+ if (ret_os_release )
693+ * ret_os_release = TAKE_PTR (os_release );
694+ if (ret_unit_files )
695+ * ret_unit_files = TAKE_PTR (unit_files );
696+ if (ret_valid_prefixes )
697+ * ret_valid_prefixes = TAKE_PTR (valid_prefixes );
656698
657699 return 0 ;
658700}
@@ -1211,6 +1253,18 @@ static int install_image_and_extensions_symlinks(
12111253 return 0 ;
12121254}
12131255
1256+ static bool prefix_matches_compatible (char * * matches , char * * valid_prefixes ) {
1257+ char * * m ;
1258+
1259+ /* Checks if all 'matches' are included in the list of 'valid_prefixes' */
1260+
1261+ STRV_FOREACH (m , matches )
1262+ if (!strv_contains (valid_prefixes , * m ))
1263+ return false;
1264+
1265+ return true;
1266+ }
1267+
12141268int portable_attach (
12151269 sd_bus * bus ,
12161270 const char * name_or_path ,
@@ -1225,33 +1279,63 @@ int portable_attach(
12251279 _cleanup_ordered_hashmap_free_ OrderedHashmap * extension_images = NULL ;
12261280 _cleanup_hashmap_free_ Hashmap * unit_files = NULL ;
12271281 _cleanup_ (lookup_paths_free ) LookupPaths paths = {};
1282+ _cleanup_strv_free_ char * * valid_prefixes = NULL ;
12281283 _cleanup_ (image_unrefp ) Image * image = NULL ;
12291284 PortableMetadata * item ;
12301285 int r ;
12311286
1232- r = extract_image_and_extensions (name_or_path ,
1233- matches ,
1234- extension_image_paths ,
1235- /* validate_sysext= */ true,
1236- & image ,
1237- & extension_images ,
1238- /* os_release= */ NULL ,
1239- & unit_files ,
1240- error );
1287+ r = extract_image_and_extensions (
1288+ name_or_path ,
1289+ matches ,
1290+ extension_image_paths ,
1291+ /* validate_sysext= */ true,
1292+ & image ,
1293+ & extension_images ,
1294+ /* os_release= */ NULL ,
1295+ & unit_files ,
1296+ & valid_prefixes ,
1297+ error );
12411298 if (r < 0 )
12421299 return r ;
12431300
1301+ if (valid_prefixes && !prefix_matches_compatible (matches , valid_prefixes )) {
1302+ _cleanup_free_ char * matches_joined = NULL , * extensions_joined = NULL , * valid_prefixes_joined = NULL ;
1303+
1304+ matches_joined = strv_join (matches , "', '" );
1305+ if (!matches_joined )
1306+ return - ENOMEM ;
1307+
1308+ extensions_joined = strv_join (extension_image_paths , ", " );
1309+ if (!extensions_joined )
1310+ return - ENOMEM ;
1311+
1312+ valid_prefixes_joined = strv_join (valid_prefixes , ", " );
1313+ if (!valid_prefixes_joined )
1314+ return - ENOMEM ;
1315+
1316+ return sd_bus_error_setf (
1317+ error ,
1318+ SD_BUS_ERROR_INVALID_ARGS ,
1319+ "Selected matches '%s' are not compatible with portable service image '%s%s%s', refusing. (Acceptable prefix matches are: %s)" ,
1320+ matches_joined ,
1321+ image -> path ,
1322+ isempty (extensions_joined ) ? "" : "' or any of its extensions '" ,
1323+ strempty (extensions_joined ),
1324+ valid_prefixes_joined );
1325+ }
1326+
12441327 if (hashmap_isempty (unit_files )) {
1245- _cleanup_free_ char * extensions = strv_join (extension_image_paths , ", " );
1246- if (!extensions )
1328+ _cleanup_free_ char * extensions_joined = strv_join (extension_image_paths , ", " );
1329+ if (!extensions_joined )
12471330 return - ENOMEM ;
12481331
1249- return sd_bus_error_setf (error ,
1250- SD_BUS_ERROR_INVALID_ARGS ,
1251- "Couldn't find any matching unit files in image '%s%s%s', refusing." ,
1252- image -> path ,
1253- isempty (extensions ) ? "" : "' or any of its extensions '" ,
1254- isempty (extensions ) ? "" : extensions );
1332+ return sd_bus_error_setf (
1333+ error ,
1334+ SD_BUS_ERROR_INVALID_ARGS ,
1335+ "Couldn't find any matching unit files in image '%s%s%s', refusing." ,
1336+ image -> path ,
1337+ isempty (extensions_joined ) ? "" : "' or any of its extensions '" ,
1338+ strempty (extensions_joined ));
12551339 }
12561340
12571341 r = lookup_paths_init (& paths , UNIT_FILE_SYSTEM , LOOKUP_PATHS_SPLIT_USR , NULL );
0 commit comments