2015-03-23 11:17:58 +00:00
/*
* Copyright © 2014 Red Hat , Inc
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
2016-07-29 18:27:49 +00:00
* version 2.1 of the License , or ( at your option ) any later version .
2015-03-23 11:17:58 +00:00
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2016-01-21 20:16:37 +00:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
2015-03-23 11:17:58 +00:00
* 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 < http : //www.gnu.org/licenses/>.
*
* Authors :
* Alexander Larsson < alexl @ redhat . com >
*/
2014-12-18 09:14:46 +00:00
# include "config.h"
2017-08-25 07:35:34 +00:00
# include "builder-flatpak-utils.h"
2014-12-18 09:14:46 +00:00
2016-07-23 17:59:45 +00:00
# include <glib/gi18n.h>
2015-03-20 15:21:19 +00:00
# include <string.h>
2014-12-18 09:14:46 +00:00
# include <stdlib.h>
2016-05-09 10:21:28 +00:00
# include <stdio.h>
2015-01-09 09:30:43 +00:00
# include <errno.h>
2017-05-01 02:20:19 +00:00
# include <unistd.h>
2015-01-09 09:30:43 +00:00
# include <fcntl.h>
# include <string.h>
# include <sys/stat.h>
2017-03-10 22:58:15 +00:00
# include <sys/file.h>
2015-01-09 09:30:43 +00:00
# include <sys/types.h>
2014-12-18 09:14:46 +00:00
# include <sys/utsname.h>
2017-05-22 15:01:14 +00:00
# include <sys/ioctl.h>
2014-12-18 09:14:46 +00:00
2015-03-23 10:36:48 +00:00
# include <glib.h>
# include "libglnx/libglnx.h"
# include <libsoup/soup.h>
2016-08-22 13:44:52 +00:00
# include <gio/gunixoutputstream.h>
2017-05-11 08:06:07 +00:00
# include <gio/gunixinputstream.h>
2015-03-23 10:36:48 +00:00
2016-05-12 14:33:48 +00:00
2017-02-09 11:08:12 +00:00
GFile *
flatpak_file_new_tmp_in ( GFile * dir ,
const char * template ,
GError * * error )
{
glnx_fd_close int tmp_fd = - 1 ;
g_autofree char * tmpl = g_build_filename ( flatpak_file_get_path_cached ( dir ) , template , NULL ) ;
tmp_fd = g_mkstemp_full ( tmpl , O_RDWR , 0644 ) ;
if ( tmp_fd = = - 1 )
{
glnx_set_error_from_errno ( error ) ;
return NULL ;
}
return g_file_new_for_path ( tmpl ) ;
}
2017-02-09 10:24:57 +00:00
gboolean
flatpak_write_update_checksum ( GOutputStream * out ,
gconstpointer data ,
gsize len ,
gsize * out_bytes_written ,
2017-12-07 14:40:04 +00:00
GChecksum * * checksums ,
gsize n_checksums ,
2017-02-09 10:24:57 +00:00
GCancellable * cancellable ,
GError * * error )
{
2017-12-07 14:40:04 +00:00
gsize i ;
2017-02-09 10:24:57 +00:00
if ( out )
{
if ( ! g_output_stream_write_all ( out , data , len , out_bytes_written ,
cancellable , error ) )
return FALSE ;
}
else if ( out_bytes_written )
{
* out_bytes_written = len ;
}
2017-12-07 14:40:04 +00:00
for ( i = 0 ; i < n_checksums ; i + + )
g_checksum_update ( checksums [ i ] , data , len ) ;
2017-02-09 10:24:57 +00:00
return TRUE ;
}
gboolean
flatpak_splice_update_checksum ( GOutputStream * out ,
GInputStream * in ,
2017-12-07 14:40:04 +00:00
GChecksum * * checksums ,
gsize n_checksums ,
2017-04-24 19:23:46 +00:00
FlatpakLoadUriProgress progress ,
gpointer progress_data ,
2017-02-09 10:24:57 +00:00
GCancellable * cancellable ,
GError * * error )
{
gsize bytes_read , bytes_written ;
char buf [ 32 * 1024 ] ;
2017-04-24 19:23:46 +00:00
guint64 downloaded_bytes = 0 ;
gint64 progress_start ;
2017-02-09 10:24:57 +00:00
2017-04-24 19:23:46 +00:00
progress_start = g_get_monotonic_time ( ) ;
2017-02-09 10:24:57 +00:00
do
{
if ( ! g_input_stream_read_all ( in , buf , sizeof buf , & bytes_read , cancellable , error ) )
return FALSE ;
2017-12-07 14:40:04 +00:00
if ( ! flatpak_write_update_checksum ( out , buf , bytes_read , & bytes_written ,
checksums , n_checksums ,
2017-02-09 10:24:57 +00:00
cancellable , error ) )
return FALSE ;
2017-04-24 19:23:46 +00:00
downloaded_bytes + = bytes_read ;
if ( progress & &
g_get_monotonic_time ( ) - progress_start > 5 * 1000000 )
{
progress ( downloaded_bytes , progress_data ) ;
progress_start = g_get_monotonic_time ( ) ;
}
2017-02-09 10:24:57 +00:00
}
while ( bytes_read > 0 ) ;
2017-04-24 19:23:46 +00:00
if ( progress )
progress ( downloaded_bytes , progress_data ) ;
2017-02-09 10:24:57 +00:00
return TRUE ;
}
2016-01-11 12:56:07 +00:00
/* Returns end of matching path prefix, or NULL if no match */
const char *
2016-05-06 14:37:47 +00:00
flatpak_path_match_prefix ( const char * pattern ,
2016-01-11 12:56:07 +00:00
const char * string )
{
char c , test ;
const char * tmp ;
while ( * pattern = = ' / ' )
pattern + + ;
while ( * string = = ' / ' )
string + + ;
while ( TRUE )
{
switch ( c = * pattern + + )
{
case 0 :
if ( * string = = ' / ' | | * string = = 0 )
return string ;
return NULL ;
case ' ? ' :
if ( * string = = ' / ' | | * string = = 0 )
return NULL ;
string + + ;
break ;
case ' * ' :
c = * pattern ;
while ( c = = ' * ' )
c = * + + pattern ;
/* special case * at end */
if ( c = = 0 )
{
char * tmp = strchr ( string , ' / ' ) ;
if ( tmp ! = NULL )
return tmp ;
return string + strlen ( string ) ;
}
else if ( c = = ' / ' )
{
string = strchr ( string , ' / ' ) ;
if ( string = = NULL )
return NULL ;
break ;
}
while ( ( test = * string ) ! = 0 )
{
2016-05-06 14:37:47 +00:00
tmp = flatpak_path_match_prefix ( pattern , string ) ;
2016-01-11 12:56:07 +00:00
if ( tmp ! = NULL )
return tmp ;
if ( test = = ' / ' )
break ;
string + + ;
}
return NULL ;
default :
if ( c ! = * string )
return NULL ;
string + + ;
break ;
}
}
return NULL ; /* Should not be reached */
}
2017-12-05 12:08:36 +00:00
# if !defined(__i386__) && !defined(__x86_64__) && !defined(__aarch64__) && !defined(__arm__)
2017-09-04 10:18:22 +00:00
static const char *
flatpak_get_kernel_arch ( void )
{
static struct utsname buf ;
static char * arch = NULL ;
char * m ;
if ( arch ! = NULL )
return arch ;
if ( uname ( & buf ) )
{
arch = " unknown " ;
return arch ;
}
/* By default, just pass on machine, good enough for most arches */
arch = buf . machine ;
/* Override for some arches */
m = buf . machine ;
/* i?86 */
if ( strlen ( m ) = = 4 & & m [ 0 ] = = ' i ' & & m [ 2 ] = = ' 8 ' & & m [ 3 ] = = ' 6 ' )
{
arch = " i386 " ;
}
else if ( g_str_has_prefix ( m , " arm " ) )
{
if ( g_str_has_suffix ( m , " b " ) )
arch = " armeb " ;
else
arch = " arm " ;
}
else if ( strcmp ( m , " mips " ) = = 0 )
{
# if G_BYTE_ORDER == G_LITTLE_ENDIAN
arch = " mipsel " ;
# endif
}
else if ( strcmp ( m , " mips64 " ) = = 0 )
{
# if G_BYTE_ORDER == G_LITTLE_ENDIAN
arch = " mips64el " ;
# endif
}
return arch ;
}
2017-12-05 12:08:36 +00:00
# endif /* !__i386__ && !__x86_64__ && !__aarch64__ && !__arm__ */
2014-12-18 09:14:46 +00:00
2016-05-04 07:00:12 +00:00
/* This maps the kernel-reported uname to a single string representing
* the cpu family , in the sense that all members of this family would
* be able to understand and link to a binary file with such cpu
* opcodes . That doesn ' t necessarily mean that all members of the
* family can run all opcodes , for instance for modern 32 bit intel we
* report " i386 " , even though they support instructions that the
* original i386 cpu cannot run . Still , such an executable would
2016-05-27 06:49:15 +00:00
* at least try to execute a 386 , whereas an arm binary would not .
2016-05-04 07:00:12 +00:00
*/
const char *
2016-05-06 14:37:47 +00:00
flatpak_get_arch ( void )
2016-05-04 07:00:12 +00:00
{
/* Avoid using uname on multiarch machines, because uname reports the kernels
* arch , and that may be different from userspace . If e . g . the kernel is 64 bit and
* the userspace is 32 bit we want to use 32 bit by default . So , we take the current build
* arch as the default . */
# if defined(__i386__)
return " i386 " ;
# elif defined(__x86_64__)
return " x86_64 " ;
# elif defined(__aarch64__)
return " aarch64 " ;
# elif defined(__arm__)
# if G_BYTE_ORDER == G_LITTLE_ENDIAN
return " arm " ;
# else
return " armeb " ;
# endif
2017-12-05 12:08:36 +00:00
# else
2017-09-06 17:16:23 +00:00
return flatpak_get_kernel_arch ( ) ;
2017-12-05 12:08:36 +00:00
# endif
2016-05-04 07:00:12 +00:00
}
2016-09-02 12:23:19 +00:00
gboolean
flatpak_is_in_sandbox ( void )
{
static gsize in_sandbox = 0 ;
if ( g_once_init_enter ( & in_sandbox ) )
{
g_autofree char * path = g_build_filename ( g_get_user_runtime_dir ( ) , " flatpak-info " , NULL ) ;
gsize new_in_sandbox ;
2016-06-23 08:53:07 +00:00
2016-09-02 12:23:19 +00:00
new_in_sandbox = 2 ;
if ( g_file_test ( path , G_FILE_TEST_IS_REGULAR ) )
new_in_sandbox = 1 ;
g_once_init_leave ( & in_sandbox , new_in_sandbox ) ;
}
return in_sandbox = = 1 ;
}
2016-06-23 08:53:07 +00:00
2017-02-15 18:22:14 +00:00
gboolean
2017-02-22 08:43:57 +00:00
flatpak_break_hardlink ( GFile * file , GError * * error )
2017-02-15 18:22:14 +00:00
{
g_autofree char * path = g_file_get_path ( file ) ;
struct stat st_buf ;
if ( stat ( path , & st_buf ) = = 0 & & st_buf . st_nlink > 1 )
{
g_autoptr ( GFile ) dir = g_file_get_parent ( file ) ;
g_autoptr ( GFile ) tmp = NULL ;
2017-02-22 08:43:57 +00:00
tmp = flatpak_file_new_tmp_in ( dir , " .breaklinkXXXXXX " , error ) ;
2017-02-15 18:22:14 +00:00
if ( tmp = = NULL )
return FALSE ;
if ( ! g_file_copy ( file , tmp ,
G_FILE_COPY_OVERWRITE ,
NULL , NULL , NULL , error ) )
return FALSE ;
if ( rename ( flatpak_file_get_path_cached ( tmp ) , path ) ! = 0 )
{
glnx_set_error_from_errno ( error ) ;
return FALSE ;
}
}
return TRUE ;
}
2015-02-11 11:31:53 +00:00
static gboolean
2016-10-06 08:53:52 +00:00
is_valid_initial_name_character ( gint c , gboolean allow_dash )
2015-02-11 11:31:53 +00:00
{
return
( c > = ' A ' & & c < = ' Z ' ) | |
( c > = ' a ' & & c < = ' z ' ) | |
2016-10-06 08:53:52 +00:00
( c = = ' _ ' ) | | ( allow_dash & & c = = ' - ' ) ;
2015-02-11 11:31:53 +00:00
}
static gboolean
2016-10-06 08:53:52 +00:00
is_valid_name_character ( gint c , gboolean allow_dash )
2015-02-11 11:31:53 +00:00
{
return
2016-10-06 08:53:52 +00:00
is_valid_initial_name_character ( c , allow_dash ) | |
2015-02-11 11:31:53 +00:00
( c > = ' 0 ' & & c < = ' 9 ' ) ;
}
2015-02-11 13:32:45 +00:00
gboolean
2016-05-06 14:37:47 +00:00
flatpak_has_name_prefix ( const char * string ,
2015-02-11 13:32:45 +00:00
const char * name )
{
const char * rest ;
if ( ! g_str_has_prefix ( string , name ) )
return FALSE ;
rest = string + strlen ( name ) ;
return
* rest = = 0 | |
* rest = = ' . ' | |
2016-10-06 08:53:52 +00:00
! is_valid_name_character ( * rest , FALSE ) ;
2015-02-11 13:32:45 +00:00
}
2015-02-11 11:31:53 +00:00
2017-05-30 16:07:23 +00:00
/* Dashes are only valid in the last part of the app id, so
we replace them with underscore so we can suffix the id */
char *
flatpak_make_valid_id_prefix ( const char * orig_id )
{
char * id , * t ;
id = g_strdup ( orig_id ) ;
t = id ;
while ( * t ! = 0 & & * t ! = ' / ' )
{
if ( * t = = ' - ' )
* t = ' _ ' ;
t + + ;
}
return id ;
}
2017-08-24 13:48:55 +00:00
char *
flatpak_compose_ref ( gboolean app ,
const char * name ,
const char * branch ,
2017-12-12 11:35:35 +00:00
const char * arch )
2017-05-30 16:07:23 +00:00
{
2017-08-24 13:48:55 +00:00
if ( app )
return flatpak_build_app_ref ( name , branch , arch ) ;
else
return flatpak_build_runtime_ref ( name , branch , arch ) ;
2017-05-30 16:07:23 +00:00
}
2017-08-24 13:48:55 +00:00
char *
flatpak_build_untyped_ref ( const char * runtime ,
const char * branch ,
const char * arch )
2015-10-16 13:47:34 +00:00
{
2017-08-24 13:48:55 +00:00
if ( arch = = NULL )
arch = flatpak_get_arch ( ) ;
2015-10-16 13:47:34 +00:00
2017-08-24 13:48:55 +00:00
return g_build_filename ( runtime , arch , branch , NULL ) ;
}
2015-10-16 13:47:34 +00:00
2017-08-24 13:48:55 +00:00
char *
flatpak_build_runtime_ref ( const char * runtime ,
const char * branch ,
const char * arch )
{
if ( branch = = NULL )
branch = " master " ;
2015-10-16 13:47:34 +00:00
2017-08-24 13:48:55 +00:00
if ( arch = = NULL )
arch = flatpak_get_arch ( ) ;
2015-10-16 13:47:34 +00:00
2017-08-24 13:48:55 +00:00
return g_build_filename ( " runtime " , runtime , arch , branch , NULL ) ;
}
2014-12-18 09:14:46 +00:00
char *
2016-05-06 14:37:47 +00:00
flatpak_build_app_ref ( const char * app ,
2014-12-18 09:14:46 +00:00
const char * branch ,
const char * arch )
{
2015-12-17 19:33:02 +00:00
if ( branch = = NULL )
branch = " master " ;
2014-12-18 09:14:46 +00:00
if ( arch = = NULL )
2016-05-06 14:37:47 +00:00
arch = flatpak_get_arch ( ) ;
2014-12-18 09:14:46 +00:00
return g_build_filename ( " app " , app , arch , branch , NULL ) ;
}
2015-01-08 16:08:59 +00:00
2017-08-24 13:48:55 +00:00
typedef struct
2015-05-12 08:33:16 +00:00
{
2017-08-24 13:48:55 +00:00
GError * error ;
GError * splice_error ;
GMainLoop * loop ;
int refs ;
} SpawnData ;
2015-05-12 08:33:16 +00:00
2017-08-24 13:48:55 +00:00
static void
spawn_data_exit ( SpawnData * data )
2015-12-03 12:29:08 +00:00
{
data - > refs - - ;
if ( data - > refs = = 0 )
g_main_loop_quit ( data - > loop ) ;
}
2017-08-24 13:48:55 +00:00
static void
spawn_output_spliced_cb ( GObject * obj ,
GAsyncResult * result ,
gpointer user_data )
2016-01-21 20:37:29 +00:00
{
2017-08-24 13:48:55 +00:00
SpawnData * data = user_data ;
2016-01-21 20:37:29 +00:00
2017-08-24 13:48:55 +00:00
g_output_stream_splice_finish ( G_OUTPUT_STREAM ( obj ) , result , & data - > splice_error ) ;
spawn_data_exit ( data ) ;
2016-01-21 20:37:29 +00:00
}
2017-08-24 13:48:55 +00:00
static void
spawn_exit_cb ( GObject * obj ,
GAsyncResult * result ,
gpointer user_data )
2016-01-21 20:37:29 +00:00
{
2017-08-24 13:48:55 +00:00
SpawnData * data = user_data ;
2016-01-21 20:37:29 +00:00
2017-08-24 13:48:55 +00:00
g_subprocess_wait_check_finish ( G_SUBPROCESS ( obj ) , result , & data - > error ) ;
spawn_data_exit ( data ) ;
2016-01-21 20:37:29 +00:00
}
2017-08-24 13:48:55 +00:00
static gboolean
needs_quoting ( const char * arg )
2016-01-21 20:37:29 +00:00
{
2017-08-24 13:48:55 +00:00
while ( * arg ! = 0 )
2016-05-06 14:03:27 +00:00
{
2017-08-24 13:48:55 +00:00
char c = * arg ;
if ( ! g_ascii_isalnum ( c ) & &
! ( c = = ' - ' | | c = = ' / ' | | c = = ' ~ ' | |
c = = ' : ' | | c = = ' . ' | | c = = ' _ ' | |
c = = ' = ' ) )
return TRUE ;
arg + + ;
2016-05-06 14:03:27 +00:00
}
2017-08-24 13:48:55 +00:00
return FALSE ;
}
2016-01-21 20:37:29 +00:00
2017-08-24 13:48:55 +00:00
char *
flatpak_quote_argv ( const char * argv [ ] )
{
GString * res = g_string_new ( " " ) ;
int i ;
2016-01-21 20:37:29 +00:00
2017-08-24 13:48:55 +00:00
for ( i = 0 ; argv [ i ] ! = NULL ; i + + )
2016-01-21 20:37:29 +00:00
{
2017-08-24 13:48:55 +00:00
if ( i ! = 0 )
g_string_append_c ( res , ' ' ) ;
2016-02-24 13:16:09 +00:00
2017-08-24 13:48:55 +00:00
if ( needs_quoting ( argv [ i ] ) )
{
g_autofree char * quoted = g_shell_quote ( argv [ i ] ) ;
g_string_append ( res , quoted ) ;
}
else
g_string_append ( res , argv [ i ] ) ;
}
2016-02-24 13:16:09 +00:00
2017-08-24 13:48:55 +00:00
return g_string_free ( res , FALSE ) ;
2016-03-24 10:04:23 +00:00
}
2017-08-24 13:48:55 +00:00
gboolean
flatpak_spawn ( GFile * dir ,
char * * output ,
2017-10-12 14:19:54 +00:00
GSubprocessFlags flags ,
2017-08-24 13:48:55 +00:00
GError * * error ,
const gchar * argv0 ,
va_list ap )
2016-02-25 20:34:44 +00:00
{
2017-08-24 13:48:55 +00:00
GPtrArray * args ;
const gchar * arg ;
gboolean res ;
2016-02-25 20:34:44 +00:00
2017-08-24 13:48:55 +00:00
args = g_ptr_array_new ( ) ;
g_ptr_array_add ( args , ( gchar * ) argv0 ) ;
while ( ( arg = va_arg ( ap , const gchar * ) ) )
g_ptr_array_add ( args , ( gchar * ) arg ) ;
g_ptr_array_add ( args , NULL ) ;
2016-02-25 20:34:44 +00:00
2017-10-12 14:19:54 +00:00
res = flatpak_spawnv ( dir , output , flags , error , ( const gchar * const * ) args - > pdata ) ;
2016-02-25 20:34:44 +00:00
2017-08-24 13:48:55 +00:00
g_ptr_array_free ( args , TRUE ) ;
2016-02-25 20:34:44 +00:00
2017-08-24 13:48:55 +00:00
return res ;
2016-02-25 20:34:44 +00:00
}
2017-08-24 13:48:55 +00:00
gboolean
flatpak_spawnv ( GFile * dir ,
char * * output ,
GSubprocessFlags flags ,
GError * * error ,
const gchar * const * argv )
2016-02-24 13:16:09 +00:00
{
2017-08-24 13:48:55 +00:00
g_autoptr ( GSubprocessLauncher ) launcher = NULL ;
g_autoptr ( GSubprocess ) subp = NULL ;
GInputStream * in ;
g_autoptr ( GOutputStream ) out = NULL ;
g_autoptr ( GMainLoop ) loop = NULL ;
SpawnData data = { 0 } ;
g_autofree gchar * commandline = NULL ;
2016-02-24 13:16:09 +00:00
2017-08-24 13:48:55 +00:00
launcher = g_subprocess_launcher_new ( 0 ) ;
2016-02-24 13:16:09 +00:00
2018-05-07 13:04:58 +00:00
g_subprocess_launcher_setenv ( launcher , " LANGUAGE " , " C " , TRUE ) ;
2017-08-24 13:48:55 +00:00
if ( output )
flags | = G_SUBPROCESS_FLAGS_STDOUT_PIPE ;
2016-02-24 13:16:09 +00:00
2017-08-24 13:48:55 +00:00
g_subprocess_launcher_set_flags ( launcher , flags ) ;
2016-02-24 13:16:09 +00:00
2017-08-24 13:48:55 +00:00
if ( dir )
2016-03-24 10:04:23 +00:00
{
2017-08-24 13:48:55 +00:00
g_autofree char * path = g_file_get_path ( dir ) ;
g_subprocess_launcher_set_cwd ( launcher , path ) ;
}
2016-05-06 14:03:27 +00:00
2017-08-24 13:48:55 +00:00
commandline = flatpak_quote_argv ( ( const char * * ) argv ) ;
g_debug ( " Running: %s " , commandline ) ;
2016-05-06 14:03:27 +00:00
2017-08-24 13:48:55 +00:00
subp = g_subprocess_launcher_spawnv ( launcher , argv , error ) ;
2016-03-24 10:04:23 +00:00
2017-08-24 13:48:55 +00:00
if ( subp = = NULL )
return FALSE ;
2016-10-06 10:25:55 +00:00
2017-08-24 13:48:55 +00:00
loop = g_main_loop_new ( NULL , FALSE ) ;
2016-03-24 10:04:23 +00:00
2017-08-24 13:48:55 +00:00
data . loop = loop ;
data . refs = 1 ;
2016-02-25 15:05:13 +00:00
2017-08-24 13:48:55 +00:00
if ( output )
2016-02-25 15:05:13 +00:00
{
2017-08-24 13:48:55 +00:00
data . refs + + ;
in = g_subprocess_get_stdout_pipe ( subp ) ;
out = g_memory_output_stream_new_resizable ( ) ;
g_output_stream_splice_async ( out ,
in ,
G_OUTPUT_STREAM_SPLICE_NONE ,
0 ,
NULL ,
spawn_output_spliced_cb ,
& data ) ;
2016-02-25 15:05:13 +00:00
}
2017-08-24 13:48:55 +00:00
g_subprocess_wait_async ( subp , NULL , spawn_exit_cb , & data ) ;
2016-12-20 15:24:13 +00:00
2017-08-24 13:48:55 +00:00
g_main_loop_run ( loop ) ;
2017-06-30 14:18:50 +00:00
2017-08-24 13:48:55 +00:00
if ( data . error )
2016-12-20 15:24:13 +00:00
{
2017-08-24 13:48:55 +00:00
g_propagate_error ( error , data . error ) ;
g_clear_error ( & data . splice_error ) ;
return FALSE ;
2016-12-20 15:24:13 +00:00
}
2017-08-24 13:48:55 +00:00
if ( out )
2016-02-25 15:05:13 +00:00
{
2017-08-24 13:48:55 +00:00
if ( data . splice_error )
2016-05-06 14:03:27 +00:00
{
2017-08-24 13:48:55 +00:00
g_propagate_error ( error , data . splice_error ) ;
return FALSE ;
2016-05-06 14:03:27 +00:00
}
2017-08-24 13:48:55 +00:00
/* Null terminate */
g_output_stream_write ( out , " \0 " , 1 , NULL , NULL ) ;
g_output_stream_close ( out , NULL , NULL ) ;
* output = g_memory_output_stream_steal_data ( G_MEMORY_OUTPUT_STREAM ( out ) ) ;
2016-02-25 15:05:13 +00:00
}
2017-08-24 13:48:55 +00:00
return TRUE ;
2016-02-24 13:16:09 +00:00
}
2016-04-19 11:31:55 +00:00
2017-08-24 13:48:55 +00:00
GFile *
flatpak_build_file_va ( GFile * base ,
va_list args )
2016-04-19 11:31:55 +00:00
{
2017-08-24 13:48:55 +00:00
g_autoptr ( GFile ) res = g_object_ref ( base ) ;
const gchar * arg ;
2016-04-19 11:31:55 +00:00
2017-08-24 13:48:55 +00:00
while ( ( arg = va_arg ( args , const gchar * ) ) )
2016-04-19 11:31:55 +00:00
{
2017-08-24 13:48:55 +00:00
GFile * child = g_file_resolve_relative_path ( res , arg ) ;
g_set_object ( & res , child ) ;
2016-04-19 11:31:55 +00:00
}
2017-08-24 13:48:55 +00:00
return g_steal_pointer ( & res ) ;
}
2016-04-19 11:31:55 +00:00
2017-08-24 13:48:55 +00:00
GFile *
flatpak_build_file ( GFile * base , . . . )
{
GFile * res ;
va_list args ;
2016-04-19 11:31:55 +00:00
2017-08-24 13:48:55 +00:00
va_start ( args , base ) ;
res = flatpak_build_file_va ( base , args ) ;
va_end ( args ) ;
2016-04-19 11:31:55 +00:00
2017-08-24 13:48:55 +00:00
return res ;
}
2016-04-19 11:31:55 +00:00
2017-08-24 13:48:55 +00:00
const char *
flatpak_file_get_path_cached ( GFile * file )
{
const char * path ;
static GQuark _file_path_quark = 0 ;
2016-04-19 11:31:55 +00:00
2017-08-24 13:48:55 +00:00
if ( G_UNLIKELY ( _file_path_quark = = 0 ) )
_file_path_quark = g_quark_from_static_string ( " flatpak-file-path " ) ;
2016-04-19 11:31:55 +00:00
2017-08-24 13:48:55 +00:00
do
2016-04-19 11:31:55 +00:00
{
2017-08-24 13:48:55 +00:00
path = g_object_get_qdata ( ( GObject * ) file , _file_path_quark ) ;
if ( path = = NULL )
{
g_autofree char * new_path = NULL ;
new_path = g_file_get_path ( file ) ;
if ( new_path = = NULL )
return NULL ;
2017-03-08 17:53:38 +00:00
2017-08-24 13:48:55 +00:00
if ( g_object_replace_qdata ( ( GObject * ) file , _file_path_quark ,
NULL , new_path , g_free , NULL ) )
path = g_steal_pointer ( & new_path ) ;
}
}
while ( path = = NULL ) ;
2017-03-08 17:53:38 +00:00
2017-08-24 13:48:55 +00:00
return path ;
2017-03-08 17:53:38 +00:00
}
2017-03-28 17:23:51 +00:00
gboolean
2017-08-24 13:48:55 +00:00
flatpak_cp_a ( GFile * src ,
GFile * dest ,
FlatpakCpFlags flags ,
2018-04-25 15:28:06 +00:00
GPtrArray * skip_files ,
2017-08-24 13:48:55 +00:00
GCancellable * cancellable ,
GError * * error )
2017-03-28 17:23:51 +00:00
{
2017-08-24 13:48:55 +00:00
gboolean ret = FALSE ;
GFileEnumerator * enumerator = NULL ;
GFileInfo * src_info = NULL ;
GFile * dest_child = NULL ;
int dest_dfd = - 1 ;
gboolean merge = ( flags & FLATPAK_CP_FLAGS_MERGE ) ! = 0 ;
gboolean no_chown = ( flags & FLATPAK_CP_FLAGS_NO_CHOWN ) ! = 0 ;
gboolean move = ( flags & FLATPAK_CP_FLAGS_MOVE ) ! = 0 ;
g_autoptr ( GFileInfo ) child_info = NULL ;
GError * temp_error = NULL ;
int r ;
2017-03-28 17:23:51 +00:00
2017-08-24 13:48:55 +00:00
enumerator = g_file_enumerate_children ( src , " standard::type,standard::name,unix::uid,unix::gid,unix::mode " ,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS ,
cancellable , error ) ;
if ( ! enumerator )
goto out ;
2017-03-29 15:09:24 +00:00
2017-08-24 13:48:55 +00:00
src_info = g_file_query_info ( src , " standard::name,unix::mode,unix::uid,unix::gid, " \
" time::modified,time::modified-usec,time::access,time::access-usec " ,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS ,
cancellable , error ) ;
if ( ! src_info )
goto out ;
2017-03-28 17:23:51 +00:00
2017-08-24 13:48:55 +00:00
do
r = mkdir ( flatpak_file_get_path_cached ( dest ) , 0755 ) ;
while ( G_UNLIKELY ( r = = - 1 & & errno = = EINTR ) ) ;
if ( r = = - 1 & &
( ! merge | | errno ! = EEXIST ) )
2017-03-28 17:23:51 +00:00
{
2017-08-24 13:48:55 +00:00
glnx_set_error_from_errno ( error ) ;
goto out ;
2017-03-28 17:23:51 +00:00
}
2017-08-24 13:48:55 +00:00
if ( ! glnx_opendirat ( AT_FDCWD , flatpak_file_get_path_cached ( dest ) , TRUE ,
& dest_dfd , error ) )
goto out ;
2017-03-28 17:23:51 +00:00
2017-08-24 13:48:55 +00:00
if ( ! no_chown )
2017-03-28 17:23:51 +00:00
{
2017-08-24 13:48:55 +00:00
do
r = fchown ( dest_dfd ,
g_file_info_get_attribute_uint32 ( src_info , " unix::uid " ) ,
g_file_info_get_attribute_uint32 ( src_info , " unix::gid " ) ) ;
while ( G_UNLIKELY ( r = = - 1 & & errno = = EINTR ) ) ;
if ( r = = - 1 )
{
glnx_set_error_from_errno ( error ) ;
goto out ;
}
2017-03-28 17:23:51 +00:00
}
2017-08-24 13:48:55 +00:00
do
r = fchmod ( dest_dfd , g_file_info_get_attribute_uint32 ( src_info , " unix::mode " ) ) ;
while ( G_UNLIKELY ( r = = - 1 & & errno = = EINTR ) ) ;
2017-03-28 17:23:51 +00:00
2017-08-24 13:48:55 +00:00
if ( dest_dfd ! = - 1 )
2017-03-28 17:23:51 +00:00
{
2017-08-24 13:48:55 +00:00
( void ) close ( dest_dfd ) ;
dest_dfd = - 1 ;
2017-03-28 17:23:51 +00:00
}
2017-08-24 13:48:55 +00:00
while ( ( child_info = g_file_enumerator_next_file ( enumerator , cancellable , & temp_error ) ) )
2017-03-29 15:09:24 +00:00
{
2017-08-24 13:48:55 +00:00
const char * name = g_file_info_get_name ( child_info ) ;
g_autoptr ( GFile ) src_child = g_file_get_child ( src , name ) ;
2018-04-25 15:28:06 +00:00
gboolean skip = FALSE ;
int i ;
for ( i = 0 ; skip_files ! = NULL & & i < skip_files - > len ; i + + )
{
if ( g_file_equal ( src_child , g_ptr_array_index ( skip_files , i ) ) )
{
skip = TRUE ;
break ;
}
}
2017-03-29 15:09:24 +00:00
2017-08-24 13:48:55 +00:00
if ( dest_child )
g_object_unref ( dest_child ) ;
dest_child = g_file_get_child ( dest , name ) ;
2017-03-29 15:09:24 +00:00
2018-04-25 15:28:06 +00:00
if ( skip )
{
/* skip src */
}
else if ( g_file_info_get_file_type ( child_info ) = = G_FILE_TYPE_DIRECTORY )
2017-03-29 15:09:24 +00:00
{
2018-04-25 15:28:06 +00:00
if ( ! flatpak_cp_a ( src_child , dest_child , flags , skip_files ,
2017-08-24 13:48:55 +00:00
cancellable , error ) )
goto out ;
2017-03-29 15:09:24 +00:00
}
2017-08-24 13:48:55 +00:00
else
2017-03-29 15:09:24 +00:00
{
2017-08-24 13:48:55 +00:00
( void ) unlink ( flatpak_file_get_path_cached ( dest_child ) ) ;
GFileCopyFlags copyflags = G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS ;
if ( ! no_chown )
copyflags | = G_FILE_COPY_ALL_METADATA ;
if ( move )
{
if ( ! g_file_move ( src_child , dest_child , copyflags ,
cancellable , NULL , NULL , error ) )
goto out ;
}
else
{
if ( ! g_file_copy ( src_child , dest_child , copyflags ,
cancellable , NULL , NULL , error ) )
goto out ;
}
2017-03-29 15:09:24 +00:00
}
2017-08-24 13:48:55 +00:00
g_clear_object ( & child_info ) ;
2017-03-28 13:16:18 +00:00
}
2017-08-24 13:48:55 +00:00
if ( temp_error ! = NULL )
2017-03-28 13:16:18 +00:00
{
2017-08-24 13:48:55 +00:00
g_propagate_error ( error , temp_error ) ;
goto out ;
2017-03-28 13:16:18 +00:00
}
2016-12-09 13:44:25 +00:00
2017-08-24 13:48:55 +00:00
if ( move & &
! g_file_delete ( src , NULL , error ) )
goto out ;
2016-12-12 11:26:37 +00:00
2017-08-24 13:48:55 +00:00
ret = TRUE ;
out :
if ( dest_dfd ! = - 1 )
( void ) close ( dest_dfd ) ;
g_clear_object ( & src_info ) ;
g_clear_object ( & enumerator ) ;
g_clear_object ( & dest_child ) ;
return ret ;
}
2016-12-09 13:44:25 +00:00
2017-08-24 13:48:55 +00:00
gboolean
flatpak_zero_mtime ( int parent_dfd ,
const char * rel_path ,
GCancellable * cancellable ,
GError * * error )
{
struct stat stbuf ;
2016-12-09 13:44:25 +00:00
2017-08-24 13:48:55 +00:00
if ( TEMP_FAILURE_RETRY ( fstatat ( parent_dfd , rel_path , & stbuf , AT_SYMLINK_NOFOLLOW ) ) ! = 0 )
2017-03-08 17:53:38 +00:00
{
2017-08-24 13:48:55 +00:00
glnx_set_error_from_errno ( error ) ;
return FALSE ;
2017-03-08 17:53:38 +00:00
}
2017-08-24 13:48:55 +00:00
if ( S_ISDIR ( stbuf . st_mode ) )
2016-12-09 13:44:25 +00:00
{
2017-08-24 13:48:55 +00:00
g_auto ( GLnxDirFdIterator ) dfd_iter = { 0 , } ;
gboolean inited ;
2016-12-09 13:44:25 +00:00
2017-08-24 13:48:55 +00:00
inited = glnx_dirfd_iterator_init_at ( parent_dfd , rel_path , FALSE , & dfd_iter , NULL ) ;
2016-12-09 13:44:25 +00:00
2017-08-24 13:48:55 +00:00
while ( inited )
2016-12-09 13:44:25 +00:00
{
2017-08-24 13:48:55 +00:00
struct dirent * dent ;
if ( ! glnx_dirfd_iterator_next_dent ( & dfd_iter , & dent , NULL , NULL ) | | dent = = NULL )
break ;
if ( ! flatpak_zero_mtime ( dfd_iter . fd , dent - > d_name ,
cancellable , error ) )
return FALSE ;
2016-12-09 13:44:25 +00:00
}
2017-03-08 17:53:38 +00:00
2017-08-24 13:48:55 +00:00
/* Update stbuf */
if ( TEMP_FAILURE_RETRY ( fstat ( dfd_iter . fd , & stbuf ) ) ! = 0 )
2017-03-28 13:17:37 +00:00
{
2017-08-24 13:48:55 +00:00
glnx_set_error_from_errno ( error ) ;
return FALSE ;
2017-03-28 13:17:37 +00:00
}
2016-12-09 13:44:25 +00:00
}
2017-08-24 13:48:55 +00:00
/* OSTree checks out to mtime 0, so we do the same */
if ( stbuf . st_mtime ! = OSTREE_TIMESTAMP )
{
const struct timespec times [ 2 ] = { { 0 , UTIME_OMIT } , { OSTREE_TIMESTAMP , } } ;
2016-12-09 13:44:25 +00:00
2017-08-24 13:48:55 +00:00
if ( TEMP_FAILURE_RETRY ( utimensat ( parent_dfd , rel_path , times , AT_SYMLINK_NOFOLLOW ) ) ! = 0 )
{
glnx_set_error_from_errno ( error ) ;
return FALSE ;
}
}
2016-12-09 13:44:25 +00:00
2017-08-24 13:48:55 +00:00
return TRUE ;
}
2016-12-09 13:44:25 +00:00
2017-08-24 13:48:55 +00:00
/* Make a directory, and its parent. Don't error if it already exists.
* If you want a failure mode with EEXIST , use g_file_make_directory_with_parents . */
gboolean
flatpak_mkdir_p ( GFile * dir ,
GCancellable * cancellable ,
GError * * error )
{
return glnx_shutil_mkdir_p_at ( AT_FDCWD ,
flatpak_file_get_path_cached ( dir ) ,
0777 ,
cancellable ,
error ) ;
}
2016-12-09 13:44:25 +00:00
2017-08-24 13:48:55 +00:00
gboolean
flatpak_rm_rf ( GFile * dir ,
GCancellable * cancellable ,
GError * * error )
{
return glnx_shutil_rm_rf_at ( AT_FDCWD ,
flatpak_file_get_path_cached ( dir ) ,
cancellable , error ) ;
2016-12-09 13:44:25 +00:00
}
2018-01-11 08:36:00 +00:00
gboolean flatpak_file_rename ( GFile * from ,
GFile * to ,
GCancellable * cancellable ,
GError * * error )
{
if ( g_cancellable_set_error_if_cancelled ( cancellable , error ) )
return FALSE ;
if ( rename ( flatpak_file_get_path_cached ( from ) ,
flatpak_file_get_path_cached ( to ) ) < 0 )
{
glnx_set_error_from_errno ( error ) ;
return FALSE ;
}
return TRUE ;
}
2017-08-24 13:48:55 +00:00
# define OSTREE_GIO_FAST_QUERYINFO ("standard::name,standard::type,standard::size,standard::is-symlink,standard::symlink-target," \
" unix::device,unix::inode,unix::mode,unix::uid,unix::gid,unix::rdev " )
2016-04-22 08:37:06 +00:00
/* This allocates and locks a subdir of the tmp dir, using an existing
* one with the same prefix if it is not in use already . */
gboolean
2016-05-06 14:37:47 +00:00
flatpak_allocate_tmpdir ( int tmpdir_dfd ,
2016-05-06 14:03:27 +00:00
const char * tmpdir_relpath ,
const char * tmpdir_prefix ,
char * * tmpdir_name_out ,
int * tmpdir_fd_out ,
2016-04-22 08:37:06 +00:00
GLnxLockFile * file_lock_out ,
2016-05-06 14:03:27 +00:00
gboolean * reusing_dir_out ,
2016-04-22 08:37:06 +00:00
GCancellable * cancellable ,
2016-05-06 14:03:27 +00:00
GError * * error )
2016-04-22 08:37:06 +00:00
{
gboolean reusing_dir = FALSE ;
g_autofree char * tmpdir_name = NULL ;
glnx_fd_close int tmpdir_fd = - 1 ;
2016-05-06 14:03:27 +00:00
2016-04-22 08:37:06 +00:00
g_auto ( GLnxDirFdIterator ) dfd_iter = { 0 , } ;
/* Look for existing tmpdir (with same prefix) to reuse */
if ( ! glnx_dirfd_iterator_init_at ( tmpdir_dfd , tmpdir_relpath ? tmpdir_relpath : " . " , FALSE , & dfd_iter , error ) )
return FALSE ;
while ( tmpdir_name = = NULL )
{
struct dirent * dent ;
glnx_fd_close int existing_tmpdir_fd = - 1 ;
g_autoptr ( GError ) local_error = NULL ;
g_autofree char * lock_name = NULL ;
if ( ! glnx_dirfd_iterator_next_dent ( & dfd_iter , & dent , cancellable , error ) )
return FALSE ;
if ( dent = = NULL )
break ;
if ( ! g_str_has_prefix ( dent - > d_name , tmpdir_prefix ) )
continue ;
/* Quickly skip non-dirs, if unknown we ignore ENOTDIR when opening instead */
if ( dent - > d_type ! = DT_UNKNOWN & &
dent - > d_type ! = DT_DIR )
continue ;
if ( ! glnx_opendirat ( dfd_iter . fd , dent - > d_name , FALSE ,
& existing_tmpdir_fd , & local_error ) )
{
if ( g_error_matches ( local_error , G_IO_ERROR , G_IO_ERROR_NOT_DIRECTORY ) )
2016-05-06 14:03:27 +00:00
{
continue ;
}
2016-04-22 08:37:06 +00:00
else
{
g_propagate_error ( error , g_steal_pointer ( & local_error ) ) ;
return FALSE ;
}
}
lock_name = g_strconcat ( dent - > d_name , " -lock " , NULL ) ;
/* We put the lock outside the dir, so we can hold the lock
* until the directory is fully removed */
if ( ! glnx_make_lock_file ( dfd_iter . fd , lock_name , LOCK_EX | LOCK_NB ,
file_lock_out , & local_error ) )
{
if ( g_error_matches ( local_error , G_IO_ERROR , G_IO_ERROR_WOULD_BLOCK ) )
2016-05-06 14:03:27 +00:00
{
continue ;
}
2016-04-22 08:37:06 +00:00
else
{
g_propagate_error ( error , g_steal_pointer ( & local_error ) ) ;
return FALSE ;
}
}
/* Touch the reused directory so that we don't accidentally
* remove it due to being old when cleaning up the tmpdir
*/
2016-05-06 14:03:27 +00:00
( void ) futimens ( existing_tmpdir_fd , NULL ) ;
2016-04-22 08:37:06 +00:00
/* We found an existing tmpdir which we managed to lock */
tmpdir_name = g_strdup ( dent - > d_name ) ;
tmpdir_fd = glnx_steal_fd ( & existing_tmpdir_fd ) ;
reusing_dir = TRUE ;
}
while ( tmpdir_name = = NULL )
{
g_autofree char * tmpdir_name_template = g_strconcat ( tmpdir_prefix , " XXXXXX " , NULL ) ;
g_autoptr ( GError ) local_error = NULL ;
g_autofree char * lock_name = NULL ;
2018-02-14 17:29:11 +00:00
g_auto ( GLnxTmpDir ) new_tmpdir = { 0 , } ;
2016-04-22 08:37:06 +00:00
/* No existing tmpdir found, create a new */
2018-02-14 17:29:11 +00:00
if ( ! glnx_mkdtempat ( dfd_iter . fd , tmpdir_name_template , 0777 ,
& new_tmpdir , error ) )
2016-04-22 08:37:06 +00:00
return FALSE ;
2018-02-14 17:29:11 +00:00
lock_name = g_strconcat ( new_tmpdir . path , " -lock " , NULL ) ;
2016-04-22 08:37:06 +00:00
/* Note, at this point we can race with another process that picks up this
* new directory . If that happens we need to retry , making a new directory . */
2016-06-01 08:22:55 +00:00
if ( ! glnx_make_lock_file ( dfd_iter . fd , lock_name , LOCK_EX | LOCK_NB ,
2016-04-22 08:37:06 +00:00
file_lock_out , & local_error ) )
{
if ( g_error_matches ( local_error , G_IO_ERROR , G_IO_ERROR_WOULD_BLOCK ) )
2016-05-06 14:03:27 +00:00
{
2018-02-14 17:29:11 +00:00
glnx_tmpdir_unset ( & new_tmpdir ) ; /* Don't delete */
2016-05-06 14:03:27 +00:00
continue ;
}
2016-04-22 08:37:06 +00:00
else
{
g_propagate_error ( error , g_steal_pointer ( & local_error ) ) ;
return FALSE ;
}
}
2018-02-14 17:29:11 +00:00
tmpdir_name = g_strdup ( new_tmpdir . path ) ;
tmpdir_fd = dup ( new_tmpdir . fd ) ;
glnx_tmpdir_unset ( & new_tmpdir ) ; /* Don't delete */
2017-08-24 13:48:55 +00:00
}
2016-10-18 14:15:44 +00:00
2017-08-24 13:48:55 +00:00
if ( tmpdir_name_out )
* tmpdir_name_out = g_steal_pointer ( & tmpdir_name ) ;
2016-10-18 14:15:44 +00:00
2017-08-24 13:48:55 +00:00
if ( tmpdir_fd_out )
* tmpdir_fd_out = glnx_steal_fd ( & tmpdir_fd ) ;
2016-10-18 14:15:44 +00:00
2017-08-24 13:48:55 +00:00
if ( reusing_dir_out )
* reusing_dir_out = reusing_dir ;
return TRUE ;
2016-10-18 14:15:44 +00:00
}
2016-12-08 13:23:48 +00:00
SoupSession *
flatpak_create_soup_session ( const char * user_agent )
{
SoupSession * soup_session ;
const char * http_proxy ;
soup_session = soup_session_new_with_options ( SOUP_SESSION_USER_AGENT , user_agent ,
SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE , TRUE ,
SOUP_SESSION_USE_THREAD_CONTEXT , TRUE ,
SOUP_SESSION_TIMEOUT , 60 ,
SOUP_SESSION_IDLE_TIMEOUT , 60 ,
NULL ) ;
2016-12-08 15:38:49 +00:00
soup_session_remove_feature_by_type ( soup_session , SOUP_TYPE_CONTENT_DECODER ) ;
2016-12-08 13:23:48 +00:00
http_proxy = g_getenv ( " http_proxy " ) ;
if ( http_proxy )
{
g_autoptr ( SoupURI ) proxy_uri = soup_uri_new ( http_proxy ) ;
if ( ! proxy_uri )
g_warning ( " Invalid proxy URI '%s' " , http_proxy ) ;
else
g_object_set ( soup_session , SOUP_SESSION_PROXY_URI , proxy_uri , NULL ) ;
}
return soup_session ;
}
2018-05-03 07:28:27 +00:00
CURL *
flatpak_create_curl_session ( const char * user_agent )
{
CURL * curl_session ;
curl_global_init ( CURL_GLOBAL_DEFAULT ) ;
curl_session = curl_easy_init ( ) ;
if ( curl_session = = NULL )
return NULL ;
curl_easy_setopt ( curl_session , CURLOPT_CONNECTTIMEOUT , 60 ) ;
2018-05-27 17:48:54 +00:00
curl_easy_setopt ( curl_session , CURLOPT_FAILONERROR , 1 ) ;
2018-05-15 09:44:24 +00:00
curl_easy_setopt ( curl_session , CURLOPT_FOLLOWLOCATION , 1 ) ;
curl_easy_setopt ( curl_session , CURLOPT_MAXREDIRS , 50 ) ;
2018-05-03 07:28:27 +00:00
curl_easy_setopt ( curl_session , CURLOPT_NOPROGRESS , 0 ) ;
curl_easy_setopt ( curl_session , CURLOPT_USERAGENT , user_agent ) ;
return curl_session ;
}
2017-08-24 13:48:55 +00:00
typedef enum {
FLATPAK_POLICY_NONE ,
FLATPAK_POLICY_SEE ,
FLATPAK_POLICY_TALK ,
FLATPAK_POLICY_OWN
} FlatpakPolicy ;
typedef enum {
FLATPAK_CONTEXT_SHARED_NETWORK = 1 < < 0 ,
FLATPAK_CONTEXT_SHARED_IPC = 1 < < 1 ,
} FlatpakContextShares ;
/* In numerical order of more privs */
typedef enum {
FLATPAK_FILESYSTEM_MODE_READ_ONLY = 1 ,
FLATPAK_FILESYSTEM_MODE_READ_WRITE = 2 ,
FLATPAK_FILESYSTEM_MODE_CREATE = 3 ,
} FlatpakFilesystemMode ;
/* Same order as enum */
const char * flatpak_context_shares [ ] = {
" network " ,
" ipc " ,
NULL
} ;
typedef enum {
FLATPAK_CONTEXT_SOCKET_X11 = 1 < < 0 ,
FLATPAK_CONTEXT_SOCKET_WAYLAND = 1 < < 1 ,
FLATPAK_CONTEXT_SOCKET_PULSEAUDIO = 1 < < 2 ,
FLATPAK_CONTEXT_SOCKET_SESSION_BUS = 1 < < 3 ,
FLATPAK_CONTEXT_SOCKET_SYSTEM_BUS = 1 < < 4 ,
2018-02-14 10:49:33 +00:00
FLATPAK_CONTEXT_SOCKET_FALLBACK_X11 = 1 < < 5 , /* For backwards compat, also set SOCKET_X11 */
2017-08-24 13:48:55 +00:00
} FlatpakContextSockets ;
/* Same order as enum */
const char * flatpak_context_sockets [ ] = {
" x11 " ,
" wayland " ,
" pulseaudio " ,
" session-bus " ,
" system-bus " ,
2018-02-14 10:49:33 +00:00
" fallback-x11 " ,
2017-08-24 13:48:55 +00:00
NULL
} ;
const char * dont_mount_in_root [ ] = {
" . " , " .. " , " lib " , " lib32 " , " lib64 " , " bin " , " sbin " , " usr " , " boot " , " root " ,
" tmp " , " etc " , " app " , " run " , " proc " , " sys " , " dev " , " var " , NULL
} ;
/* We don't want to export paths pointing into these, because they are readonly
( so we can ' t create mountpoints there ) and don ' t match whats on the host anyway */
const char * dont_export_in [ ] = {
" /lib " , " /lib32 " , " /lib64 " , " /bin " , " /sbin " , " /usr " , " /etc " , " /app " , NULL
} ;
2017-03-08 17:53:38 +00:00
2017-08-24 13:48:55 +00:00
typedef enum {
FLATPAK_CONTEXT_DEVICE_DRI = 1 < < 0 ,
FLATPAK_CONTEXT_DEVICE_ALL = 1 < < 1 ,
FLATPAK_CONTEXT_DEVICE_KVM = 1 < < 2 ,
} FlatpakContextDevices ;
2017-03-08 17:53:38 +00:00
2017-08-24 13:48:55 +00:00
const char * flatpak_context_devices [ ] = {
" dri " ,
" all " ,
" kvm " ,
NULL
} ;
typedef enum {
FLATPAK_CONTEXT_FEATURE_DEVEL = 1 < < 0 ,
FLATPAK_CONTEXT_FEATURE_MULTIARCH = 1 < < 1 ,
} FlatpakContextFeatures ;
const char * flatpak_context_features [ ] = {
" devel " ,
" multiarch " ,
NULL
} ;
struct FlatpakContext
{
FlatpakContextShares shares ;
FlatpakContextShares shares_valid ;
FlatpakContextSockets sockets ;
FlatpakContextSockets sockets_valid ;
FlatpakContextDevices devices ;
FlatpakContextDevices devices_valid ;
FlatpakContextFeatures features ;
FlatpakContextFeatures features_valid ;
GHashTable * env_vars ;
GHashTable * persistent ;
GHashTable * filesystems ;
GHashTable * session_bus_policy ;
GHashTable * system_bus_policy ;
GHashTable * generic_policy ;
} ;
FlatpakContext *
flatpak_context_new ( void )
{
FlatpakContext * context ;
context = g_slice_new0 ( FlatpakContext ) ;
context - > env_vars = g_hash_table_new_full ( g_str_hash , g_str_equal , g_free , g_free ) ;
context - > persistent = g_hash_table_new_full ( g_str_hash , g_str_equal , g_free , NULL ) ;
context - > filesystems = g_hash_table_new_full ( g_str_hash , g_str_equal , g_free , NULL ) ;
context - > session_bus_policy = g_hash_table_new_full ( g_str_hash , g_str_equal , g_free , NULL ) ;
context - > system_bus_policy = g_hash_table_new_full ( g_str_hash , g_str_equal , g_free , NULL ) ;
context - > generic_policy = g_hash_table_new_full ( g_str_hash , g_str_equal ,
g_free , ( GDestroyNotify ) g_strfreev ) ;
return context ;
2016-10-20 12:24:32 +00:00
}
2017-08-24 13:48:55 +00:00
void
flatpak_context_free ( FlatpakContext * context )
2016-12-08 15:38:23 +00:00
{
2017-08-24 13:48:55 +00:00
g_hash_table_destroy ( context - > env_vars ) ;
g_hash_table_destroy ( context - > persistent ) ;
g_hash_table_destroy ( context - > filesystems ) ;
g_hash_table_destroy ( context - > session_bus_policy ) ;
g_hash_table_destroy ( context - > system_bus_policy ) ;
g_hash_table_destroy ( context - > generic_policy ) ;
g_slice_free ( FlatpakContext , context ) ;
}
2016-12-08 15:38:23 +00:00
2017-08-24 13:48:55 +00:00
static guint32
flatpak_context_bitmask_from_string ( const char * name , const char * * names )
{
guint32 i ;
2016-12-08 15:38:23 +00:00
2017-08-24 13:48:55 +00:00
for ( i = 0 ; names [ i ] ! = NULL ; i + + )
{
if ( strcmp ( names [ i ] , name ) = = 0 )
return 1 < < i ;
}
2017-01-05 13:38:32 +00:00
2017-08-24 13:48:55 +00:00
return 0 ;
}
static void
flatpak_context_bitmask_to_args ( guint32 enabled , guint32 valid , const char * * names ,
const char * enable_arg , const char * disable_arg ,
GPtrArray * args )
{
guint32 i ;
for ( i = 0 ; names [ i ] ! = NULL ; i + + )
{
guint32 bitmask = 1 < < i ;
if ( valid & bitmask )
{
if ( enabled & bitmask )
g_ptr_array_add ( args , g_strdup_printf ( " %s=%s " , enable_arg , names [ i ] ) ) ;
else
g_ptr_array_add ( args , g_strdup_printf ( " %s=%s " , disable_arg , names [ i ] ) ) ;
}
}
}
static FlatpakContextShares
flatpak_context_share_from_string ( const char * string , GError * * error )
{
FlatpakContextShares shares = flatpak_context_bitmask_from_string ( string , flatpak_context_shares ) ;
if ( shares = = 0 )
{
g_autofree char * values = g_strjoinv ( " , " , ( char * * ) flatpak_context_shares ) ;
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_FAILED ,
_ ( " Unknown share type %s, valid types are: %s " ) , string , values ) ;
}
return shares ;
}
static void
flatpak_context_shared_to_args ( FlatpakContextShares shares ,
FlatpakContextShares valid ,
GPtrArray * args )
{
return flatpak_context_bitmask_to_args ( shares , valid , flatpak_context_shares , " --share " , " --unshare " , args ) ;
}
static const char *
flatpak_policy_to_string ( FlatpakPolicy policy )
{
if ( policy = = FLATPAK_POLICY_SEE )
return " see " ;
if ( policy = = FLATPAK_POLICY_TALK )
return " talk " ;
if ( policy = = FLATPAK_POLICY_OWN )
return " own " ;
return " none " ;
}
static gboolean
flatpak_verify_dbus_name ( const char * name , GError * * error )
{
const char * name_part ;
g_autofree char * tmp = NULL ;
if ( g_str_has_suffix ( name , " .* " ) )
{
tmp = g_strndup ( name , strlen ( name ) - 2 ) ;
name_part = tmp ;
}
else
{
name_part = name ;
}
if ( g_dbus_is_name ( name_part ) & & ! g_dbus_is_unique_name ( name_part ) )
return TRUE ;
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_FAILED ,
_ ( " Invalid dbus name %s \n " ) , name ) ;
return FALSE ;
}
static FlatpakContextSockets
flatpak_context_socket_from_string ( const char * string , GError * * error )
{
FlatpakContextSockets sockets = flatpak_context_bitmask_from_string ( string , flatpak_context_sockets ) ;
if ( sockets = = 0 )
{
g_autofree char * values = g_strjoinv ( " , " , ( char * * ) flatpak_context_sockets ) ;
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_FAILED ,
_ ( " Unknown socket type %s, valid types are: %s " ) , string , values ) ;
}
return sockets ;
}
static void
flatpak_context_sockets_to_args ( FlatpakContextSockets sockets ,
FlatpakContextSockets valid ,
GPtrArray * args )
{
return flatpak_context_bitmask_to_args ( sockets , valid , flatpak_context_sockets , " --socket " , " --nosocket " , args ) ;
}
static FlatpakContextDevices
flatpak_context_device_from_string ( const char * string , GError * * error )
{
FlatpakContextDevices devices = flatpak_context_bitmask_from_string ( string , flatpak_context_devices ) ;
if ( devices = = 0 )
{
g_autofree char * values = g_strjoinv ( " , " , ( char * * ) flatpak_context_devices ) ;
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_FAILED ,
_ ( " Unknown device type %s, valid types are: %s " ) , string , values ) ;
}
return devices ;
}
static void
flatpak_context_devices_to_args ( FlatpakContextDevices devices ,
FlatpakContextDevices valid ,
GPtrArray * args )
{
return flatpak_context_bitmask_to_args ( devices , valid , flatpak_context_devices , " --device " , " --nodevice " , args ) ;
}
static FlatpakContextFeatures
flatpak_context_feature_from_string ( const char * string , GError * * error )
{
FlatpakContextFeatures feature = flatpak_context_bitmask_from_string ( string , flatpak_context_features ) ;
if ( feature = = 0 )
{
g_autofree char * values = g_strjoinv ( " , " , ( char * * ) flatpak_context_features ) ;
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_FAILED ,
_ ( " Unknown feature type %s, valid types are: %s " ) , string , values ) ;
}
return feature ;
}
static void
flatpak_context_features_to_args ( FlatpakContextFeatures features ,
FlatpakContextFeatures valid ,
GPtrArray * args )
{
return flatpak_context_bitmask_to_args ( features , valid , flatpak_context_features , " --allow " , " --disallow " , args ) ;
}
static void
flatpak_context_add_shares ( FlatpakContext * context ,
FlatpakContextShares shares )
{
context - > shares_valid | = shares ;
context - > shares | = shares ;
}
static void
flatpak_context_remove_shares ( FlatpakContext * context ,
FlatpakContextShares shares )
{
context - > shares_valid | = shares ;
context - > shares & = ~ shares ;
}
static void
flatpak_context_add_sockets ( FlatpakContext * context ,
FlatpakContextSockets sockets )
{
context - > sockets_valid | = sockets ;
context - > sockets | = sockets ;
}
static void
flatpak_context_remove_sockets ( FlatpakContext * context ,
FlatpakContextSockets sockets )
{
context - > sockets_valid | = sockets ;
context - > sockets & = ~ sockets ;
}
static void
flatpak_context_add_devices ( FlatpakContext * context ,
FlatpakContextDevices devices )
{
context - > devices_valid | = devices ;
context - > devices | = devices ;
}
static void
flatpak_context_remove_devices ( FlatpakContext * context ,
FlatpakContextDevices devices )
{
context - > devices_valid | = devices ;
context - > devices & = ~ devices ;
}
static void
flatpak_context_add_features ( FlatpakContext * context ,
FlatpakContextFeatures features )
{
context - > features_valid | = features ;
context - > features | = features ;
}
static void
flatpak_context_remove_features ( FlatpakContext * context ,
FlatpakContextFeatures features )
{
context - > features_valid | = features ;
context - > features & = ~ features ;
}
static void
flatpak_context_set_env_var ( FlatpakContext * context ,
const char * name ,
const char * value )
{
g_hash_table_insert ( context - > env_vars , g_strdup ( name ) , g_strdup ( value ) ) ;
}
static void
flatpak_context_set_session_bus_policy ( FlatpakContext * context ,
const char * name ,
FlatpakPolicy policy )
{
g_hash_table_insert ( context - > session_bus_policy , g_strdup ( name ) , GINT_TO_POINTER ( policy ) ) ;
}
static void
flatpak_context_set_system_bus_policy ( FlatpakContext * context ,
const char * name ,
FlatpakPolicy policy )
{
g_hash_table_insert ( context - > system_bus_policy , g_strdup ( name ) , GINT_TO_POINTER ( policy ) ) ;
}
static void
flatpak_context_apply_generic_policy ( FlatpakContext * context ,
const char * key ,
const char * value )
{
GPtrArray * new = g_ptr_array_new ( ) ;
const char * * old_v ;
int i ;
g_assert ( strchr ( key , ' . ' ) ! = NULL ) ;
old_v = g_hash_table_lookup ( context - > generic_policy , key ) ;
for ( i = 0 ; old_v ! = NULL & & old_v [ i ] ! = NULL ; i + + )
{
const char * old = old_v [ i ] ;
const char * cmp1 = old ;
const char * cmp2 = value ;
if ( * cmp1 = = ' ! ' )
cmp1 + + ;
if ( * cmp2 = = ' ! ' )
cmp2 + + ;
if ( strcmp ( cmp1 , cmp2 ) ! = 0 )
g_ptr_array_add ( new , g_strdup ( old ) ) ;
}
g_ptr_array_add ( new , g_strdup ( value ) ) ;
g_ptr_array_add ( new , NULL ) ;
g_hash_table_insert ( context - > generic_policy , g_strdup ( key ) ,
g_ptr_array_free ( new , FALSE ) ) ;
}
static void
flatpak_context_set_persistent ( FlatpakContext * context ,
const char * path )
{
g_hash_table_insert ( context - > persistent , g_strdup ( path ) , GINT_TO_POINTER ( 1 ) ) ;
}
static gboolean
get_xdg_dir_from_prefix ( const char * prefix ,
const char * * where ,
const char * * dir )
{
if ( strcmp ( prefix , " xdg-data " ) = = 0 )
{
if ( where )
* where = " data " ;
if ( dir )
* dir = g_get_user_data_dir ( ) ;
return TRUE ;
}
if ( strcmp ( prefix , " xdg-cache " ) = = 0 )
{
if ( where )
* where = " cache " ;
if ( dir )
* dir = g_get_user_cache_dir ( ) ;
return TRUE ;
}
if ( strcmp ( prefix , " xdg-config " ) = = 0 )
{
if ( where )
* where = " config " ;
if ( dir )
* dir = g_get_user_config_dir ( ) ;
return TRUE ;
}
return FALSE ;
}
static gboolean
get_xdg_user_dir_from_string ( const char * filesystem ,
const char * * config_key ,
const char * * suffix ,
const char * * dir )
{
char * slash ;
const char * rest ;
g_autofree char * prefix = NULL ;
gsize len ;
slash = strchr ( filesystem , ' / ' ) ;
if ( slash )
len = slash - filesystem ;
else
len = strlen ( filesystem ) ;
rest = filesystem + len ;
while ( * rest = = ' / ' )
rest + + ;
if ( suffix )
* suffix = rest ;
prefix = g_strndup ( filesystem , len ) ;
if ( strcmp ( prefix , " xdg-desktop " ) = = 0 )
{
if ( config_key )
* config_key = " XDG_DESKTOP_DIR " ;
if ( dir )
* dir = g_get_user_special_dir ( G_USER_DIRECTORY_DESKTOP ) ;
return TRUE ;
}
if ( strcmp ( prefix , " xdg-documents " ) = = 0 )
{
if ( config_key )
* config_key = " XDG_DOCUMENTS_DIR " ;
if ( dir )
* dir = g_get_user_special_dir ( G_USER_DIRECTORY_DOCUMENTS ) ;
return TRUE ;
}
if ( strcmp ( prefix , " xdg-download " ) = = 0 )
{
if ( config_key )
* config_key = " XDG_DOWNLOAD_DIR " ;
if ( dir )
* dir = g_get_user_special_dir ( G_USER_DIRECTORY_DOWNLOAD ) ;
return TRUE ;
}
if ( strcmp ( prefix , " xdg-music " ) = = 0 )
{
if ( config_key )
* config_key = " XDG_MUSIC_DIR " ;
if ( dir )
* dir = g_get_user_special_dir ( G_USER_DIRECTORY_MUSIC ) ;
return TRUE ;
}
if ( strcmp ( prefix , " xdg-pictures " ) = = 0 )
{
if ( config_key )
* config_key = " XDG_PICTURES_DIR " ;
if ( dir )
* dir = g_get_user_special_dir ( G_USER_DIRECTORY_PICTURES ) ;
return TRUE ;
}
if ( strcmp ( prefix , " xdg-public-share " ) = = 0 )
{
if ( config_key )
* config_key = " XDG_PUBLICSHARE_DIR " ;
if ( dir )
* dir = g_get_user_special_dir ( G_USER_DIRECTORY_PUBLIC_SHARE ) ;
return TRUE ;
}
if ( strcmp ( prefix , " xdg-templates " ) = = 0 )
{
if ( config_key )
* config_key = " XDG_TEMPLATES_DIR " ;
if ( dir )
* dir = g_get_user_special_dir ( G_USER_DIRECTORY_TEMPLATES ) ;
return TRUE ;
}
if ( strcmp ( prefix , " xdg-videos " ) = = 0 )
{
if ( config_key )
* config_key = " XDG_VIDEOS_DIR " ;
if ( dir )
* dir = g_get_user_special_dir ( G_USER_DIRECTORY_VIDEOS ) ;
return TRUE ;
}
if ( get_xdg_dir_from_prefix ( prefix , NULL , dir ) )
{
if ( config_key )
* config_key = NULL ;
return TRUE ;
}
/* Don't support xdg-run without suffix, because that doesn't work */
if ( strcmp ( prefix , " xdg-run " ) = = 0 & &
* rest ! = 0 )
{
if ( config_key )
* config_key = NULL ;
if ( dir )
* dir = g_get_user_runtime_dir ( ) ;
return TRUE ;
}
2016-12-08 15:38:23 +00:00
2017-08-24 13:48:55 +00:00
return FALSE ;
}
2016-10-20 12:24:32 +00:00
2017-08-24 13:48:55 +00:00
static char *
parse_filesystem_flags ( const char * filesystem , FlatpakFilesystemMode * mode )
{
gsize len = strlen ( filesystem ) ;
2016-10-20 12:24:32 +00:00
2017-08-24 13:48:55 +00:00
if ( mode )
* mode = FLATPAK_FILESYSTEM_MODE_READ_WRITE ;
2016-12-08 15:38:23 +00:00
2017-08-24 13:48:55 +00:00
if ( g_str_has_suffix ( filesystem , " :ro " ) )
2016-12-08 15:38:23 +00:00
{
2017-08-24 13:48:55 +00:00
len - = 3 ;
if ( mode )
* mode = FLATPAK_FILESYSTEM_MODE_READ_ONLY ;
}
else if ( g_str_has_suffix ( filesystem , " :rw " ) )
{
len - = 3 ;
if ( mode )
* mode = FLATPAK_FILESYSTEM_MODE_READ_WRITE ;
}
else if ( g_str_has_suffix ( filesystem , " :create " ) )
{
len - = 7 ;
if ( mode )
* mode = FLATPAK_FILESYSTEM_MODE_CREATE ;
2016-12-08 15:38:23 +00:00
}
2017-08-24 13:48:55 +00:00
return g_strndup ( filesystem , len ) ;
2016-12-08 15:38:23 +00:00
}
2016-10-20 12:24:32 +00:00
2017-08-24 13:48:55 +00:00
static gboolean
flatpak_context_verify_filesystem ( const char * filesystem_and_mode ,
GError * * error )
2016-05-27 14:30:13 +00:00
{
2017-08-24 13:48:55 +00:00
g_autofree char * filesystem = parse_filesystem_flags ( filesystem_and_mode , NULL ) ;
if ( strcmp ( filesystem , " host " ) = = 0 )
return TRUE ;
if ( strcmp ( filesystem , " home " ) = = 0 )
return TRUE ;
if ( get_xdg_user_dir_from_string ( filesystem , NULL , NULL , NULL ) )
return TRUE ;
if ( g_str_has_prefix ( filesystem , " ~/ " ) )
return TRUE ;
if ( g_str_has_prefix ( filesystem , " / " ) )
return TRUE ;
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_FAILED ,
_ ( " Unknown filesystem location %s, valid locations are: host, home, xdg-*[/...], ~/dir, /dir " ) , filesystem ) ;
return FALSE ;
2016-05-27 14:30:13 +00:00
}
2017-08-24 13:48:55 +00:00
static void
flatpak_context_add_filesystem ( FlatpakContext * context ,
const char * what )
2016-05-27 14:30:13 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakFilesystemMode mode ;
char * fs = parse_filesystem_flags ( what , & mode ) ;
g_hash_table_insert ( context - > filesystems , fs , GINT_TO_POINTER ( mode ) ) ;
2016-05-27 14:30:13 +00:00
}
2017-08-24 13:48:55 +00:00
static void
flatpak_context_remove_filesystem ( FlatpakContext * context ,
const char * what )
2016-05-30 11:06:00 +00:00
{
2017-08-24 13:48:55 +00:00
g_hash_table_insert ( context - > filesystems ,
parse_filesystem_flags ( what , NULL ) ,
NULL ) ;
2016-05-30 11:06:00 +00:00
}
2017-08-24 13:48:55 +00:00
static gboolean
option_share_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
2016-05-30 11:06:00 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakContext * context = data ;
FlatpakContextShares share ;
share = flatpak_context_share_from_string ( value , error ) ;
if ( share = = 0 )
return FALSE ;
flatpak_context_add_shares ( context , share ) ;
return TRUE ;
2016-05-30 11:06:00 +00:00
}
2017-08-24 13:48:55 +00:00
static gboolean
option_unshare_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
2016-05-27 14:30:13 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakContext * context = data ;
FlatpakContextShares share ;
2016-05-27 14:30:13 +00:00
2017-08-24 13:48:55 +00:00
share = flatpak_context_share_from_string ( value , error ) ;
if ( share = = 0 )
return FALSE ;
2016-05-27 14:30:13 +00:00
2017-08-24 13:48:55 +00:00
flatpak_context_remove_shares ( context , share ) ;
2016-05-27 14:30:13 +00:00
2017-08-24 13:48:55 +00:00
return TRUE ;
}
2016-09-14 12:30:19 +00:00
2017-08-24 13:48:55 +00:00
static gboolean
option_socket_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
{
FlatpakContext * context = data ;
FlatpakContextSockets socket ;
2017-05-19 10:52:39 +00:00
2017-08-24 13:48:55 +00:00
socket = flatpak_context_socket_from_string ( value , error ) ;
if ( socket = = 0 )
return FALSE ;
2016-05-27 14:30:13 +00:00
2018-02-14 10:49:33 +00:00
if ( socket = = FLATPAK_CONTEXT_SOCKET_FALLBACK_X11 )
socket | = FLATPAK_CONTEXT_SOCKET_X11 ;
2017-08-24 13:48:55 +00:00
flatpak_context_add_sockets ( context , socket ) ;
2016-05-27 14:30:13 +00:00
2017-08-24 13:48:55 +00:00
return TRUE ;
2016-05-27 14:30:13 +00:00
}
2017-08-24 13:48:55 +00:00
static gboolean
option_nosocket_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
2016-08-19 14:51:24 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakContext * context = data ;
FlatpakContextSockets socket ;
2016-08-19 14:51:24 +00:00
2017-08-24 13:48:55 +00:00
socket = flatpak_context_socket_from_string ( value , error ) ;
if ( socket = = 0 )
return FALSE ;
2016-08-19 14:51:24 +00:00
2018-02-14 10:49:33 +00:00
if ( socket = = FLATPAK_CONTEXT_SOCKET_FALLBACK_X11 )
socket | = FLATPAK_CONTEXT_SOCKET_X11 ;
2017-08-24 13:48:55 +00:00
flatpak_context_remove_sockets ( context , socket ) ;
return TRUE ;
2016-08-19 14:51:24 +00:00
}
2017-08-24 13:48:55 +00:00
static gboolean
option_device_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
2016-10-14 13:45:16 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakContext * context = data ;
FlatpakContextDevices device ;
2016-10-14 13:45:16 +00:00
2017-08-24 13:48:55 +00:00
device = flatpak_context_device_from_string ( value , error ) ;
if ( device = = 0 )
return FALSE ;
2016-10-14 13:45:16 +00:00
2017-08-24 13:48:55 +00:00
flatpak_context_add_devices ( context , device ) ;
2016-10-14 13:45:16 +00:00
2017-08-24 13:48:55 +00:00
return TRUE ;
2016-10-14 13:45:16 +00:00
}
2017-08-24 13:48:55 +00:00
static gboolean
option_nodevice_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
2016-10-14 13:45:16 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakContext * context = data ;
FlatpakContextDevices device ;
2016-10-14 13:45:16 +00:00
2017-08-24 13:48:55 +00:00
device = flatpak_context_device_from_string ( value , error ) ;
if ( device = = 0 )
return FALSE ;
2016-10-14 13:45:16 +00:00
2017-08-24 13:48:55 +00:00
flatpak_context_remove_devices ( context , device ) ;
2016-10-14 13:45:16 +00:00
2017-08-24 13:48:55 +00:00
return TRUE ;
}
2016-10-14 13:45:16 +00:00
2017-08-24 13:48:55 +00:00
static gboolean
option_allow_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
{
FlatpakContext * context = data ;
FlatpakContextFeatures feature ;
2016-10-14 13:45:16 +00:00
2017-08-24 13:48:55 +00:00
feature = flatpak_context_feature_from_string ( value , error ) ;
if ( feature = = 0 )
return FALSE ;
2016-10-14 13:45:16 +00:00
2017-08-24 13:48:55 +00:00
flatpak_context_add_features ( context , feature ) ;
2016-10-14 13:45:16 +00:00
2017-08-24 13:48:55 +00:00
return TRUE ;
2016-10-14 13:45:16 +00:00
}
2016-06-02 07:16:34 +00:00
static gboolean
2017-08-24 13:48:55 +00:00
option_disallow_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
2016-06-02 07:16:34 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakContext * context = data ;
FlatpakContextFeatures feature ;
2016-06-02 07:16:34 +00:00
2017-08-24 13:48:55 +00:00
feature = flatpak_context_feature_from_string ( value , error ) ;
if ( feature = = 0 )
return FALSE ;
2016-06-02 07:16:34 +00:00
2017-08-24 13:48:55 +00:00
flatpak_context_remove_features ( context , feature ) ;
2016-06-02 07:16:34 +00:00
2017-08-24 13:48:55 +00:00
return TRUE ;
2016-06-02 07:16:34 +00:00
}
static gboolean
2017-08-24 13:48:55 +00:00
option_filesystem_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
2016-06-02 07:16:34 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakContext * context = data ;
if ( ! flatpak_context_verify_filesystem ( value , error ) )
return FALSE ;
flatpak_context_add_filesystem ( context , value ) ;
return TRUE ;
2016-06-02 07:16:34 +00:00
}
2017-08-24 13:48:55 +00:00
static gboolean
option_nofilesystem_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
2016-05-27 14:30:13 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakContext * context = data ;
2016-05-27 14:30:13 +00:00
2017-08-24 13:48:55 +00:00
if ( ! flatpak_context_verify_filesystem ( value , error ) )
return FALSE ;
2016-06-02 07:16:34 +00:00
2017-08-24 13:48:55 +00:00
flatpak_context_remove_filesystem ( context , value ) ;
return TRUE ;
2016-05-27 14:30:13 +00:00
}
2017-08-24 13:48:55 +00:00
static gboolean
option_env_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
2016-05-27 14:30:13 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakContext * context = data ;
2016-05-27 14:30:13 +00:00
2017-08-24 13:48:55 +00:00
g_auto ( GStrv ) split = g_strsplit ( value , " = " , 2 ) ;
2016-05-27 14:30:13 +00:00
2017-08-24 13:48:55 +00:00
if ( split = = NULL | | split [ 0 ] = = NULL | | split [ 0 ] [ 0 ] = = 0 | | split [ 1 ] = = NULL )
2016-05-27 14:30:13 +00:00
{
2017-08-24 13:48:55 +00:00
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_FAILED ,
_ ( " Invalid env format %s " ) , value ) ;
return FALSE ;
2016-05-27 14:30:13 +00:00
}
2017-08-24 13:48:55 +00:00
flatpak_context_set_env_var ( context , split [ 0 ] , split [ 1 ] ) ;
return TRUE ;
2016-05-27 14:30:13 +00:00
}
2016-06-02 13:27:50 +00:00
static gboolean
2017-08-24 13:48:55 +00:00
option_own_name_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
2016-06-02 13:27:50 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakContext * context = data ;
2016-06-02 13:27:50 +00:00
2017-08-24 13:48:55 +00:00
if ( ! flatpak_verify_dbus_name ( value , error ) )
return FALSE ;
2016-06-02 13:27:50 +00:00
2017-08-24 13:48:55 +00:00
flatpak_context_set_session_bus_policy ( context , value , FLATPAK_POLICY_OWN ) ;
return TRUE ;
2016-06-02 13:27:50 +00:00
}
2017-08-24 13:48:55 +00:00
static gboolean
option_talk_name_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
2016-05-27 14:30:13 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakContext * context = data ;
2016-05-27 14:30:13 +00:00
2017-08-24 13:48:55 +00:00
if ( ! flatpak_verify_dbus_name ( value , error ) )
return FALSE ;
2016-05-27 14:30:13 +00:00
2017-08-24 13:48:55 +00:00
flatpak_context_set_session_bus_policy ( context , value , FLATPAK_POLICY_TALK ) ;
return TRUE ;
}
2016-05-27 14:30:13 +00:00
2017-08-24 13:48:55 +00:00
static gboolean
option_system_own_name_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
{
FlatpakContext * context = data ;
2016-05-27 14:30:13 +00:00
2017-08-24 13:48:55 +00:00
if ( ! flatpak_verify_dbus_name ( value , error ) )
return FALSE ;
2016-05-27 14:30:13 +00:00
2017-08-24 13:48:55 +00:00
flatpak_context_set_system_bus_policy ( context , value , FLATPAK_POLICY_OWN ) ;
return TRUE ;
2016-05-27 14:30:13 +00:00
}
2017-08-24 13:48:55 +00:00
static gboolean
option_system_talk_name_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
2016-05-27 14:30:13 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakContext * context = data ;
if ( ! flatpak_verify_dbus_name ( value , error ) )
return FALSE ;
flatpak_context_set_system_bus_policy ( context , value , FLATPAK_POLICY_TALK ) ;
return TRUE ;
2016-05-27 14:30:13 +00:00
}
2016-06-30 09:41:12 +00:00
2017-08-24 13:48:55 +00:00
static gboolean
option_add_generic_policy_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
2016-06-30 09:41:12 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakContext * context = data ;
char * t ;
g_autofree char * key = NULL ;
const char * policy_value ;
2016-06-30 09:41:12 +00:00
2017-08-24 13:48:55 +00:00
t = strchr ( value , ' = ' ) ;
if ( t = = NULL )
return flatpak_fail ( error , " --policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE " ) ;
policy_value = t + 1 ;
key = g_strndup ( value , t - value ) ;
if ( strchr ( key , ' . ' ) = = NULL )
return flatpak_fail ( error , " --policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE " ) ;
2016-06-30 09:41:12 +00:00
2017-08-24 13:48:55 +00:00
if ( policy_value [ 0 ] = = ' ! ' )
return flatpak_fail ( error , " --policy values can't start with \" ! \" " ) ;
2016-06-30 09:41:12 +00:00
2017-08-24 13:48:55 +00:00
flatpak_context_apply_generic_policy ( context , key , policy_value ) ;
2016-06-30 09:41:12 +00:00
2017-08-24 13:48:55 +00:00
return TRUE ;
2016-06-30 09:41:12 +00:00
}
2017-05-10 15:24:48 +00:00
2017-08-24 13:48:55 +00:00
static gboolean
option_remove_generic_policy_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
2017-05-22 15:01:14 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakContext * context = data ;
char * t ;
g_autofree char * key = NULL ;
const char * policy_value ;
g_autofree char * extended_value = NULL ;
2017-05-22 15:01:14 +00:00
2017-08-24 13:48:55 +00:00
t = strchr ( value , ' = ' ) ;
if ( t = = NULL )
return flatpak_fail ( error , " --policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE " ) ;
policy_value = t + 1 ;
key = g_strndup ( value , t - value ) ;
if ( strchr ( key , ' . ' ) = = NULL )
return flatpak_fail ( error , " --policy arguments must be in the form SUBSYSTEM.KEY=[!]VALUE " ) ;
2017-05-22 15:01:14 +00:00
2017-08-24 13:48:55 +00:00
if ( policy_value [ 0 ] = = ' ! ' )
return flatpak_fail ( error , " --policy values can't start with \" ! \" " ) ;
2017-05-22 15:01:14 +00:00
2017-08-24 13:48:55 +00:00
extended_value = g_strconcat ( " ! " , policy_value , NULL ) ;
2017-05-22 15:01:14 +00:00
2017-08-24 13:48:55 +00:00
flatpak_context_apply_generic_policy ( context , key , extended_value ) ;
2017-05-22 15:01:14 +00:00
2017-08-24 13:48:55 +00:00
return TRUE ;
2017-05-22 15:01:14 +00:00
}
2017-08-24 13:48:55 +00:00
static gboolean
option_persist_cb ( const gchar * option_name ,
const gchar * value ,
gpointer data ,
GError * * error )
2017-05-22 15:01:14 +00:00
{
2017-08-24 13:48:55 +00:00
FlatpakContext * context = data ;
2017-05-22 15:01:14 +00:00
2017-08-24 13:48:55 +00:00
flatpak_context_set_persistent ( context , value ) ;
return TRUE ;
2017-05-09 15:04:35 +00:00
}
2017-08-24 13:48:55 +00:00
static GOptionEntry context_options [ ] = {
{ " share " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_share_cb , N_ ( " Share with host " ) , N_ ( " SHARE " ) } ,
{ " unshare " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_unshare_cb , N_ ( " Unshare with host " ) , N_ ( " SHARE " ) } ,
{ " socket " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_socket_cb , N_ ( " Expose socket to app " ) , N_ ( " SOCKET " ) } ,
{ " nosocket " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_nosocket_cb , N_ ( " Don't expose socket to app " ) , N_ ( " SOCKET " ) } ,
{ " device " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_device_cb , N_ ( " Expose device to app " ) , N_ ( " DEVICE " ) } ,
{ " nodevice " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_nodevice_cb , N_ ( " Don't expose device to app " ) , N_ ( " DEVICE " ) } ,
{ " allow " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_allow_cb , N_ ( " Allow feature " ) , N_ ( " FEATURE " ) } ,
{ " disallow " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_disallow_cb , N_ ( " Don't allow feature " ) , N_ ( " FEATURE " ) } ,
{ " filesystem " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_filesystem_cb , N_ ( " Expose filesystem to app (:ro for read-only) " ) , N_ ( " FILESYSTEM[:ro] " ) } ,
{ " nofilesystem " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_nofilesystem_cb , N_ ( " Don't expose filesystem to app " ) , N_ ( " FILESYSTEM " ) } ,
{ " env " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_env_cb , N_ ( " Set environment variable " ) , N_ ( " VAR=VALUE " ) } ,
{ " own-name " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_own_name_cb , N_ ( " Allow app to own name on the session bus " ) , N_ ( " DBUS_NAME " ) } ,
{ " talk-name " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_talk_name_cb , N_ ( " Allow app to talk to name on the session bus " ) , N_ ( " DBUS_NAME " ) } ,
{ " system-own-name " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_system_own_name_cb , N_ ( " Allow app to own name on the system bus " ) , N_ ( " DBUS_NAME " ) } ,
{ " system-talk-name " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_system_talk_name_cb , N_ ( " Allow app to talk to name on the system bus " ) , N_ ( " DBUS_NAME " ) } ,
{ " add-policy " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_add_generic_policy_cb , N_ ( " Add generic policy option " ) , N_ ( " SUBSYSTEM.KEY=VALUE " ) } ,
{ " remove-policy " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_remove_generic_policy_cb , N_ ( " Remove generic policy option " ) , N_ ( " SUBSYSTEM.KEY=VALUE " ) } ,
{ " persist " , 0 , G_OPTION_FLAG_IN_MAIN , G_OPTION_ARG_CALLBACK , & option_persist_cb , N_ ( " Persist home directory " ) , N_ ( " FILENAME " ) } ,
{ NULL }
} ;
GOptionGroup *
flatpak_context_get_options ( FlatpakContext * context )
2017-05-10 15:24:48 +00:00
{
2017-08-24 13:48:55 +00:00
GOptionGroup * group ;
2017-05-09 15:04:35 +00:00
2017-08-24 13:48:55 +00:00
group = g_option_group_new ( " environment " ,
" Runtime Environment " ,
" Runtime Environment " ,
context ,
NULL ) ;
g_option_group_set_translation_domain ( group , GETTEXT_PACKAGE ) ;
2017-05-18 13:14:42 +00:00
2017-08-24 13:48:55 +00:00
g_option_group_add_entries ( group , context_options ) ;
2017-05-18 13:14:42 +00:00
2017-08-24 13:48:55 +00:00
return group ;
}
2017-05-18 13:14:42 +00:00
2017-08-24 13:48:55 +00:00
void
flatpak_context_to_args ( FlatpakContext * context ,
GPtrArray * args )
{
GHashTableIter iter ;
gpointer key , value ;
2017-05-10 15:24:48 +00:00
2017-08-24 13:48:55 +00:00
flatpak_context_shared_to_args ( context - > shares , context - > shares_valid , args ) ;
flatpak_context_sockets_to_args ( context - > sockets , context - > sockets_valid , args ) ;
flatpak_context_devices_to_args ( context - > devices , context - > devices_valid , args ) ;
flatpak_context_features_to_args ( context - > features , context - > features_valid , args ) ;
2017-05-10 15:24:48 +00:00
2017-08-24 13:48:55 +00:00
g_hash_table_iter_init ( & iter , context - > env_vars ) ;
while ( g_hash_table_iter_next ( & iter , & key , & value ) )
g_ptr_array_add ( args , g_strdup_printf ( " --env=%s=%s " , ( char * ) key , ( char * ) value ) ) ;
2017-05-10 15:24:48 +00:00
2017-08-24 13:48:55 +00:00
g_hash_table_iter_init ( & iter , context - > persistent ) ;
while ( g_hash_table_iter_next ( & iter , & key , & value ) )
g_ptr_array_add ( args , g_strdup_printf ( " --persist=%s " , ( char * ) key ) ) ;
2017-05-10 15:24:48 +00:00
2017-08-24 13:48:55 +00:00
g_hash_table_iter_init ( & iter , context - > session_bus_policy ) ;
while ( g_hash_table_iter_next ( & iter , & key , & value ) )
{
const char * name = key ;
FlatpakPolicy policy = GPOINTER_TO_INT ( value ) ;
2017-05-09 15:04:35 +00:00
2017-08-24 13:48:55 +00:00
g_ptr_array_add ( args , g_strdup_printf ( " --%s-name=%s " , flatpak_policy_to_string ( policy ) , name ) ) ;
2017-05-18 13:14:42 +00:00
}
2017-05-09 15:04:35 +00:00
2017-08-24 13:48:55 +00:00
g_hash_table_iter_init ( & iter , context - > system_bus_policy ) ;
while ( g_hash_table_iter_next ( & iter , & key , & value ) )
2017-05-18 13:14:42 +00:00
{
2017-08-24 13:48:55 +00:00
const char * name = key ;
FlatpakPolicy policy = GPOINTER_TO_INT ( value ) ;
2017-05-10 15:24:48 +00:00
2017-08-24 13:48:55 +00:00
g_ptr_array_add ( args , g_strdup_printf ( " --system-%s-name=%s " , flatpak_policy_to_string ( policy ) , name ) ) ;
}
2017-05-10 15:24:48 +00:00
2017-08-24 13:48:55 +00:00
g_hash_table_iter_init ( & iter , context - > filesystems ) ;
while ( g_hash_table_iter_next ( & iter , & key , & value ) )
{
FlatpakFilesystemMode mode = GPOINTER_TO_INT ( value ) ;
2017-05-23 08:18:35 +00:00
2017-08-24 13:48:55 +00:00
if ( mode = = FLATPAK_FILESYSTEM_MODE_READ_ONLY )
g_ptr_array_add ( args , g_strdup_printf ( " --filesystem=%s:ro " , ( char * ) key ) ) ;
else if ( mode = = FLATPAK_FILESYSTEM_MODE_READ_WRITE )
g_ptr_array_add ( args , g_strdup_printf ( " --filesystem=%s " , ( char * ) key ) ) ;
else if ( mode = = FLATPAK_FILESYSTEM_MODE_CREATE )
g_ptr_array_add ( args , g_strdup_printf ( " --filesystem=%s:create " , ( char * ) key ) ) ;
else
g_ptr_array_add ( args , g_strdup_printf ( " --nofilesystem=%s " , ( char * ) key ) ) ;
}
2017-05-10 15:24:48 +00:00
}