Re: [pacman-dev] [PATCH 3/3] meson: use hidden symbol visiblity by default

2020-12-23 Thread Eli Schwartz

On 12/23/20 5:42 PM, Emil Velikov wrote:

All the required public API is annotated with SYMEXPORT, so we can just
add the meson notation, to hide all the symbols by default.

Thus we no longer spill all the internal API into the global namespace.
Thanks for noticing this... it's a regression from autotools, which 
contained in lib/libalpm/Makefile.am:


if ENABLE_VISIBILITY_CC
if DARWIN
AM_CFLAGS += -fvisibility=hidden
else
AM_CFLAGS += -fvisibility=internal
endif
endif


I wonder if we had a good reason to use "internal" and if we should 
continue to do so? IIUC it makes it slightly more optimized at the cost 
of allowing pointers into private functions (e.g. callbacks) used by 
other programs to segfault.


--
Eli Schwartz
Bug Wrangler and Trusted User



OpenPGP_signature
Description: OpenPGP digital signature


Re: [pacman-dev] [PATCH 1/3] meson: replace objects with link_whole

2020-12-23 Thread Eli Schwartz

On 12/23/20 5:42 PM, Emil Velikov wrote:

Instead extracting/listing objects from existing static libraries,
simply add those to the link list. Note that link_whole is needed
otherwise, unused code will be pruned.

This is intentional! link_whole is broken, just like the comment says.

It is fixed in newer versions of meson than the current minimum. The fix 
is that link_whole does precisely what extract_all_objects() does. In 
other words, link_whole is syntactic sugar for extract_all_objects().


I have intended to switch to link_whole, but *only* once we bump the 
minimum version of meson to something that guarantees the result is 
functional.


I didn't want to bump the minimum version of meson just for this.

--
Eli Schwartz
Bug Wrangler and Trusted User



OpenPGP_signature
Description: OpenPGP digital signature


Re: [pacman-dev] [PATCH 1/2] libalpm: const annotate struct pkg_operations

2020-12-23 Thread Allan McRae
On 24/12/20 8:41 am, Emil Velikov wrote:
> Signed-off-by: Emil Velikov 
> ---

This patch does a lot more than just add const annotations.  And it
looks to me like the extra bit needs justification, so separate patch
please.

