Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 0.20240321.x
  • main
  • v0.20231013.x
  • wip/case_insensitive_fs
  • wip/denittis/line_based
  • wip/denittis/portal_helper
  • wip/denittis/t17372
  • wip/denittis/t20052
  • wip/denittis/t20177
  • wip/denittis/t7017
  • wip/denittis/tasks239
  • wip/denittis/up_libcapsule
  • wip/diagnose-task724
  • wip/fix_archlinux_ci
  • wip/flatpak_exports_for_run_host
  • wip/gameoverlayrenderer
  • wip/input-device-diagnostic
  • wip/input-portal
  • wip/issue18
  • wip/luigipiucco/nixos
  • wip/protected/ci
  • wip/protected/scout-on-soldier
  • wip/protected/smcv-ci
  • wip/protected/smcv-merge-slr
  • wip/protected/task679
  • wip/refi64/check-steam-running
  • wip/refi64/ci-coverage-demo
  • wip/refi64/ci-coverage-expose
  • wip/refi64/mr_template_test
  • wip/refi64/openxr
  • wip/refi64/openxr-rt-pv
  • wip/refi64/openxr-socket
  • wip/refi64/openxr-sysinfo
  • wip/refi64/vk-layers
  • wip/regenerate-ldso-cache
  • wip/shorten_socket
  • wip/smcv/always-copy-runtime
  • wip/smcv/ci
  • wip/smcv/clang
  • wip/smcv/delete-in-background
  • wip/smcv/dev-lib-symlinks
  • wip/smcv/diagnose-sfl10602
  • wip/smcv/diffable
  • wip/smcv/doc
  • wip/smcv/docs
  • wip/smcv/fex-3
  • wip/smcv/gcc11
  • wip/smcv/guessed-library-arch
  • wip/smcv/inspect-library
  • wip/smcv/into-library
  • wip/smcv/ld-so-wrapper
  • wip/smcv/lxc
  • wip/smcv/no-nvidia-pkcs11
  • wip/smcv/pv-wrap-to-journal
  • wip/smcv/refcount-locks
  • wip/smcv/relocatable-bin
  • wip/smcv/spawn-really-async
  • wip/smcv/staging
  • wip/smcv/steam-portal
  • wip/smcv/task149-nonfhs
  • wip/smcv/task212-basename
  • wip/smcv/task370-debug
  • wip/smcv/test-diagnostics
  • wip/smcv/variant-init-statically
  • wip/smcv/vulkan-1-1
  • wip/sr640
  • wip/sr684
  • wip/task293-v1
  • wip/task342-3
  • wip/task466
  • wip/task466-more
  • wip/task481-test
  • wip/task488-test
  • wip/task595
  • wip/task634
  • wip/task647
  • wip/task88
  • wip/task88-pre
  • wip/vulkan_shaders
  • wip/xytovl/openxr
  • pressure-vessel-v0.20180430.0
  • pressure-vessel-v0.20180517.0
  • pressure-vessel-v0.20180517.1
  • pressure-vessel-v0.20180518.0
  • pressure-vessel-v0.20180518.1
  • pressure-vessel-v0.20190510.0
  • pressure-vessel-v0.20190815.0
  • pressure-vessel-v0.20190819.0
  • pressure-vessel-v0.20190820.0
  • pressure-vessel-v0.20190822.0
  • pressure-vessel-v0.20190823.0
  • pressure-vessel-v0.20190912.0
  • pressure-vessel-v0.20190926.0
  • pressure-vessel-v0.20190927.0
  • pressure-vessel-v0.20191112.0
  • pressure-vessel-v0.20191112.1
  • pressure-vessel-v0.20191115.0
  • pressure-vessel-v0.20191216.0
  • pressure-vessel-v0.20200108.0
  • pressure-vessel-v0.20200331.0
  • pressure-vessel-v0.20200624.0
  • pressure-vessel-v0.20200708.0
  • pressure-vessel-v0.20200709.0
  • pressure-vessel-v0.20200805.0
  • pressure-vessel-v0.20200819.0
  • pressure-vessel-v0.20200908.0
  • pressure-vessel-v0.20200909.0
  • steamrt/0.20190926.0-0+steamrt1.2
  • v0.20190717.0
  • v0.20190801.0
  • v0.20190802.0
  • v0.20190806.0
  • v0.20190816.0
  • v0.20190820.0
  • v0.20190820.1
  • v0.20190822.0
  • v0.20190909.0
  • v0.20190926.0
  • v0.20190926.1
  • v0.20191024.0
  • v0.20191120.0
  • v0.20191205.0
  • v0.20191209.0
  • v0.20200108.0
  • v0.20200109.0
  • v0.20200306.0
  • v0.20200331.0
  • v0.20200331.1
  • v0.20200415.0
  • v0.20200624.0
  • v0.20200908.0
  • v0.20200908.1
  • v0.20200914.0
  • v0.20201002.0
  • v0.20201007.0
  • v0.20201022.0
  • v0.20201124.0
  • v0.20201203.0
  • v0.20210105.0
  • v0.20210114.0
  • v0.20210126.0
  • v0.20210201.0
  • v0.20210203.0
  • v0.20210305.0
  • v0.20210312.0
  • v0.20210317.0
  • v0.20210331.0
  • v0.20210415.0
  • v0.20210430.0
  • v0.20210518.0
  • v0.20210608.0
  • v0.20210608.1
  • v0.20210608.2
  • v0.20210608.3
  • v0.20210617.0
  • v0.20210617.1
  • v0.20210630.0
  • v0.20210722.0
  • v0.20210806.0
  • v0.20210809.0
  • v0.20210809.1
  • v0.20210820.0
  • v0.20210906.0
  • v0.20210906.1
  • v0.20210906.2
  • v0.20211027.0
  • v0.20211207.0
  • v0.20220107.0
  • v0.20220111.0
  • v0.20220119.0
  • v0.20220131.0
  • v0.20220311.0
  • v0.20220315.0
  • v0.20220420.0
  • v0.20220601.0
  • v0.20220620.0
  • v0.20220624.0
  • v0.20220725.0
  • v0.20220725.1
  • v0.20220726.0
180 results

Target

Select target project
  • steamrt/steam-runtime-tools
1 result
Select Git revision
  • 0.20240321.x
  • main
  • v0.20231013.x
  • wip/case_insensitive_fs
  • wip/denittis/line_based
  • wip/denittis/portal_helper
  • wip/denittis/t17372
  • wip/denittis/t20052
  • wip/denittis/t20177
  • wip/denittis/t7017
  • wip/denittis/tasks239
  • wip/denittis/up_libcapsule
  • wip/diagnose-task724
  • wip/fix_archlinux_ci
  • wip/flatpak_exports_for_run_host
  • wip/gameoverlayrenderer
  • wip/input-device-diagnostic
  • wip/input-portal
  • wip/issue18
  • wip/luigipiucco/nixos
  • wip/protected/ci
  • wip/protected/scout-on-soldier
  • wip/protected/smcv-ci
  • wip/protected/smcv-merge-slr
  • wip/protected/task679
  • wip/refi64/check-steam-running
  • wip/refi64/ci-coverage-demo
  • wip/refi64/ci-coverage-expose
  • wip/refi64/mr_template_test
  • wip/refi64/openxr
  • wip/refi64/openxr-rt-pv
  • wip/refi64/openxr-socket
  • wip/refi64/openxr-sysinfo
  • wip/refi64/vk-layers
  • wip/regenerate-ldso-cache
  • wip/shorten_socket
  • wip/smcv/always-copy-runtime
  • wip/smcv/ci
  • wip/smcv/clang
  • wip/smcv/delete-in-background
  • wip/smcv/dev-lib-symlinks
  • wip/smcv/diagnose-sfl10602
  • wip/smcv/diffable
  • wip/smcv/doc
  • wip/smcv/docs
  • wip/smcv/fex-3
  • wip/smcv/gcc11
  • wip/smcv/guessed-library-arch
  • wip/smcv/inspect-library
  • wip/smcv/into-library
  • wip/smcv/ld-so-wrapper
  • wip/smcv/lxc
  • wip/smcv/no-nvidia-pkcs11
  • wip/smcv/pv-wrap-to-journal
  • wip/smcv/refcount-locks
  • wip/smcv/relocatable-bin
  • wip/smcv/spawn-really-async
  • wip/smcv/staging
  • wip/smcv/steam-portal
  • wip/smcv/task149-nonfhs
  • wip/smcv/task212-basename
  • wip/smcv/task370-debug
  • wip/smcv/test-diagnostics
  • wip/smcv/variant-init-statically
  • wip/smcv/vulkan-1-1
  • wip/sr640
  • wip/sr684
  • wip/task293-v1
  • wip/task342-3
  • wip/task466
  • wip/task466-more
  • wip/task481-test
  • wip/task488-test
  • wip/task595
  • wip/task634
  • wip/task647
  • wip/task88
  • wip/task88-pre
  • wip/vulkan_shaders
  • wip/xytovl/openxr
  • pressure-vessel-v0.20180430.0
  • pressure-vessel-v0.20180517.0
  • pressure-vessel-v0.20180517.1
  • pressure-vessel-v0.20180518.0
  • pressure-vessel-v0.20180518.1
  • pressure-vessel-v0.20190510.0
  • pressure-vessel-v0.20190815.0
  • pressure-vessel-v0.20190819.0
  • pressure-vessel-v0.20190820.0
  • pressure-vessel-v0.20190822.0
  • pressure-vessel-v0.20190823.0
  • pressure-vessel-v0.20190912.0
  • pressure-vessel-v0.20190926.0
  • pressure-vessel-v0.20190927.0
  • pressure-vessel-v0.20191112.0
  • pressure-vessel-v0.20191112.1
  • pressure-vessel-v0.20191115.0
  • pressure-vessel-v0.20191216.0
  • pressure-vessel-v0.20200108.0
  • pressure-vessel-v0.20200331.0
  • pressure-vessel-v0.20200624.0
  • pressure-vessel-v0.20200708.0
  • pressure-vessel-v0.20200709.0
  • pressure-vessel-v0.20200805.0
  • pressure-vessel-v0.20200819.0
  • pressure-vessel-v0.20200908.0
  • pressure-vessel-v0.20200909.0
  • steamrt/0.20190926.0-0+steamrt1.2
  • v0.20190717.0
  • v0.20190801.0
  • v0.20190802.0
  • v0.20190806.0
  • v0.20190816.0
  • v0.20190820.0
  • v0.20190820.1
  • v0.20190822.0
  • v0.20190909.0
  • v0.20190926.0
  • v0.20190926.1
  • v0.20191024.0
  • v0.20191120.0
  • v0.20191205.0
  • v0.20191209.0
  • v0.20200108.0
  • v0.20200109.0
  • v0.20200306.0
  • v0.20200331.0
  • v0.20200331.1
  • v0.20200415.0
  • v0.20200624.0
  • v0.20200908.0
  • v0.20200908.1
  • v0.20200914.0
  • v0.20201002.0
  • v0.20201007.0
  • v0.20201022.0
  • v0.20201124.0
  • v0.20201203.0
  • v0.20210105.0
  • v0.20210114.0
  • v0.20210126.0
  • v0.20210201.0
  • v0.20210203.0
  • v0.20210305.0
  • v0.20210312.0
  • v0.20210317.0
  • v0.20210331.0
  • v0.20210415.0
  • v0.20210430.0
  • v0.20210518.0
  • v0.20210608.0
  • v0.20210608.1
  • v0.20210608.2
  • v0.20210608.3
  • v0.20210617.0
  • v0.20210617.1
  • v0.20210630.0
  • v0.20210722.0
  • v0.20210806.0
  • v0.20210809.0
  • v0.20210809.1
  • v0.20210820.0
  • v0.20210906.0
  • v0.20210906.1
  • v0.20210906.2
  • v0.20211027.0
  • v0.20211207.0
  • v0.20220107.0
  • v0.20220111.0
  • v0.20220119.0
  • v0.20220131.0
  • v0.20220311.0
  • v0.20220315.0
  • v0.20220420.0
  • v0.20220601.0
  • v0.20220620.0
  • v0.20220624.0
  • v0.20220725.0
  • v0.20220725.1
  • v0.20220726.0