>  lib/libalpm/be_local.c   |  2 +-
>  lib/libalpm/be_package.c |  2 +-
>  lib/libalpm/be_sync.c| 20 ++--
>  lib/libalpm/package.c|  2 +-
>  lib/libalpm/package.h|  4 ++--
>  5 files changed, 23 insertions(+), 7 deletions(-)
> 
> diff --git a/lib/libalpm/be_local.c b/lib/libalpm/be_local.c
> index e73a97bb..9c3c8bb3 100644
> --- a/lib/libalpm/be_local.c
> +++ b/lib/libalpm/be_local.c
> @@ -326,7 +326,7 @@ static int _cache_force_load(alpm_pkg_t *pkg)
>   * lazy accessor methods that handle any backend loading and caching
>   * logic.
>   */
> -static struct pkg_operations local_pkg_ops = {
> +static const struct pkg_operations local_pkg_ops = {
>   .get_base = _cache_get_base,
>   .get_desc = _cache_get_desc,
>   .get_url = _cache_get_url,
> diff --git a/lib/libalpm/be_package.c b/lib/libalpm/be_package.c
> index 4dde7167..f855003a 100644
> --- a/lib/libalpm/be_package.c
> +++ b/lib/libalpm/be_package.c
> @@ -138,7 +138,7 @@ static int _package_changelog_close(const alpm_pkg_t 
> UNUSED *pkg, void *fp)
>   * majority of the default_pkg_ops struct and add only a few operations of
>   * our own on top.
>   */
> -static struct pkg_operations *get_file_pkg_ops(void)
> +static const struct pkg_operations *get_file_pkg_ops(void)
>  {
>   static struct pkg_operations file_pkg_ops;
>   static int file_pkg_ops_initialized = 0;
> diff --git a/lib/libalpm/be_sync.c b/lib/libalpm/be_sync.c
> index 225df76d..5356b544 100644
> --- a/lib/libalpm/be_sync.c
> +++ b/lib/libalpm/be_sync.c
> @@ -281,6 +281,23 @@ static int _sync_get_validation(alpm_pkg_t *pkg)
>   return pkg->validation;
>  }
>  
> +/** Package sync operations struct accessor. We implement this as a method
> + * rather than a static struct as in be_files because we want to reuse the
> + * majority of the default_pkg_ops struct and add only a few operations of
> + * our own on top.
> + */
> +static const struct pkg_operations *get_sync_pkg_ops(void)
> +{
> + static struct pkg_operations sync_pkg_ops;
> + static int sync_pkg_ops_initalized = 0;
> + if(!sync_pkg_ops_initalized) {
> + sync_pkg_ops = default_pkg_ops;
> + sync_pkg_ops.get_validation = _sync_get_validation;
> + sync_pkg_ops_initalized = 1;
> + }
> + return _pkg_ops;
> +}
> +
>  static alpm_pkg_t *load_pkg_for_entry(alpm_db_t *db, const char *entryname,
>   const char **entry_filename, alpm_pkg_t *likely_pkg)
>  {
> @@ -321,8 +338,7 @@ static alpm_pkg_t *load_pkg_for_entry(alpm_db_t *db, 
> const char *entryname,
>  
>   pkg->origin = ALPM_PKG_FROM_SYNCDB;
>   pkg->origin_data.db = db;
> - pkg->ops = _pkg_ops;
> - pkg->ops->get_validation = _sync_get_validation;
> + pkg->ops = get_sync_pkg_ops();
>   pkg->handle = db->handle;
>  
>   /* add to the collection */
> diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c
> index a4356518..5766c600 100644
> --- a/lib/libalpm/package.c
> +++ b/lib/libalpm/package.c
> @@ -138,7 +138,7 @@ static int _pkg_force_load(alpm_pkg_t UNUSED *pkg) { 
> return 0; }
>  /** The standard package operations struct. Get fields directly from the
>   * struct itself with no abstraction layer or any type of lazy loading.
>   */
> -struct pkg_operations default_pkg_ops = {
> +const struct pkg_operations default_pkg_ops = {
>   .get_base= _pkg_get_base,
>   .get_desc= _pkg_get_desc,
>   .get_url = _pkg_get_url,
> diff --git a/lib/libalpm/package.h b/lib/libalpm/package.h
> index c37bd11e..b134ad5a 100644
> --- a/lib/libalpm/package.h
> +++ b/lib/libalpm/package.h
> @@ -83,7 +83,7 @@ struct pkg_operations {
>   * The actual definition is in package.c so it can have access to the
>   * default accessor functions which are defined there.
>   */
> -extern struct pkg_operations default_pkg_ops;
> +extern const struct pkg_operations default_pkg_ops;
>  
>  struct __alpm_pkg_t {
>   unsigned long name_hash;
> @@ -121,7 +121,7 @@ struct __alpm_pkg_t {
>   alpm_list_t *removes; /* in transaction targets only */
>   alpm_pkg_t *oldpkg; /* in transaction targets only */
>  
> - struct pkg_operations *ops;
> + const struct pkg_operations *ops;
>  
>   alpm_filelist_t files;
>  
> 


[pacman-dev] [PATCH 3/3] pacman: add file checksum validation against mtree

2020-12-23 Thread Emil Velikov
With libarchive v3.5.0 we have API to fetch the digest from the mtree.
Use that to validate if the installed files are modified or not.

As always, a modified backup file will trigger a warning but will not
result in an actual failure.

TODO: localization... no idea how that is even done :-)
NOTE: indentation is likely all over the place - first time I see ts=2

Signed-off-by: Emil Velikov 
---
 src/pacman/check.c | 66 +-
 1 file changed, 59 insertions(+), 7 deletions(-)

diff --git a/src/pacman/check.c b/src/pacman/check.c
index 02217d0f..083c547d 100644
--- a/src/pacman/check.c
+++ b/src/pacman/check.c
@@ -176,19 +176,70 @@ static int check_file_size(const char *pkgname, const 
char *filepath,
return 0;
 }
 
-/* placeholders - libarchive currently does not read checksums from mtree files
-static int check_file_md5sum(const char *pkgname, const char *filepath,
-   struct stat *st, struct archive_entry *entry, int backup)
+#if ARCHIVE_VERSION_NUMBER >= 3005000
+static int check_file_cksum(const char *pkgname, const char *filepath,
+   int backup, const char *cksum_name, const char *cksum_calc, 
const char *cksum_mtree)
 {
+   if(!cksum_calc || !cksum_mtree) {
+   if(!config->quiet) {
+   pm_printf(ALPM_LOG_WARNING, _("%s: %s (failed to 
compute %s checksum)\n"),
+   pkgname, filepath, cksum_name);
+   }
+   return 1;
+   }
+
+   if(strcmp(cksum_calc, cksum_mtree) != 0) {
+   if(backup) {
+   if(!config->quiet) {
+   printf("%s%s%s: ", config->colstr.title, 
_("backup file"),
+   config->colstr.nocolor);
+   printf(_("%s: %s (%s checksum mismatch)\n"),
+   pkgname, filepath, cksum_name);
+   }
+   return 0;
+   }
+   if(!config->quiet) {
+   pm_printf(ALPM_LOG_WARNING, _("%s: %s (%s checksum 
mismatch)\n"),
+   pkgname, filepath, cksum_name);
+   }
+   return 1;
+   }
+
return 0;
 }
+#endif
+
+static int check_file_md5sum(const char *pkgname, const char *filepath,
+   struct archive_entry *entry, int backup)
+{
+   int errors = 0;
+#if ARCHIVE_VERSION_NUMBER >= 3005000
+   char *cksum_calc = alpm_compute_md5sum(filepath);
+   char *cksum_mtree = hex_representation(archive_entry_digest(entry,
+   
ARCHIVE_ENTRY_DIGEST_MD5), 16);
+   errors = check_file_cksum(pkgname, filepath, backup, "MD5", cksum_calc,
+   
cksum_mtree);
+   free(cksum_mtree);
+   free(cksum_calc);
+#endif
+   return (errors != 0 ? 1 : 0);
+}
 
 static int check_file_sha256sum(const char *pkgname, const char *filepath,
-   struct stat *st, struct archive_entry *entry, int backup)
+   struct archive_entry *entry, int backup)
 {
-   return 0;
+   int errors = 0;
+#if ARCHIVE_VERSION_NUMBER >= 3005000
+   char *cksum_calc = alpm_compute_sha256sum(filepath);
+   char *cksum_mtree = hex_representation(archive_entry_digest(entry,
+   
ARCHIVE_ENTRY_DIGEST_SHA256), 32);
+   errors = check_file_cksum(pkgname, filepath, backup, "SHA256", 
cksum_calc,
+   
cksum_mtree);
+   free(cksum_mtree);
+   free(cksum_calc);
+#endif
+   return (errors != 0 ? 1 : 0);
 }
-*/
 
 /* Loop through the files of the package to check if they exist. */
 int check_pkg_fast(alpm_pkg_t *pkg)
@@ -369,7 +420,8 @@ int check_pkg_full(alpm_pkg_t *pkg)
 
if(type == AE_IFREG) {
file_errors += check_file_size(pkgname, filepath, , 
entry, backup);
-   /* file_errors += check_file_md5sum(pkgname, filepath, 
, entry, backup); */
+   file_errors += check_file_md5sum(pkgname, filepath, 
entry, backup);
+   file_errors += check_file_sha256sum(pkgname, filepath, 
entry, backup);
}
 
if(config->quiet && file_errors) {
-- 
2.29.2


[pacman-dev] [PATCH 2/3] Move hex_representation() to src/common

2020-12-23 Thread Emil Velikov
We'll reuse the function in pacman with a later commit.

Signed-off-by: Emil Velikov 
---
 lib/libalpm/util.c   | 24 
 src/common/util-common.c | 26 ++
 src/common/util-common.h |  1 +
 3 files changed, 27 insertions(+), 24 deletions(-)

diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c
index b70a8192..9ae08745 100644
--- a/lib/libalpm/util.c
+++ b/lib/libalpm/util.c
@@ -1014,30 +1014,6 @@ static int sha256_file(const char *path, unsigned char 
output[32])
 }
 #endif /* HAVE_LIBSSL || HAVE_LIBNETTLE */
 
-/** Create a string representing bytes in hexadecimal.
- * @param bytes the bytes to represent in hexadecimal
- * @param size number of bytes to consider
- * @return a NULL terminated string with the hexadecimal representation of
- * bytes or NULL on error. This string must be freed.
- */
-static char *hex_representation(const unsigned char *bytes, size_t size)
-{
-   static const char *hex_digits = "0123456789abcdef";
-   char *str;
-   size_t i;
-
-   MALLOC(str, 2 * size + 1, return NULL);
-
-   for(i = 0; i < size; i++) {
-   str[2 * i] = hex_digits[bytes[i] >> 4];
-   str[2 * i + 1] = hex_digits[bytes[i] & 0x0f];
-   }
-
-   str[2 * size] = '\0';
-
-   return str;
-}
-
 char SYMEXPORT *alpm_compute_md5sum(const char *filename)
 {
unsigned char output[16];
diff --git a/src/common/util-common.c b/src/common/util-common.c
index 7d43ac0d..fc892c11 100644
--- a/src/common/util-common.c
+++ b/src/common/util-common.c
@@ -25,6 +25,32 @@
 #include "util-common.h"
 
 
+/** Create a string representing bytes in hexadecimal.
+ * @param bytes the bytes to represent in hexadecimal
+ * @param size number of bytes to consider
+ * @return a NULL terminated string with the hexadecimal representation of
+ * bytes or NULL on error. This string must be freed.
+ */
+char *hex_representation(const unsigned char *bytes, size_t size)
+{
+   static const char *hex_digits = "0123456789abcdef";
+   char *str = malloc(2 * size + 1);
+   size_t i;
+
+   if(!str) {
+   return NULL;
+   }
+
+   for(i = 0; i < size; i++) {
+   str[2 * i] = hex_digits[bytes[i] >> 4];
+   str[2 * i + 1] = hex_digits[bytes[i] & 0x0f];
+   }
+
+   str[2 * size] = '\0';
+
+   return str;
+}
+
 /** Parse the basename of a program from a path.
 * @param path path to parse basename from
 *
diff --git a/src/common/util-common.h b/src/common/util-common.h
index 483d5da4..d9b6b4b7 100644
--- a/src/common/util-common.h
+++ b/src/common/util-common.h
@@ -23,6 +23,7 @@
 #include 
 #include  /* struct stat */
 
+char *hex_representation(const unsigned char *bytes, size_t size);
 const char *mbasename(const char *path);
 char *mdirname(const char *path);
 
-- 
2.29.2


[pacman-dev] [PATCH 1/3] Remove pre libarchive 3.0 code

2020-12-23 Thread Emil Velikov
Pacman has required libarchive 3.0 or later for quite some time mow.

Signed-off-by: Emil Velikov 
---
 lib/libalpm/libarchive-compat.h | 20 
 1 file changed, 20 deletions(-)

diff --git a/lib/libalpm/libarchive-compat.h b/lib/libalpm/libarchive-compat.h
index 0ff005ae..596fd664 100644
--- a/lib/libalpm/libarchive-compat.h
+++ b/lib/libalpm/libarchive-compat.h
@@ -24,48 +24,28 @@
 
 static inline int _alpm_archive_read_free(struct archive *archive)
 {
-#if ARCHIVE_VERSION_NUMBER >= 300
return archive_read_free(archive);
-#else
-   return archive_read_finish(archive);
-#endif
 }
 
 static inline int64_t _alpm_archive_compressed_ftell(struct archive *archive)
 {
-#if ARCHIVE_VERSION_NUMBER >= 300
return archive_filter_bytes(archive, -1);
-#else
-   return archive_position_compressed(archive);
-#endif
 }
 
 static inline int _alpm_archive_read_open_file(struct archive *archive,
const char *filename, size_t block_size)
 {
-#if ARCHIVE_VERSION_NUMBER >= 300
return archive_read_open_filename(archive, filename, block_size);
-#else
-   return archive_read_open_file(archive, filename, block_size);
-#endif
 }
 
 static inline int _alpm_archive_filter_code(struct archive *archive)
 {
-#if ARCHIVE_VERSION_NUMBER >= 300
return archive_filter_code(archive, 0);
-#else
-   return archive_compression(archive);
-#endif
 }
 
 static inline int _alpm_archive_read_support_filter_all(struct archive 
*archive)
 {
-#if ARCHIVE_VERSION_NUMBER >= 300
return archive_read_support_filter_all(archive);
-#else
-   return archive_read_support_compression_all(archive);
-#endif
 }
 
 #endif /* LIBARCHIVE_COMPAT_H */
-- 
2.29.2


[pacman-dev] [PATCH 3/3] meson: use hidden symbol visiblity by default

2020-12-23 Thread Emil Velikov
All the required public API is annotated with SYMEXPORT, so we can just
add the meson notation, to hide all the symbols by default.

Thus we no longer spill all the internal API into the global namespace.

Signed-off-by: Emil Velikov 
---
 meson.build | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/meson.build b/meson.build
index 380564bb..6173f2b7 100644
--- a/meson.build
+++ b/meson.build
@@ -303,6 +303,7 @@ libcommon = static_library(
   'common',
   libcommon_sources,
   include_directories : includes,
+  gnu_symbol_visibility : 'hidden',
   install : false)
 
 alpm_deps = [crypto_provider, libarchive, libcurl, libintl, gpgme]
@@ -312,6 +313,7 @@ libalpm_a = static_library(
   libalpm_sources,
   link_whole: libcommon,
   include_directories : includes,
+  gnu_symbol_visibility : 'hidden',
   dependencies : alpm_deps)
 
 libalpm = library(
-- 
2.29.2


[pacman-dev] [PATCH 2/3] meson: pacman-conf add missing libcommon link

2020-12-23 Thread Emil Velikov
Currently, we are erroneously exporting all the symbols via the
libalpm.so. As such, the libcommon dependency is resolved.

The libalpm.so exports are about to be resolved shortly, yet that
exposed that pacman-conf is missing a link against libcommon.

Signed-off-by: Emil Velikov 
---
 meson.build | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/meson.build b/meson.build
index fd573826..380564bb 100644
--- a/meson.build
+++ b/meson.build
@@ -346,7 +346,7 @@ executable(
   'pacman-conf',
   pacman_conf_sources,
   include_directories : includes,
-  link_with : [libalpm],
+  link_with : [libalpm, libcommon],
   dependencies : [libarchive],
   install : true,
 )
-- 
2.29.2


[pacman-dev] [PATCH 1/3] meson: replace objects with link_whole

2020-12-23 Thread Emil Velikov
Instead extracting/listing objects from existing static libraries,
simply add those to the link list. Note that link_whole is needed
otherwise, unused code will be pruned.

Signed-off-by: Emil Velikov 
---
 meson.build | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/meson.build b/meson.build
index 264c6501..fd573826 100644
--- a/meson.build
+++ b/meson.build
@@ -310,15 +310,14 @@ alpm_deps = [crypto_provider, libarchive, libcurl, 
libintl, gpgme]
 libalpm_a = static_library(
   'alpm_objlib',
   libalpm_sources,
-  # https://github.com/mesonbuild/meson/issues/3937
-  objects : libcommon.extract_all_objects(),
+  link_whole: libcommon,
   include_directories : includes,
   dependencies : alpm_deps)
 
 libalpm = library(
   'alpm',
   version : libalpm_version,
-  objects: libalpm_a.extract_all_objects(recursive: true),
+  link_whole: libalpm_a,
   dependencies : alpm_deps,
   install : true)
 
-- 
2.29.2


[pacman-dev] [PATCH 2/2] libalpm: const annotate struct db_operations

2020-12-23 Thread Emil Velikov
Signed-off-by: Emil Velikov 
---
 lib/libalpm/be_local.c | 2 +-
 lib/libalpm/db.h   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/libalpm/be_local.c b/lib/libalpm/be_local.c
index 9c3c8bb3..1d7748d5 100644
--- a/lib/libalpm/be_local.c
+++ b/lib/libalpm/be_local.c
@@ -1158,7 +1158,7 @@ int SYMEXPORT alpm_pkg_set_reason(alpm_pkg_t *pkg, 
alpm_pkgreason_t reason)
return 0;
 }
 
-struct db_operations local_db_ops = {
+static const struct db_operations local_db_ops = {
.validate = local_db_validate,
.populate = local_db_populate,
.unregister   = _alpm_db_unregister,
diff --git a/lib/libalpm/db.h b/lib/libalpm/db.h
index c642e077..bc0f8bfc 100644
--- a/lib/libalpm/db.h
+++ b/lib/libalpm/db.h
@@ -70,7 +70,7 @@ struct __alpm_db_t {
alpm_pkghash_t *pkgcache;
alpm_list_t *grpcache;
alpm_list_t *servers;
-   struct db_operations *ops;
+   const struct db_operations *ops;
 
/* bitfields for validity, local, loaded caches, etc. */
/* From _alpm_dbstatus_t */
-- 
2.29.2


[pacman-dev] [PATCH 1/2] libalpm: const annotate struct pkg_operations

2020-12-23 Thread Emil Velikov
Signed-off-by: Emil Velikov 
---
 lib/libalpm/be_local.c   |  2 +-
 lib/libalpm/be_package.c |  2 +-
 lib/libalpm/be_sync.c| 20 ++--
 lib/libalpm/package.c|  2 +-
 lib/libalpm/package.h|  4 ++--
 5 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/lib/libalpm/be_local.c b/lib/libalpm/be_local.c
index e73a97bb..9c3c8bb3 100644
--- a/lib/libalpm/be_local.c
+++ b/lib/libalpm/be_local.c
@@ -326,7 +326,7 @@ static int _cache_force_load(alpm_pkg_t *pkg)
  * lazy accessor methods that handle any backend loading and caching
  * logic.
  */
-static struct pkg_operations local_pkg_ops = {
+static const struct pkg_operations local_pkg_ops = {
.get_base = _cache_get_base,
.get_desc = _cache_get_desc,
.get_url = _cache_get_url,
diff --git a/lib/libalpm/be_package.c b/lib/libalpm/be_package.c
index 4dde7167..f855003a 100644
--- a/lib/libalpm/be_package.c
+++ b/lib/libalpm/be_package.c
@@ -138,7 +138,7 @@ static int _package_changelog_close(const alpm_pkg_t UNUSED 
*pkg, void *fp)
  * majority of the default_pkg_ops struct and add only a few operations of
  * our own on top.
  */
-static struct pkg_operations *get_file_pkg_ops(void)
+static const struct pkg_operations *get_file_pkg_ops(void)
 {
static struct pkg_operations file_pkg_ops;
static int file_pkg_ops_initialized = 0;
diff --git a/lib/libalpm/be_sync.c b/lib/libalpm/be_sync.c
index 225df76d..5356b544 100644
--- a/lib/libalpm/be_sync.c
+++ b/lib/libalpm/be_sync.c
@@ -281,6 +281,23 @@ static int _sync_get_validation(alpm_pkg_t *pkg)
return pkg->validation;
 }
 
+/** Package sync operations struct accessor. We implement this as a method
+ * rather than a static struct as in be_files because we want to reuse the
+ * majority of the default_pkg_ops struct and add only a few operations of
+ * our own on top.
+ */
+static const struct pkg_operations *get_sync_pkg_ops(void)
+{
+   static struct pkg_operations sync_pkg_ops;
+   static int sync_pkg_ops_initalized = 0;
+   if(!sync_pkg_ops_initalized) {
+   sync_pkg_ops = default_pkg_ops;
+   sync_pkg_ops.get_validation = _sync_get_validation;
+   sync_pkg_ops_initalized = 1;
+   }
+   return _pkg_ops;
+}
+
 static alpm_pkg_t *load_pkg_for_entry(alpm_db_t *db, const char *entryname,
const char **entry_filename, alpm_pkg_t *likely_pkg)
 {
@@ -321,8 +338,7 @@ static alpm_pkg_t *load_pkg_for_entry(alpm_db_t *db, const 
char *entryname,
 
pkg->origin = ALPM_PKG_FROM_SYNCDB;
pkg->origin_data.db = db;
-   pkg->ops = _pkg_ops;
-   pkg->ops->get_validation = _sync_get_validation;
+   pkg->ops = get_sync_pkg_ops();
pkg->handle = db->handle;
 
/* add to the collection */
diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c
index a4356518..5766c600 100644
--- a/lib/libalpm/package.c
+++ b/lib/libalpm/package.c
@@ -138,7 +138,7 @@ static int _pkg_force_load(alpm_pkg_t UNUSED *pkg) { return 
0; }
 /** The standard package operations struct. Get fields directly from the
  * struct itself with no abstraction layer or any type of lazy loading.
  */
-struct pkg_operations default_pkg_ops = {
+const struct pkg_operations default_pkg_ops = {
.get_base= _pkg_get_base,
.get_desc= _pkg_get_desc,
.get_url = _pkg_get_url,
diff --git a/lib/libalpm/package.h b/lib/libalpm/package.h
index c37bd11e..b134ad5a 100644
--- a/lib/libalpm/package.h
+++ b/lib/libalpm/package.h
@@ -83,7 +83,7 @@ struct pkg_operations {
  * The actual definition is in package.c so it can have access to the
  * default accessor functions which are defined there.
  */
-extern struct pkg_operations default_pkg_ops;
+extern const struct pkg_operations default_pkg_ops;
 
 struct __alpm_pkg_t {
unsigned long name_hash;
@@ -121,7 +121,7 @@ struct __alpm_pkg_t {
alpm_list_t *removes; /* in transaction targets only */
alpm_pkg_t *oldpkg; /* in transaction targets only */
 
-   struct pkg_operations *ops;
+   const struct pkg_operations *ops;
 
alpm_filelist_t files;
 
-- 
2.29.2