180 results
Show changes
Showing
with 1737 additions and 120 deletions
// Copyright © 2017 Collabora Ltd
// This file is part of libcapsule.
// libcapsule is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
// libcapsule is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <link.h>
#include "utils/utils.h"
#include "mmap-info.h"
// these macros are secretly the same for elf32 & elf64:
#define ELFW_ST_TYPE(a) ELF32_ST_TYPE(a)
#define ELFW_ST_BIND(a) ELF32_ST_BIND(a)
#define ELFW_ST_VISIBILITY(a) ELF32_ST_VISIBILITY(a)
typedef enum
{
RELOCATION_FLAGS_NONE = 0,
RELOCATION_FLAGS_AVOID_LIBC = (1 << 0),
} relocation_flags;
typedef struct
{
capsule_item *relocs;
struct { int success; int failure; } count;
int debug;
char *error;
mmapinfo *mmap_info;
relocation_flags flags;
ptr_list *seen;
} relocation_data;
/*
* relocate_rela_cb:
* @start: array of relocation entries
* @relasz: number of bytes (not number of structs!) following @start
* @strtab: string table, a series of 0-terminated strings concatenated
* @strsz: length of string table in bytes
* @symtab: symbol table, an array of ElfW(Sym) structs
* @symsz: length of symbol table in bytes
* @base: base address of the shared object
* @data: the same data that was passed to process_pt_dynamic()
*
* Callback used to iterate over relocations.
*/
typedef int (*relocate_rela_cb)(const ElfW(Rela) *start,
const size_t relasz,
const char *strtab,
const size_t strsz,
const ElfW(Sym) *symtab,
const size_t symsz,
void *base,
void *data);
/*
* relocate_rel_cb:
* @start: beginning of an array of relocation entries
* @relasz: number of bytes (not number of structs!) following @start
* @strtab: string table, a series of 0-terminated strings concatenated
* @strsz: length of string table in bytes
* @symtab: symbol table, an array of ElfW(Sym) structs
* @symsz: length of symbol table in bytes
* @base: base address of the shared object
* @data: the same data that was passed to process_pt_dynamic()
*
* Callback used to iterate over relocations.
*/
typedef int (*relocate_rel_cb)(const ElfW(Rel) *start,
const size_t relasz,
const char *strtab,
const size_t strsz,
const ElfW(Sym) *symtab,
const size_t symsz,
void *base,
void *data);
int process_dt_rela (const ElfW(Rela) *start,
const size_t relasz,
const char *strtab,
const size_t strsz,
const ElfW(Sym) *symtab,
const size_t symsz,
void *base,
void *data);
int process_dt_rel (const ElfW(Rel) *start,
const size_t relasz,
const char *strtab,
const size_t strsz,
const ElfW(Sym) *symtab,
const size_t symsz,
void *base,
void *data);
int process_pt_dynamic (ElfW(Addr) start,
size_t size,
void *base,
relocate_rela_cb process_rela,
relocate_rel_cb process_rel,
void *data);
// Copyright © 2017-2019 Collabora Ltd
// This file is part of libcapsule.
// libcapsule is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
// libcapsule is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>.
#include "tools.h"
#include <stdio.h>
void
_capsule_tools_print_version( const char *command )
{
/* This is actually valid/parseable YAML, so that it's both
* human-readable and machine-readable. Inspired by `docker version`
* and `ostree --version`. */
printf( "%s%s:\n", HOST_PREFIX, command );
printf( " Package: %s\n", PACKAGE_TARNAME );
printf( " Version: '%s'\n", PACKAGE_VERSION );
}
// Copyright © 2017-2019 Collabora Ltd
// This file is part of libcapsule.
// libcapsule is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
// libcapsule is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>.
#pragma once
void _capsule_tools_print_version( const char *command );
// Copyright © 2017 Collabora Ltd
// This file is part of libcapsule.
// libcapsule is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
// libcapsule is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>.
#include <assert.h>
#include <sys/param.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "debug.h"
#include "utils.h"
unsigned long debug_flags;
// ==========================================================================
// these are for finding entries in the dynamic section
// note that tha d_un.d_ptr member may be pre-offset by the
// linker, or we may beed to adjust it by the value of base ourselves:
// this is effectively private linker information and there's no
// hard and fast rule:
const void *
fix_addr (const void *base, ElfW(Addr) offset_or_addr)
{
if (offset_or_addr < (ElfW(Addr)) base)
{
// Assume it's an offset, so an address relative to addr
return base + offset_or_addr;
}
else
{
// Assume it's an absolute address
return (const void *) offset_or_addr;
}
}
const ElfW(Dyn) *
find_dyn (ElfW(Addr) base, void *start, int what)
{
ElfW(Dyn) *entry = start + base;
for( ; entry->d_tag != DT_NULL; entry++ )
if( entry->d_tag == what )
return entry;
return NULL;
}
size_t
find_value (ElfW(Addr) base, void *start, int what)
{
const ElfW(Dyn) *entry = find_dyn( base, start, what );
return entry ? entry->d_un.d_val : (size_t) -1;
}
ElfW(Addr)
find_ptr (ElfW(Addr) base, void *start, int what)
{
const ElfW(Dyn) *entry = find_dyn( base, start, what );
if( entry )
{
if( entry->d_un.d_ptr < base )
return base + entry->d_un.d_ptr;
else
return entry->d_un.d_ptr;
}
return (ElfW(Addr)) NULL;
}
/*
* dynamic_section_find_strtab:
* @start: Array of dynamic section entries
* @base: Starting address of the shared object in memory
* @siz: (out) (optional): Used to return the length of the string
* table
*
* Find the string table for the given dynamic section.
*
* Returns: (nullable): The string table, a series of concatenated
* 0-terminated strings whose total size is written through @siz if
* non-%NULL, or %NULL if not found
*/
const char *
dynamic_section_find_strtab (const ElfW(Dyn) *entries, const void *base, size_t *siz)
{
const ElfW(Dyn) *entry;
ElfW(Addr) stab = 0;
for( entry = entries; entry->d_tag != DT_NULL; entry++ )
{
if( entry->d_tag == DT_STRTAB )
{
stab = entry->d_un.d_ptr;
}
else if( entry->d_tag == DT_STRSZ )
{
if( siz )
*siz = entry->d_un.d_val;
}
}
if (stab == 0)
{
return NULL;
}
else if (stab < (ElfW(Addr)) base)
{
return base + stab;
}
else
{
return (const char *) stab;
}
}
/*
* find_symbol:
* @idx: The symbol index
* @stab: (array element-type=ElfW(Sym)): The symbol table
* @symsz: The length of the symbol table in bytes
* @str: The string table
* @strsz: The length of the string table in bytes
* @name: (out) (optional): Used to return the name, a pointer into @str
*
* If @idx is in-bounds for @stab, return a pointer to the @idx'th
* entry in @stab. Otherwise return %NULL.
*/
const ElfW(Sym) *
find_symbol (int idx, const ElfW(Sym) *stab, size_t symsz, const char *str, size_t strsz, const char **name)
{
const ElfW(Sym) *target = stab + idx;
if( idx < 0 )
return NULL;
// we could just accept the index as legitimate but then we'd
// run the risk of popping off into an unknown hyperspace coordinate
// this way we stop if the target is past the known end of the table:
if( (idx + 1) * sizeof(*target) > symsz )
return NULL;
if( name )
*name = str + target->st_name;
return target;
}
// ==========================================================================
// like strncpy except it guarantees the final byte is NUL in
// case we truncated the string. does not yet warn you in any
// way about truncation though, should probably fix that:
char *safe_strncpy (char *dest, const char *src, size_t n)
{
char *rv = strncpy( dest, src, n - 1 );
dest[ n - 1 ] = '\0';
return rv;
}
// prefix is the root of the external tree we're patching in
// with libcapsule, path is what we're trying to resolve if
// it is a symlink. path must have at least PATH_MAX chars allocated.
//
// Designed to be called repeatedly, starting with an ABSOLUTE
// path the first time. Will write the resolved link back into
// path each time and return true, until fed a path which is
// not a symlink, at which point it will leave path alone and
// return false:
int resolve_link(const char *prefix, char path[PATH_MAX])
{
int dfd;
char rl[PATH_MAX];
char dir[PATH_MAX];
char *end = NULL;
int rv = 0;
safe_strncpy( dir, path, PATH_MAX );
end = strrchr( dir, '/' );
if( end )
*end = '\0';
else
strcpy( dir, "." ); // not sure this is right, FIXME?
// but as long as the first call to us
// in any sequence was an absolute path
// this will never come up
dfd = open( dir, O_RDONLY );
if( dfd < 0 )
return 0;
rv = readlinkat( dfd, path, rl, sizeof(rl) );
if( rv == sizeof(rl) )
{
// Either rl was truncated, or there is no longer space for '\0'
rv = -1;
goto out;
}
if( rv >= 0 )
{
rl[ rv ] = '\0';
if( rl[0] == '/' )
{
if( build_filename( path, PATH_MAX, prefix, rl, NULL ) >= PATH_MAX )
{
goto out;
}
}
else
{
if( build_filename( path, PATH_MAX, dir, rl, NULL ) >= PATH_MAX )
{
goto out;
}
}
}
out:
close( dfd );
return rv != -1;
}
// todo - check this properly for actual word boundaries and
// make it warn about unknown debug keywords:
void set_debug_flags (const char *control)
{
debug_flags = DEBUG_NONE;
if ( !control )
return;
if( strstr( control, "path" ) ) debug_flags |= DEBUG_PATH;
if( strstr( control, "search" ) ) debug_flags |= DEBUG_SEARCH;
if( strstr( control, "ldcache" ) ) debug_flags |= DEBUG_LDCACHE;
if( strstr( control, "capsule" ) ) debug_flags |= DEBUG_CAPSULE;
if( strstr( control, "mprotect" ) ) debug_flags |= DEBUG_MPROTECT;
if( strstr( control, "wrappers" ) ) debug_flags |= DEBUG_WRAPPERS;
if( strstr( control, "reloc" ) ) debug_flags |= DEBUG_RELOCS;
if( strstr( control, "elf" ) ) debug_flags |= DEBUG_ELF;
if( strstr( control, "dlfunc" ) ) debug_flags |= DEBUG_DLFUNC;
if( strstr( control, "tool" ) ) debug_flags |= DEBUG_TOOL;
if( strstr( control, "all" ) ) debug_flags |= DEBUG_ALL;
if( !debug_flags )
return;
fprintf(stderr, "capsule debug flags: \n"
" path : %c # path manipulation and translation" "\n"
" search : %c # searching for DSOs" "\n"
" ldcache : %c # loading/processing the ld cache" "\n"
" capsule : %c # setting up the proxy capsule" "\n"
" mprotect: %c # handling mprotect (for RELRO)" "\n"
" wrappers: %c # function wrappers installed in the capsule" "\n"
" reloc : %c # patching capsule symbols into external DSOs" "\n"
" dlfunc : %c # special handling of dlopen/dlsym calls" "\n"
" elf : %c # detailed ELF introspection logging" "\n"
" tool : %c # command-line tools" "\n",
(debug_flags & DEBUG_PATH ) ? 'Y' : 'n' ,
(debug_flags & DEBUG_SEARCH ) ? 'Y' : 'n' ,
(debug_flags & DEBUG_LDCACHE ) ? 'Y' : 'n' ,
(debug_flags & DEBUG_CAPSULE ) ? 'Y' : 'n' ,
(debug_flags & DEBUG_MPROTECT) ? 'Y' : 'n' ,
(debug_flags & DEBUG_WRAPPERS) ? 'Y' : 'n' ,
(debug_flags & DEBUG_RELOCS ) ? 'Y' : 'n' ,
(debug_flags & DEBUG_DLFUNC ) ? 'Y' : 'n' ,
(debug_flags & DEBUG_ELF ) ? 'Y' : 'n' ,
(debug_flags & DEBUG_TOOL ) ? 'Y' : 'n' );
}
// soname: bare libfoo.so.X style name
// path: [possibly absolute] path to DSO
// return true if soname: libFOO.so.X matches
// path: /path/to/libFOO.so.X.Y or /path/to/libFOO.so.X
int soname_matches_path (const char *soname, const char *path)
{
const char *path_soname = strrchr( path, '/' );
const char *pattern = path_soname ? path_soname + 1: path;
const size_t slen = strlen( soname );
if( strncmp( soname, pattern, slen ) != 0 )
return 0;
const char *end = pattern + slen;
return ( *end == '\0' || *end == '.' );
}
/*
* build_filename_va:
* @buf: a buffer
* @len: length of buffer
* @first_path: an absolute or relative path
* @ap: further path segments
*
* Fill @buf with a copy of @first_path, with subsequent path segments
* appended to it.
*
* Returns: The number of bytes that would have been used in buf, not
* including the '\0', if there was enough space. If this is >= len,
* then truncation has occurred.
*/
size_t
build_filename_va (char *buf, size_t len, const char *first_path, va_list ap)
{
const char *path;
size_t used = 0;
int first = 1;
int need_separator = 0;
if( len > 0 )
*buf = '\0';
for( path = first_path, first = 1;
path != NULL;
path = va_arg( ap, const char * ), first = 0)
{
size_t path_len;
// Collapse any leading '//' to '/'
while( path[0] == '/' && path[1] == '/' )
path++;
// If this is not the first path segment, strip any leading '/'
if( path[0] == '/' && !first )
path++;
path_len = strlen( path );
// Collapse any trailing '/' to nothing, unless this is the
// first path segment, in which case collapse them to just '/'
while( path_len > (first ? 1 : 0) && path[path_len - 1] == '/' )
{
path_len--;
}
// Ensure there is a '/' before we append path if necessary
if( need_separator )
{
used++;
if( used < len )
{
buf[used - 1] = '/';
buf[used] = '\0';
}
}
// If there's still any space left, try to append the path
if( used < len )
{
strncpy( buf + used, path, MIN( len - used, path_len + 1 ) );
buf[len - 1] = '\0';
}
used += path_len;
// Next time, we need to append a separator, unless this was
// the first path segment and it was '/'
need_separator = (path_len == 0 || path[path_len - 1] != '/');
}
va_end( ap );
return used;
}
/*
* build_filename:
* @buf: a buffer
* @len: length of buffer
* @first_path: an absolute or relative path
* @...: further path segments
*
* Fill @buf with a copy of @first_path, with subsequent path segments
* appended to it.
*
* Returns: The number of bytes that would have been used in buf, not
* including the '\0', if there was enough space. If this is >= len,
* then truncation has occurred.
*/
size_t
build_filename (char *buf, size_t len, const char *first_path, ...)
{
size_t used;
va_list ap;
va_start( ap, first_path );
used = build_filename_va( buf, len, first_path, ap );
va_end( ap );
return used;
}
/*
* build_filename_alloc:
* @first_path: an absolute or relative path
* @...: further path segments
*
* Allocate and return a string built from @first_path and subsequent
* segments. Abort if there is not enough memory.
*
* Returns: A string that can be freed with free()
*/
char *
build_filename_alloc (const char *first_path, ...)
{
char *buf;
size_t allocate;
size_t len;
va_list ap;
// Do a first pass to count how much space we're going to need
va_start( ap, first_path );
// We need an extra byte for the "\0" which isn't included in the
// result, consistent with strlcpy()
allocate = build_filename_va( NULL, 0, first_path, ap ) + 1;
va_end( ap );
buf = xrealloc( NULL, allocate );
// Iterate over the arguments again to fill buf
va_start( ap, first_path );
len = build_filename_va( buf, allocate, first_path, ap );
va_end( ap );
// build_filename_va() returns the same thing every time. In
// particular this means truncation did not occur, because that
// would be indicated by len >= allocate.
assert( len + 1 == allocate );
return buf;
}
ptr_list *
ptr_list_alloc(size_t size)
{
ptr_list *list = xcalloc( 1, sizeof(ptr_list) );
list->loc = xcalloc( size, sizeof(ptr_item) );
list->allocated = size;
list->next = 0;
return list;
}
/*
* ptr_list_free_to_array:
* @list: (transfer full): list to free
* @n: (out) (optional): if not %NULL, used to return the number of
* items that were in @list
*
* Add an extra %NULL entry to @list, then return an array with the
* same contents as @list, freeing @list. If @n is not %NULL, set it
* to the number of elements in the returned array before the %NULL
* added by this function.
*/
void **
ptr_list_free_to_array (ptr_list *list, size_t *n)
{
void *ret;
// Without these assertions, it's unsafe to assign a ptr_item *
// to a void **
static_assert( alignof( void * ) == alignof( ptr_item ),
"union {void *, ElfW(Addr)} assumed to be same size "
"as void *");
static_assert( sizeof( void * ) == sizeof( ptr_item ),
"union {void *, ElfW(Addr)} assumed to be same "
"alignment as void *");
if( n != NULL )
*n = list->next;
ptr_list_push_ptr( list, NULL );
ret = list->loc;
list->loc = NULL;
ptr_list_free (list);
return ret;
}
void
ptr_list_free (ptr_list *list)
{
free( list->loc );
list->loc = NULL;
list->allocated = 0;
list->next = 0;
free( list );
}
void
ptr_list_push_addr (ptr_list *list, ElfW(Addr) addr)
{
if( list->next >= list->allocated )
{
list->loc = xrealloc( list->loc,
(list->allocated + 16) * sizeof(ptr_item) );
list->allocated += 16;
}
list->loc[ list->next++ ].addr = addr;
}
void
ptr_list_push_ptr (ptr_list *list, void *ptr)
{
if( list->next >= list->allocated )
{
list->loc = realloc( list->loc,
(list->allocated + 16) * sizeof(ptr_item) );
if( !list->loc )
{
fprintf( stderr, "failed to realloc ptr_list\n" );
abort();
}
list->allocated += 16;
}
list->loc[ list->next++ ].ptr = ptr;
}
int
ptr_list_add_ptr (ptr_list *list, void *ptr, ptrcmp equals)
{
for( size_t n = 0; n < list->next; n++ )
if( equals( list->loc[ n ].ptr, ptr ) )
return 0;
ptr_list_push_ptr( list, ptr );
return 1;
}
int
ptr_list_contains (ptr_list *list, ElfW(Addr) addr)
{
if( list->next == 0 )
return 0;
for( size_t n = 0; n < list->next; n++ )
if( list->loc[ n ].addr == addr )
return 1;
return 0;
}
void *
ptr_list_nth_ptr (ptr_list *list, size_t nth)
{
if( nth < list->next )
return list->loc[ nth ].ptr;
return NULL;
}
void
oom( void )
{
fprintf( stderr, "libcapsule: out of memory\n" );
abort();
}
char *
xstrdup( const char *s )
{
char *ret = strdup( s );
if (s != NULL && ret == NULL)
oom();
return ret;
}
void *
xrealloc( void *ptr, size_t size )
{
void *ret = realloc( ptr, size );
if( ptr != NULL && size != 0 && ret == NULL )
oom();
return ret;
}
void *
xcalloc( size_t n, size_t size )
{
void *ret = calloc( n, size );
if( n != 0 && size != 0 && ret == NULL )
oom();
return ret;
}
int
xasprintf( char **dest, const char *format, ...)
{
int ret;
va_list ap;
va_start( ap, format );
ret = vasprintf( dest, format, ap );
va_end( ap );
if( ret < 0 )
oom();
return ret;
}
/**
* free_strv_full:
* @strings_array: (nullable) (array zero-terminated=1): array of strings
* to free
*
* Completely frees the given @strings_array.
* If @strings_array is %NULL, this function simply returns.
*/
void
free_strv_full( char **strings_array )
{
if( strings_array )
{
for( size_t i = 0; strings_array[i] != NULL; i++ )
free( strings_array[i] );
free( strings_array );
}
}
/*
* _capsule_set_error_literal:
* @code_dest: (out) (optional): used to return an errno value
* @message_dest: (out) (optional) (transfer full): used to return an
* error message
* @code: an errno value
* @message: an error message
*
* Set an error code, like g_set_error_literal().
*/
void
_capsule_set_error_literal( int *code_dest, char **message_dest,
int code, const char *message )
{
if( code_dest != NULL )
*code_dest = code;
if( message_dest != NULL )
*message_dest = xstrdup( message );
}
/*
* _capsule_set_error:
* @code_dest: (out) (optional): used to return an errno value
* @message_dest: (out) (optional) (transfer full): used to return an
* error message
* @code: an errno value
* @message: an error message
*
* Set an error code, like g_set_error().
*/
void
_capsule_set_error( int *code_dest, char **message_dest,
int code, const char *format,
... )
{
va_list ap;
if( code_dest != NULL )
*code_dest = code;
if( message_dest != NULL )
{
va_start( ap, format );
if( vasprintf( message_dest, format, ap ) < 0 )
oom();
va_end( ap );
}
}
/*
* _capsule_basename:
* @path: A path
*
* Return the part of @path after the last directory separator, or @path
* if there is no directory separator at all, or an empty string if @path
* ends with a directory separator.
*
* Equivalent to GNU basename(3) from string.h, but not POSIX
* basename(3) from libgen.h.
*
* Returns: (transfer none): A pointer into @path
*/
const char *
_capsule_basename (const char *path)
{
const char *ret = strrchr( path, '/' );
if( ret == NULL )
return path;
assert( ret[0] == '/' );
return ret + 1;
}
// Copyright © 2017-2020 Collabora Ltd
// This file is part of libcapsule.
// libcapsule is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
// libcapsule is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <assert.h>
#include <limits.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <link.h> // for __ELF_NATIVE_CLASS
#include "debug.h"
#ifdef HAVE_STDALIGN_H
#include <stdalign.h>
#endif
#define UNLIKELY(x) __builtin_expect(x, 0)
#define LIKELY(x) __builtin_expect(x, 1)
// these macros are secretly the same for elf32 & elf64:
#define ELFW_ST_TYPE(a) ELF32_ST_TYPE(a)
#define ELFW_ST_BIND(a) ELF32_ST_BIND(a)
#define ELFW_ST_VISIBILITY(a) ELF32_ST_VISIBILITY(a)
#if __ELF_NATIVE_CLASS == 64
#define FMT_OFF "lu"
#define FMT_SWORD "lu"
#define FMT_WORD "ld"
#define FMT_SIZE "lu"
#define FMT_ADDR "ld"
#define FMT_XADDR "lx"
#define FMT_XU64 "lx"
#else
#define FMT_OFF "u"
#define FMT_SWORD "u"
#define FMT_WORD "d"
#define FMT_SIZE "u"
#define FMT_ADDR "d"
#define FMT_XADDR "x"
#define FMT_XU64 "llx"
#endif
typedef union ptr_item
{
ElfW(Addr) addr;
void *ptr;
} ptr_item;
typedef struct ptr_list
{
size_t allocated;
size_t next;
ptr_item *loc;
} ptr_list;
typedef int (*ptrcmp) (const void *a, const void *b);
ptr_list *ptr_list_alloc (size_t size);
void *ptr_list_nth_ptr (ptr_list *list, size_t nth);
void ptr_list_free (ptr_list *list);
void **ptr_list_free_to_array (ptr_list *list, size_t *n);
void ptr_list_push_ptr (ptr_list *list, void *ptr);
void ptr_list_push_addr (ptr_list *list, ElfW(Addr) addr);
int ptr_list_contains (ptr_list *list, ElfW(Addr) addr);
int ptr_list_add_ptr (ptr_list *list, void *ptr, ptrcmp equals);
#define strstarts(str, start) \
(strncmp( str, start, strlen( start ) ) == 0)
char *safe_strncpy (char *dest, const char *src, size_t n);
int resolve_link (const char *prefix, char path[PATH_MAX]);
int soname_matches_path (const char *soname, const char *path);
size_t build_filename_va (char *buf, size_t len, const char *first_path, va_list ap);
size_t build_filename (char *buf, size_t len, const char *first_path, ...) __attribute__((sentinel));
char *build_filename_alloc (const char *first_path, ...) __attribute__((sentinel));
const void * fix_addr (const void *base, ElfW(Addr) offset_or_addr);
const ElfW(Dyn) * find_dyn (ElfW(Addr) base, void *start, int what);
size_t find_value (ElfW(Addr) base, void *start, int what);
ElfW(Addr) find_ptr (ElfW(Addr) base, void *start, int what);
const char * dynamic_section_find_strtab (const ElfW(Dyn) *entries, const void *base, size_t *siz);
const ElfW(Sym) * find_symbol (int idx,
const ElfW(Sym) *stab,
size_t symsz,
const char *str,
size_t strsz,
const char **name);
void oom( void ) __attribute__((noreturn));
char *xstrdup( const char *s );
void *xrealloc( void *ptr, size_t size ) __attribute__((alloc_size(2)));
void *xcalloc( size_t n, size_t size ) __attribute__((alloc_size(1, 2), malloc));
int xasprintf( char **s, const char *format, ...) __attribute__((format(printf, 2, 3)));
void free_strv_full( char **strings_array );
/*
* _capsule_steal_pointer:
* @pp: A non-%NULL pointer to any pointer type (void ** or Something **)
*
* Transfer the contents of @pp to the caller, setting @pp to %NULL.
* This is essentially the same thing as g_steal_pointer().
*/
static inline void *
_capsule_steal_pointer( void *pp )
{
typedef void *__attribute__((may_alias)) voidp_alias;
voidp_alias *pointer_to_pointer = pp;
void *ret = *pointer_to_pointer;
*pointer_to_pointer = NULL;
return ret;
}
/*
* _capsule_propagate_error:
* @code_dest: (out) (optional): where to put the error code (errno), or %NULL
* @message_dest: (out) (transfer full) (optional) (nullable): where to
* put the message, or %NULL
* @code_src: the error code (errno)
* @message_src: (nullable) (transfer full): the error message
*
* This is essentially the same thing as g_propagate_error(), and leaves
* message_src undefined. It should typically be used as:
*
* _capsule_propagate_error( &code, &message, local_code,
* _capsule_steal_pointer( &local_message ) );
*/
static inline void
_capsule_propagate_error( int *code_dest, char **message_dest, int code_src, char *message_src )
{
if( code_dest != NULL )
*code_dest = code_src;
if( message_dest != NULL )
*message_dest = message_src;
else
free( message_src );
}
void _capsule_set_error_literal( int *code_dest, char **message_dest,
int code, const char *message );
void _capsule_set_error( int *code_dest, char **message_dest,
int code, const char *format, ... )
__attribute__((format(printf, 4, 5)));
/*
* N_ELEMENTS:
* @array: A fixed-size array (not a pointer!)
*
* Same as `G_N_ELEMENTS`, `_DBUS_N_ELEMENTS`, systemd `ELEMENTSOF`, etc.
*/
#define N_ELEMENTS(array) ( sizeof(array) / sizeof(array[0]) )
/*
* _capsule_clear:
* @pp: A pointer to a pointer that can be freed by Standard C `free()`,
* i.e. type `void **`, `char **` or more rarely `something **`
*
* Free whatever object is pointed to by `*pp`, and set `*pp` to NULL.
*/
static inline void
_capsule_clear( void *pp )
{
free( _capsule_steal_pointer( pp ) );
}
/*
* _capsule_cleanup:
* @clear:
*
* An attribute marking a variable to be cleared by `clear(&variable)`
* on exit from its scope.
*/
#define _capsule_cleanup(clear) __attribute__((cleanup(clear)))
/*
* _capsule_autofree:
*
* An attribute marking a variable to be freed by `free(variable)`
* on exit from its scope.
*/
#define _capsule_autofree _capsule_cleanup(_capsule_clear)
/*
* alignof:
* @type: A type
*
* The same as in Standard C: return the minimal alignment of a type.
*
* Note that this is not the same as gcc __alignof__, which returns the
* type's *preferred* alignment, which is sometimes greater than the
* *minimal* alignment returned by this macro.
*/
#ifndef alignof
# define alignof(type) (__builtin_offsetof(struct { char a; type b; }, b))
#endif
/*
* offsetof:
* @t: A `struct` type
* @m: A member
*
* The same as in Standard C: return the offset of @m within @t.
*/
#ifndef offsetof
# define offsetof(t, m) (__builtin_offsetof(t, m))
#endif
/*
* static_assert:
* @expr: An expression to evaluate at compile-time
* @message: A diagnostic message used if @expr is not true
*
* The same as in Standard C: evaluate @expr as a compile-time constant
* expression, and fail to build if it is zero.
*/
#ifndef static_assert
# define static_assert(expr, message) _Static_assert(expr, message)
#endif
const char *_capsule_basename (const char *path);
......@@ -17,16 +17,17 @@
],
"pinned_libs_32" : {
"list" : [
" 13902790 4 drwxr-xr-x 2 me me 4096 Apr 24 21:32 pinned_libs_32",
" 13902813 4 -rw-r--r-- 1 me me 50 Apr 24 21:32 pinned_libs_32/system_libGLU.so.1",
" 13902814 4 lrwxrwxrwx 1 me me 107 Apr 24 21:32 pinned_libs_32/libdbusmenu-gtk.so.4 -> /home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/i386-linux-gnu/libdbusmenu-gtk.so.4.0.13"
"pinned_libs_32/has_pins",
"pinned_libs_32/libdbusmenu-gtk.so.4 -> /home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/i386-linux-gnu/libdbusmenu-gtk.so.4.0.13",
"pinned_libs_32/system_libGLU.so.1"
]
},
"pinned_libs_64" : {
"list" : [
" 13902791 4 drwxr-xr-x 2 me me 4096 Apr 24 21:32 pinned_libs_64",
" 13902805 4 -rw-r--r-- 1 me me 46 Apr 24 21:32 pinned_libs_64/system_libGLU.so.1",
" 13902795 4 lrwxrwxrwx 1 me me 100 Apr 24 21:32 pinned_libs_64/libjack.so.0 -> /home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/x86_64-linux-gnu/libjack.so.0.1.0"
"pinned_libs_64/has_pins",
"pinned_libs_64/libjack.so.0 -> /home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/x86_64-linux-gnu/libjack.so.0.1.0",
"pinned_libs_64/system_libGLU.so.1"
]
}
},
......
......@@ -271,35 +271,8 @@ class TestContainers(BaseTest):
symlinks=True,
)
fallback_libcapsule_tools = os.path.join(
cls.containers_dir,
'steam-runtime', 'usr', 'lib', 'libcapsule', 'relocatable',
)
if not os.path.isdir(fallback_libcapsule_tools):
fallback_libcapsule_tools = '/usr/lib/libcapsule/relocatable'
for multiarch in ('i386-linux-gnu', 'x86_64-linux-gnu'):
for tool in ('capsule-capture-libs', 'capsule-symbols'):
exe = multiarch + '-' + tool
tool_path = os.path.join(cls.pv_dir, 'bin', exe)
found = shutil.which(exe)
relocatable = os.path.join(
os.environ.get(
'PRESSURE_VESSEL_LIBCAPSULE_TOOLS',
fallback_libcapsule_tools,
),
exe,
)
if found is not None:
cls.copy2(found, tool_path)
elif os.path.isfile(relocatable):
cls.copy2(relocatable, tool_path)
else:
raise unittest.SkipTest('{} not found'.format(exe))
for tool in ('inspect-library',):
for tool in ('inspect-library', 'capsule-capture-libs'):
exe = multiarch + '-' + tool
tool_path = os.path.join(
cls.pv_dir,
......@@ -606,6 +579,15 @@ class TestContainers(BaseTest):
check=True,
)
# Exercise the code path where we don't have a mtree manifest:
# this is important here because we're editing the runtime
# in-place to have the OLD-DEPLOYMENT flag-file, but the
# manifest doesn't include that, so if we're using a runtime
# with a manifest, that part will fail.
for manifest in ('usr-mtree.txt', 'usr-mtree.txt.gz'):
with contextlib.suppress(FileNotFoundError):
os.remove(os.path.join(old_dir, manifest))
if os.path.isdir(os.path.join(old_dir, 'files')):
old_dir = os.path.join(old_dir, 'files')
......
......@@ -36,6 +36,7 @@ tests = [
'containers.py',
'invocation.py',
'launcher.py',
'mtree-apply.py',
'test-locale-gen.sh',
'utils.py',
]
......@@ -146,6 +147,7 @@ endforeach
helpers = [
'cheap-copy',
'helper',
'mtree-apply',
]
foreach helper : helpers
......
/*
* Copyright © 2021 Collabora Ltd.
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include "subprojects/libglnx/config.h"
#include <locale.h>
#include <sysexits.h>
#include "libglnx/libglnx.h"
#include "steam-runtime-tools/glib-backports-internal.h"
#include "steam-runtime-tools/utils-internal.h"
#include "mtree.h"
#include "utils.h"
static GOptionEntry options[] =
{
{ NULL }
};
int
main (int argc,
char *argv[])
{
glnx_autofd int fd = -1;
g_autoptr(GOptionContext) context = NULL;
g_autoptr(GError) local_error = NULL;
GError **error = &local_error;
PvMtreeApplyFlags flags = PV_MTREE_APPLY_FLAGS_NONE;
int ret = EX_USAGE;
setlocale (LC_ALL, "");
_srt_setenv_disable_gio_modules ();
context = g_option_context_new ("MTREE ROOT");
g_option_context_add_main_entries (context, options, NULL);
if (!g_option_context_parse (context, &argc, &argv, error))
goto out;
if (argc >= 2 && strcmp (argv[1], "--") == 0)
{
argv++;
argc--;
}
if (argc < 3 || argc > 4)
{
g_printerr ("Usage: %s MTREE ROOT [SOURCE]\n", g_get_prgname ());
goto out;
}
ret = EX_UNAVAILABLE;
if (!glnx_opendirat (AT_FDCWD, argv[2], TRUE, &fd, error))
goto out;
if (!pv_mtree_apply (argv[1], argv[2], fd, argv[3], flags, error))
goto out;
ret = 0;
out:
if (local_error != NULL)
g_warning ("%s", local_error->message);
return ret;
}
#!/usr/bin/env python3
# Copyright 2021 Collabora Ltd.
#
# SPDX-License-Identifier: MIT
import os
import subprocess
import sys
import tempfile
from pathlib import Path
try:
import typing
typing # placate pyflakes
except ImportError:
pass
from testutils import (
BaseTest,
test_main,
)
class TestMtreeApply(BaseTest):
def setUp(self) -> None:
super().setUp()
os.environ['G_MESSAGES_DEBUG'] = 'all'
self.mtree_apply = os.path.join(
self.G_TEST_BUILDDIR,
'test-mtree-apply',
)
def assert_tree_is_superset(
self,
superset,
subset,
require_hard_links: bool = True,
require_permissions: bool = True,
require_times: bool = True,
):
for path, dirs, files in os.walk(subset):
equivalent = os.path.join(superset, os.path.relpath(path, subset))
for d in dirs:
in_subset = os.path.join(path, d)
in_superset = os.path.join(equivalent, d)
if not os.path.isdir(in_superset):
raise AssertionError(
'%r should be a directory', in_superset)
info = os.stat(in_subset)
info2 = os.stat(in_superset)
self.assertEqual(oct(info.st_mode), oct(info2.st_mode))
for f in files:
in_subset = os.path.join(path, f)
in_superset = os.path.join(equivalent, f)
if (
os.path.islink(in_subset)
or not os.path.exists(in_subset)
):
target = os.readlink(in_subset)
target2 = os.readlink(in_superset)
self.assertEqual(target, target2)
else:
info = os.stat(in_subset)
info2 = os.stat(in_superset)
if require_hard_links:
self.assertEqual(info.st_ino, info2.st_ino)
self.assertEqual(info.st_dev, info2.st_dev)
if require_permissions:
self.assertEqual(oct(info.st_mode), oct(info2.st_mode))
else:
self.assertEqual(
oct(info.st_mode & ~0o7777),
oct(info2.st_mode & ~0o7777),
)
self.assertEqual(info.st_size, info2.st_size)
if require_times:
self.assertEqual(
int(info.st_mtime),
int(info2.st_mtime),
)
def assert_tree_is_same(
self,
left,
right,
require_hard_links: bool = True,
require_permissions: bool = True,
require_times: bool = True,
):
self.assert_tree_is_superset(
left, right,
require_hard_links=require_hard_links,
require_permissions=require_permissions,
require_times=require_times,
)
self.assert_tree_is_superset(
right,
left,
require_hard_links=require_hard_links,
require_permissions=require_permissions,
require_times=require_times,
)
def test_empty(self) -> None:
content = b''
with tempfile.NamedTemporaryFile(
) as source, tempfile.TemporaryDirectory(
) as expected, tempfile.TemporaryDirectory(
) as dest:
source.write(content)
source.flush()
subprocess.run(
[
self.mtree_apply,
source.name,
dest,
],
check=True,
stdout=2,
)
self.assert_tree_is_same(
dest,
expected,
require_hard_links=False,
require_permissions=False,
require_times=False,
)
def test_populate(self) -> None:
content = b'''\
. type=dir
./foo/bar/\302\251 type=dir
./sym/link type=link link=/dev/null
./create type=file size=0
./make-executable type=file mode=755
./make-non-executable type=file mode=644 time=1597415889
'''
with tempfile.NamedTemporaryFile(
) as source, tempfile.TemporaryDirectory(
) as expected, tempfile.TemporaryDirectory(
) as dest:
source.write(content)
source.flush()
os.umask(0o077)
bar = (Path(expected) / 'foo' / 'bar')
bar.mkdir(parents=True)
non_ascii_filename = str(bar).encode('ascii') + b'/\302\251'
os.mkdir(non_ascii_filename)
(Path(expected) / 'sym').mkdir()
(Path(expected) / 'sym' / 'link').symlink_to('/dev/null')
for filename in 'make-executable', 'make-non-executable':
for d in Path(expected), Path(dest):
with open(str(d / filename), 'w') as writer:
writer.write('#!/bin/sh\n')
with open(str(Path(expected) / 'create'), 'w') as writer:
pass
# Paths that are not explicitly created get their permissions
# from umask
(Path(expected)).chmod(0o755)
os.chmod(non_ascii_filename, 0o755)
(Path(expected) / 'create').chmod(0o644)
(Path(expected) / 'make-executable').chmod(0o755)
(Path(expected) / 'make-non-executable').chmod(0o644)
os.utime(
str(Path(expected) / 'make-non-executable'),
times=(1597415889, 1597415889),
)
subprocess.run(
[
self.mtree_apply,
source.name,
dest,
],
check=True,
stdout=2,
)
subprocess.run(
[
'find',
dest,
'-ls',
],
check=True,
stdout=2,
)
self.assert_tree_is_same(
dest,
expected,
require_hard_links=False,
require_permissions=True,
require_times=False,
)
def test_populate_copy(self) -> None:
content = b'''\
# Content-addressed storage indexed by a truncated sha256
./make-executable type=file mode=755 contents=a8/076d3d28d21e02012b20eaf7dbf754
./make-non-executable type=file mode=644 time=1597415889
'''
with tempfile.NamedTemporaryFile(
) as source, tempfile.TemporaryDirectory(
) as reference, tempfile.TemporaryDirectory(
) as expected, tempfile.TemporaryDirectory(
) as dest:
source.write(content)
source.flush()
os.umask(0o077)
for filename in 'make-executable', 'make-non-executable':
with open(str(Path(expected) / filename), 'w') as writer:
writer.write('#!/bin/sh\n')
# Avoid Path.link_to() which wasn't in Python 3.5
os.link(
str(Path(expected) / 'make-non-executable'),
str(Path(reference) / 'make-non-executable'),
)
(Path(reference) / 'a8').mkdir()
os.link(
str(Path(expected) / 'make-executable'),
str(
Path(reference) / 'a8'
/ '076d3d28d21e02012b20eaf7dbf754'
),
)
subprocess.run(
[
self.mtree_apply,
source.name,
dest,
reference,
],
check=True,
stdout=2,
)
subprocess.run(
[
'find',
dest,
'-ls',
],
check=True,
stdout=2,
)
self.assert_tree_is_same(
dest,
expected,
require_hard_links=True,
require_permissions=True,
require_times=True,
)
info = (Path(dest) / 'make-executable').stat()
self.assertEqual(info.st_mode & 0o7777, 0o755)
info = (Path(dest) / 'make-non-executable').stat()
self.assertEqual(info.st_mode & 0o7777, 0o644)
self.assertEqual(info.st_mtime, 1597415889)
def tearDown(self) -> None:
super().tearDown()
if __name__ == '__main__':
assert sys.version_info >= (3, 5), \
'Python 3.5+ is required (configure with -Dpython=python3.5 ' \
'if necessary)'
test_main()
# vi: set sw=4 sts=4 et:
......@@ -84,9 +84,8 @@ EXES = [
WRAPPED = [
'bwrap',
]
MULTIARCH = [
HELPERS = [
'capsule-capture-libs',
'capsule-symbols',
]
SCRIPTS = [
'pressure-vessel-unruntime',
......@@ -200,10 +199,15 @@ def main():
path = os.path.join(relocatable_install, 'bin', exe)
check_dependencies(test, relocatable_install, path, is_wrapper=True)
for basename in MULTIARCH:
for basename in HELPERS:
for multiarch, ld_so in LD_SO:
exe = '{}-{}'.format(multiarch, basename)
path = os.path.join(relocatable_install, 'bin', exe)
path = os.path.join(
relocatable_install,
'libexec',
'steam-runtime-tools-0',
exe,
)
if isexec(path):
test.ok('{} exists and is executable'.format(path))
......
......@@ -128,11 +128,12 @@ test_resolve_in_sysroot (Fixture *f,
static const ResolveTest tests[] =
{
{ { "a/b/c/d" }, { "a/b/c/d" } },
{ { "a/b/c/d/" }, { "a/b/c/d" } },
{
{ "a/b/c/d", SRT_RESOLVE_FLAGS_NONE, RESOLVE_CALL_FLAGS_IGNORE_PATH },
{ "a/b/c/d" },
},
{ { "a/b/c/d", SRT_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d" } },
{ { "a/b/c/d/", SRT_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d" } },
{
{ "a/b/c/d", SRT_RESOLVE_FLAGS_MKDIR_P, RESOLVE_CALL_FLAGS_IGNORE_PATH },
{ "a/b/c/d" },
......@@ -150,10 +151,19 @@ test_resolve_in_sysroot (Fixture *f,
{ "a/b/c/d" }
},
{ { "a/b/c/file", SRT_RESOLVE_FLAGS_READABLE }, { "a/b/c/file" } },
{ { "a/b/c/file/" }, { NULL, G_IO_ERROR_NOT_DIRECTORY }},
{
{ "a/b/c/file", SRT_RESOLVE_FLAGS_DIRECTORY },
{ NULL, G_IO_ERROR_NOT_DIRECTORY }
},
{
{ "a/b/c/file/", SRT_RESOLVE_FLAGS_DIRECTORY },
{ NULL, G_IO_ERROR_NOT_DIRECTORY }
},
{
{ "a/b/c/file/", SRT_RESOLVE_FLAGS_READABLE },
{ NULL, G_IO_ERROR_NOT_DIRECTORY }
},
{
{ "a/b/c/file", SRT_RESOLVE_FLAGS_READABLE|SRT_RESOLVE_FLAGS_DIRECTORY },
{ NULL, G_IO_ERROR_NOT_DIRECTORY }
......@@ -161,8 +171,9 @@ test_resolve_in_sysroot (Fixture *f,
{ { "a/b///////.////./././///././c/d" }, { "a/b/c/d" } },
{ { "/a/b///////.////././../b2////././c2/d2" }, { "a/b2/c2/d2" } },
{ { "a/b/c/d/e/f" }, { NULL, G_IO_ERROR_NOT_FOUND } },
{ { "a/b/c/d/e/f/", SRT_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d/e/f" } },
{ { "a/b/c/d/e/f", SRT_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d/e/f" } },
{ { "a/b/c/d/e/f" }, { "a/b/c/d/e/f" } },
{ { "a/b/c/d/e/f/" }, { "a/b/c/d/e/f" } },
{ { "a/b/c/d/e/f", SRT_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d/e/f" } },
{ { "a3/b3/c3" }, { NULL, G_IO_ERROR_NOT_FOUND } },
{ { "a3/b3/c3", SRT_RESOLVE_FLAGS_MKDIR_P }, { "a3/b3/c3" } },
......
......@@ -34,6 +34,8 @@
#include "libglnx/libglnx.h"
#include "tests/test-utils.h"
#include "flatpak-utils-private.h"
#include "mtree.h"
#include "utils.h"
typedef struct
......@@ -280,14 +282,14 @@ test_envp_cmp (Fixture *f,
g_autofree gchar *copy = g_strdup (sorted[i]);
g_test_message ("%s == %s", copy, sorted[i]);
g_assert_cmpint (pv_envp_cmp (&copy, &sorted[i]), ==, 0);
g_assert_cmpint (pv_envp_cmp (&sorted[i], &copy), ==, 0);
g_assert_cmpint (flatpak_envp_cmp (&copy, &sorted[i]), ==, 0);
g_assert_cmpint (flatpak_envp_cmp (&sorted[i], &copy), ==, 0);
for (j = i + 1; j < G_N_ELEMENTS (sorted); j++)
{
g_test_message ("%s < %s", sorted[i], sorted[j]);
g_assert_cmpint (pv_envp_cmp (&sorted[i], &sorted[j]), <, 0);
g_assert_cmpint (pv_envp_cmp (&sorted[j], &sorted[i]), >, 0);
g_assert_cmpint (flatpak_envp_cmp (&sorted[i], &sorted[j]), <, 0);
g_assert_cmpint (flatpak_envp_cmp (&sorted[j], &sorted[i]), >, 0);
}
}
......@@ -296,7 +298,7 @@ test_envp_cmp (Fixture *f,
for (i = 0; i < G_N_ELEMENTS (unsorted); i++)
sort_this[i] = unsorted[i];
qsort (sort_this, G_N_ELEMENTS (unsorted), sizeof (char *), pv_envp_cmp);
qsort (sort_this, G_N_ELEMENTS (unsorted), sizeof (char *), flatpak_envp_cmp);
for (i = 0; i < G_N_ELEMENTS (sorted); i++)
g_assert_cmpstr (sorted[i], ==, sort_this[i]);
......@@ -305,42 +307,121 @@ test_envp_cmp (Fixture *f,
}
static void
test_get_path_after (Fixture *f,
gconstpointer context)
test_mtree_entry_parse (Fixture *f,
gconstpointer context)
{
static const struct
{
const char *str;
const char *prefix;
const char *expected;
const char *line;
const char *name;
PvMtreeEntry expected;
gboolean error;
const char *link;
const char *sha256;
} tests[] =
{
{ "/run/host/usr", "/run/host", "usr" },
{ "/run/host/usr", "/run/host/", "usr" },
{ "/run/host", "/run/host", "" },
{ "////run///host////usr", "//run//host", "usr" },
{ "////run///host////usr", "//run//host////", "usr" },
{ "/run/hostage", "/run/host", NULL },
/* Any number of leading slashes is ignored, even zero */
{ "foo/bar", "/foo", "bar" },
{ "/foo/bar", "foo", "bar" },
{ "#mtree",
NULL,
{ .size = -1, .mtime_usec = -1, .mode = -1,
.kind = PV_MTREE_ENTRY_KIND_UNKNOWN },
},
{ "",
NULL,
{ .size = -1, .mtime_usec = -1, .mode = -1,
.kind = PV_MTREE_ENTRY_KIND_UNKNOWN },
},
{ ". type=dir",
".",
{ .size = -1, .mtime_usec = -1, .mode = -1,
.kind = PV_MTREE_ENTRY_KIND_DIR },
},
{ "./foo type=file sha256=ffff mode=0640 size=42 time=1597415889.5",
"./foo",
{ .size = 42,
.mtime_usec = 1597415889 * G_TIME_SPAN_SECOND + (G_TIME_SPAN_SECOND / 2),
.mode = 0640,
.kind = PV_MTREE_ENTRY_KIND_FILE },
.sha256 = "ffff",
},
{ "./foo type=file sha256digest=ffff mode=4755",
"./foo",
{ .size = -1, .mtime_usec = -1, .mode = 04755,
.kind = PV_MTREE_ENTRY_KIND_FILE },
.sha256 = "ffff",
},
{ "./foo type=file sha256=ffff sha256digest=ffff",
"./foo",
{ .size = -1, .mtime_usec = -1, .mode = -1,
.kind = PV_MTREE_ENTRY_KIND_FILE },
.sha256 = "ffff",
},
{ "./symlink type=link link=/dev/null",
"./symlink",
{ .size = -1, .mtime_usec = -1, .mode = -1,
.kind = PV_MTREE_ENTRY_KIND_LINK },
.link = "/dev/null",
},
{ "./silly-name/\\001\\123\\n\\r type=link link=\\\"\\\\\\b",
"./silly-name/\001\123\n\r",
{ .size = -1, .mtime_usec = -1, .mode = -1,
.kind = PV_MTREE_ENTRY_KIND_LINK },
.link = "\"\\\b",
},
{ ("./ignore cksum=123 device=456 contents=./ignore flags=123 gid=123 "
"gname=users ignore=1 inode=123 md5=ffff md5digest=ffff nlink=1 "
"nochange=1 optional=1 resdevice=123 "
"ripemd160digest=ffff rmd160=ffff rmd160digest=ffff "
"sha1=ffff sha1digest=ffff "
"sha384=ffff sha384digest=ffff "
"sha512=ffff sha512digest=ffff "
"uid=0 uname=root type=dir"),
"./ignore",
{ .size = -1, .mtime_usec = -1, .mode = -1,
.kind = PV_MTREE_ENTRY_KIND_DIR },
},
{ "./foo type=file sha256=ffff sha256digest=eeee", .error = TRUE },
{ "./foo type=file mode=1a", .error = TRUE },
{ "/set type=dir", .error = TRUE },
{ "../escape type=dir", .error = TRUE },
{ "relative type=dir", .error = TRUE },
{ "./foo link", .error = TRUE },
{ "./foo type=bar", .error = TRUE },
{ "./continuation type=dir \\", .error = TRUE },
{ "./alert type=link link=\\a", .error = TRUE },
{ "./hex type=link link=\\x12", .error = TRUE },
{ "./symlink type=file link=/dev/null", .error = TRUE },
{ "./symlink type=link", .error = TRUE },
{ " ", .error = TRUE },
};
gsize i;
for (i = 0; i < G_N_ELEMENTS (tests); i++)
{
const char *str = tests[i].str;
const char *prefix = tests[i].prefix;
const char *expected = tests[i].expected;
const char *line = tests[i].line;
PvMtreeEntry expected = tests[i].expected;
g_auto(PvMtreeEntry) got = PV_MTREE_ENTRY_BLANK;
g_autoptr(GError) error = NULL;
if (expected == NULL)
g_test_message ("%s should not have path prefix %s",
str, prefix);
else
g_test_message ("%s should have path prefix %s followed by %s",
str, prefix, expected);
g_test_message ("%s", line);
pv_mtree_entry_parse (line, &got, "test.mtree", 1, &error);
g_assert_cmpstr (pv_get_path_after (str, prefix), ==, expected);
if (tests[i].error)
{
g_assert_nonnull (error);
g_test_message ("-> %s", error->message);
}
else
{
g_assert_no_error (error);
g_test_message ("-> OK");
g_assert_cmpstr (got.name, ==, tests[i].name);
g_assert_cmpstr (got.link, ==, tests[i].link);
g_assert_cmpstr (got.sha256, ==, tests[i].sha256);
g_assert_cmpint (got.size, ==, expected.size);
g_assert_cmpint (got.mtime_usec, ==, expected.mtime_usec);
g_assert_cmpint (got.mode, ==, expected.mode);
g_assert_cmpint (got.kind, ==, expected.kind);
}
}
}
......@@ -387,8 +468,8 @@ main (int argc,
g_test_add ("/delete-dangling-symlink", Fixture, NULL,
setup, test_delete_dangling_symlink, teardown);
g_test_add ("/envp-cmp", Fixture, NULL, setup, test_envp_cmp, teardown);
g_test_add ("/get-path-after", Fixture, NULL,
setup, test_get_path_after, teardown);
g_test_add ("/mtree-entry-parse", Fixture, NULL,
setup, test_mtree_entry_parse, teardown);
g_test_add ("/search-path-append", Fixture, NULL,
setup, test_search_path_append, teardown);
......
......@@ -1864,15 +1864,12 @@ overrides (Fixture *f,
if (strstr (output[i], "/run/host/usr/lib/libgcc_s.so.1") != NULL)
seen_link = TRUE;
}
/* The overrides folder contains 4 folders plus the root folder, plus 4 files,
* plus one ".keep" file */
g_assert_cmpint (i, ==, 10);
/* The overrides folder contains 4 folders, plus 4 files, plus one ".keep" file */
g_assert_cmpint (i, ==, 9);
g_assert_true (seen_link);
g_strfreev (output);
g_assert_nonnull (issues);
g_assert_cmpstr (issues[0], ==, NULL);
g_strfreev (issues);
g_assert_null (issues);
/* Repeat the same check, this time using the cached result */
output = srt_system_info_list_pressure_vessel_overrides (info, &issues);
......@@ -1883,13 +1880,11 @@ overrides (Fixture *f,
if (strstr (output[i], "/run/host/usr/lib/libgcc_s.so.1") != NULL)
seen_link = TRUE;
}
g_assert_cmpint (i, ==, 10);
g_assert_cmpint (i, ==, 9);
g_assert_true (seen_link);
g_strfreev (output);
g_assert_nonnull (issues);
g_assert_cmpstr (issues[0], ==, NULL);
g_strfreev (issues);
g_assert_null (issues);
g_object_unref (info);
g_free (sysroot);
......@@ -1939,11 +1934,10 @@ overrides_issues (Fixture *f,
if (strstr (output[i], "/run/host/usr/lib/libgcc_s.so.1") != NULL)
seen_link = TRUE;
}
/* The overrides folder contains 4 folders plus the root folder, plus one symlink,
* plus 2 ".keep" files.
/* The overrides folder contains 4 folders, plus one symlink, plus 2 ".keep" files.
* We expect to not be able to open the "lib" folder, so we should have 4 less items than
* a "normal" scenario */
g_assert_cmpint (i, ==, 4);
g_assert_cmpint (i, ==, 3);
/* We expect not to be able to reach the symlink */
g_assert_false (seen_link);
g_strfreev (output);
......@@ -2038,15 +2032,12 @@ pinned_libraries (Fixture *f,
if (strstr (values[i], "has_pins") != NULL)
seen_pins = TRUE;
}
/* We placed 3 files in `pinned_libs_32`. We expect to have 3 files plus the folder
* itself */
g_assert_cmpint (i, ==, 4);
/* We placed 3 files in `pinned_libs_32` */
g_assert_cmpint (i, ==, 3);
g_assert_true (seen_pins);
g_strfreev (values);
g_assert_nonnull (messages);
g_assert_cmpstr (messages[0], ==, NULL);
g_strfreev (messages);
g_assert_null (messages);
/* Repeat the same check, this time using the cached values */
values = srt_system_info_list_pinned_libs_32 (info, &messages);
......@@ -2057,13 +2048,11 @@ pinned_libraries (Fixture *f,
if (strstr (values[i], "has_pins") != NULL)
seen_pins = TRUE;
}
g_assert_cmpint (i, ==, 4);
g_assert_cmpint (i, ==, 3);
g_assert_true (seen_pins);
g_strfreev (values);
g_assert_nonnull (messages);
g_assert_cmpstr (messages[0], ==, NULL);
g_strfreev (messages);
g_assert_null (messages);
g_free (target1);
g_free (target2);
......@@ -2103,13 +2092,11 @@ pinned_libraries (Fixture *f,
if (strstr (values[i], "has_pins") != NULL)
seen_pins = TRUE;
}
g_assert_cmpint (i, ==, 4);
g_assert_cmpint (i, ==, 3);
g_assert_true (seen_pins);
g_strfreev (values);
g_assert_nonnull (messages);
g_assert_cmpstr (messages[0], ==, NULL);
g_strfreev (messages);
g_assert_null (messages);
/* Repeat the same check, this time using the cached values */
values = srt_system_info_list_pinned_libs_64 (info, &messages);
......@@ -2120,13 +2107,11 @@ pinned_libraries (Fixture *f,
if (strstr (values[i], "has_pins") != NULL)
seen_pins = TRUE;
}
g_assert_cmpint (i, ==, 4);
g_assert_cmpint (i, ==, 3);
g_assert_true (seen_pins);
g_strfreev (values);
g_assert_nonnull (messages);
g_assert_cmpstr (messages[0], ==, NULL);
g_strfreev (messages);
g_assert_null (messages);
g_free (target1);
g_free (target2);
......@@ -2177,9 +2162,8 @@ pinned_libraries_permission (Fixture *f,
if (strstr (values[i], "no_access") != NULL)
seen_no_access = TRUE;
}
/* We placed 1 folder in `pinned_libs_32`. We expect to have 1 folder plus the
* parent folder itself */
g_assert_cmpint (i, ==, 2);
/* We placed 1 folder in `pinned_libs_32` */
g_assert_cmpint (i, ==, 1);
g_assert_true (seen_no_access);
g_strfreev (values);
......@@ -2208,9 +2192,8 @@ pinned_libraries_permission (Fixture *f,
if (strstr (values[i], "no_access") != NULL)
seen_no_access = TRUE;
}
/* We placed 1 folder in `pinned_libs_32`. We expect to have 1 folder plus the
* parent folder itself */
g_assert_cmpint (i, ==, 2);
/* We placed 1 folder in `pinned_libs_32` */
g_assert_cmpint (i, ==, 1);
g_assert_true (seen_no_access);
g_strfreev (values);
......@@ -2242,9 +2225,7 @@ pinned_libraries_missing (Fixture *f,
g_assert_cmpint (g_rmdir (fake_home->pinned_32), ==, 0);
values = srt_system_info_list_pinned_libs_32 (info, &messages);
g_assert_nonnull (values);
g_assert_cmpstr (values[0], ==, NULL);
g_strfreev (values);
g_assert_null (values);
g_assert_nonnull (messages);
g_assert_cmpstr (strstr (messages[0], "pinned_libs_32"), !=, NULL);
......@@ -2257,9 +2238,7 @@ pinned_libraries_missing (Fixture *f,
g_assert_cmpint (g_rmdir (fake_home->pinned_64), ==, 0);
values = srt_system_info_list_pinned_libs_64 (info, &messages);
g_assert_nonnull (values);
g_assert_cmpstr (values[0], ==, NULL);
g_strfreev (values);
g_assert_null (values);
g_assert_nonnull (messages);
g_assert_cmpstr (strstr (messages[0], "pinned_libs_64"), !=, NULL);
......@@ -2633,16 +2612,16 @@ static const JsonTest json_test[] =
.issues = SRT_RUNTIME_ISSUES_NONE,
.pinned_libs_32 =
{
" 13902790 4 drwxr-xr-x 2 me me 4096 Apr 24 21:32 pinned_libs_32",
" 13902813 4 -rw-r--r-- 1 me me 50 Apr 24 21:32 pinned_libs_32/system_libGLU.so.1",
" 13902814 4 lrwxrwxrwx 1 me me 107 Apr 24 21:32 pinned_libs_32/libdbusmenu-gtk.so.4 -> /home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/i386-linux-gnu/libdbusmenu-gtk.so.4.0.13",
"pinned_libs_32/has_pins",
"pinned_libs_32/libdbusmenu-gtk.so.4 -> /home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/i386-linux-gnu/libdbusmenu-gtk.so.4.0.13",
"pinned_libs_32/system_libGLU.so.1",
NULL,
},
.pinned_libs_64 =
{
" 13902791 4 drwxr-xr-x 2 me me 4096 Apr 24 21:32 pinned_libs_64",
" 13902805 4 -rw-r--r-- 1 me me 46 Apr 24 21:32 pinned_libs_64/system_libGLU.so.1",
" 13902795 4 lrwxrwxrwx 1 me me 100 Apr 24 21:32 pinned_libs_64/libjack.so.0 -> /home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/x86_64-linux-gnu/libjack.so.0.1.0",
"pinned_libs_64/has_pins",
"pinned_libs_64/libjack.so.0 -> /home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/x86_64-linux-gnu/libjack.so.0.1.0",
"pinned_libs_64/system_libGLU.so.1",
NULL,
},
},
......
......@@ -264,6 +264,46 @@ test_file_in_sysroot (Fixture *f,
}
}
static void
test_get_path_after (Fixture *f,
gconstpointer context)
{
static const struct
{
const char *str;
const char *prefix;
const char *expected;
} tests[] =
{
{ "/run/host/usr", "/run/host", "usr" },
{ "/run/host/usr", "/run/host/", "usr" },
{ "/run/host", "/run/host", "" },
{ "////run///host////usr", "//run//host", "usr" },
{ "////run///host////usr", "//run//host////", "usr" },
{ "/run/hostage", "/run/host", NULL },
/* Any number of leading slashes is ignored, even zero */
{ "foo/bar", "/foo", "bar" },
{ "/foo/bar", "foo", "bar" },
};
gsize i;
for (i = 0; i < G_N_ELEMENTS (tests); i++)
{
const char *str = tests[i].str;
const char *prefix = tests[i].prefix;
const char *expected = tests[i].expected;
if (expected == NULL)
g_test_message ("%s should not have path prefix %s",
str, prefix);
else
g_test_message ("%s should have path prefix %s followed by %s",
str, prefix, expected);
g_assert_cmpstr (_srt_get_path_after (str, prefix), ==, expected);
}
}
/*
* Test _srt_filter_gameoverlayrenderer function.
*/
......@@ -430,6 +470,8 @@ main (int argc,
setup, test_file_in_sysroot, teardown);
g_test_add ("/utils/filter_gameoverlayrenderer", Fixture, NULL, setup,
filter_gameoverlayrenderer, teardown);
g_test_add ("/utils/get-path-after", Fixture, NULL,
setup, test_get_path_after, teardown);
g_test_add ("/utils/same-file", Fixture, NULL,
setup, test_same_file, teardown);
g_test_add ("/utils/str_is_integer", Fixture, NULL,
......