aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.gitlab-ci.yml86
-rw-r--r--Make.rules17
-rw-r--r--Makefile9
-rw-r--r--README.md23
-rw-r--r--VERSION1
-rw-r--r--doc/Makefile3
-rw-r--r--doc/apk-add.8.scd37
-rw-r--r--doc/apk-audit.8.scd39
-rw-r--r--doc/apk-cache.5.scd4
-rw-r--r--doc/apk-cache.8.scd34
-rw-r--r--doc/apk-del.8.scd9
-rw-r--r--doc/apk-fetch.8.scd12
-rw-r--r--doc/apk-index.8.scd12
-rw-r--r--doc/apk-info.8.scd2
-rw-r--r--doc/apk-list.8.scd23
-rw-r--r--doc/apk-package.5.scd279
-rw-r--r--doc/apk-policy.8.scd2
-rw-r--r--doc/apk-repositories.5.scd5
-rw-r--r--doc/apk-search.8.scd5
-rw-r--r--doc/apk-v2.5.scd85
-rw-r--r--doc/apk-v3.5.scd118
-rw-r--r--doc/apk-version.8.scd4
-rw-r--r--doc/apk-world.5.scd58
-rw-r--r--doc/apk.8.scd239
-rw-r--r--doc/meson.build16
-rwxr-xr-xget-version.sh23
-rw-r--r--libfetch/Makefile7
-rw-r--r--libfetch/common.c109
-rw-r--r--libfetch/common.h12
-rw-r--r--libfetch/fetch.c92
-rw-r--r--libfetch/fetch.h6
-rw-r--r--libfetch/ftp.c20
-rw-r--r--libfetch/http.c74
-rw-r--r--libfetch/meson.build6
-rw-r--r--meson.build27
-rw-r--r--meson_options.txt6
-rw-r--r--portability/endian.h61
-rw-r--r--portability/memrchr.c9
-rw-r--r--portability/meson.build50
-rw-r--r--portability/mknodat.c30
-rw-r--r--portability/pipe2.c22
-rw-r--r--portability/qsort_r.c26
-rw-r--r--portability/reallocarray.c11
-rw-r--r--portability/socket.c12
-rw-r--r--portability/stdlib.h37
-rw-r--r--portability/strchrnul.c6
-rw-r--r--portability/string.h13
-rw-r--r--portability/strlcpy.c13
-rw-r--r--portability/sys/socket.h9
-rw-r--r--portability/sys/stat.h5
-rw-r--r--portability/sys/sysmacros.h9
-rw-r--r--portability/unistd.h10
-rw-r--r--src/Makefile34
-rw-r--r--src/adb.c396
-rw-r--r--src/adb.h103
-rw-r--r--src/adb_comp.c126
-rw-r--r--src/adb_walk_adb.c39
-rw-r--r--src/adb_walk_text.c2
-rw-r--r--src/apk.c97
-rw-r--r--src/apk_adb.c277
-rw-r--r--src/apk_adb.h15
-rw-r--r--src/apk_applet.h9
-rw-r--r--src/apk_archive.h35
-rw-r--r--src/apk_blob.h36
-rw-r--r--src/apk_context.h25
-rw-r--r--src/apk_crypto.h121
-rw-r--r--src/apk_crypto_openssl.h25
-rw-r--r--src/apk_ctype.h25
-rw-r--r--src/apk_database.h121
-rw-r--r--src/apk_defines.h75
-rw-r--r--src/apk_extract.h62
-rw-r--r--src/apk_fs.h81
-rw-r--r--src/apk_hash.h6
-rw-r--r--src/apk_io.h32
-rw-r--r--src/apk_nproc.h16
-rw-r--r--src/apk_openssl.h34
-rw-r--r--src/apk_package.h121
-rw-r--r--src/apk_pathbuilder.h2
-rw-r--r--src/apk_print.h20
-rw-r--r--src/apk_tar.h22
-rw-r--r--src/apk_trust.h7
-rw-r--r--src/apk_version.h10
-rw-r--r--src/apk_xattr.h30
-rw-r--r--src/app_adbsign.c10
-rw-r--r--src/app_add.c71
-rw-r--r--src/app_audit.c282
-rw-r--r--src/app_cache.c109
-rw-r--r--src/app_convdb.c4
-rw-r--r--src/app_convndx.c44
-rw-r--r--src/app_del.c50
-rw-r--r--src/app_dot.c62
-rw-r--r--src/app_extract.c310
-rw-r--r--src/app_fetch.c124
-rw-r--r--src/app_fix.c48
-rw-r--r--src/app_index.c163
-rw-r--r--src/app_info.c49
-rw-r--r--src/app_list.c101
-rw-r--r--src/app_manifest.c97
-rw-r--r--src/app_mkndx.c113
-rw-r--r--src/app_mkpkg.c249
-rw-r--r--src/app_policy.c21
-rw-r--r--src/app_search.c76
-rw-r--r--src/app_update.c14
-rw-r--r--src/app_upgrade.c22
-rw-r--r--src/app_verify.c31
-rw-r--r--src/app_version.c106
-rw-r--r--src/app_vertest.c74
-rw-r--r--src/applet.c32
-rw-r--r--src/blob.c197
-rw-r--r--src/commit.c289
-rw-r--r--src/common.c16
-rw-r--r--src/context.c55
-rw-r--r--src/crypto.c80
-rw-r--r--src/crypto_openssl.c187
-rw-r--r--src/ctype.c134
-rw-r--r--src/database.c2217
-rw-r--r--src/extract_v2.c387
-rw-r--r--src/extract_v3.c295
-rw-r--r--src/fs_fsys.c332
-rw-r--r--src/fs_uvol.c170
-rw-r--r--src/genhelp.lua37
-rw-r--r--src/hash.c30
-rw-r--r--src/io.c220
-rw-r--r--src/io_gunzip.c96
-rw-r--r--src/io_url_libfetch.c (renamed from src/io_url.c)63
-rw-r--r--src/io_zstd.c258
-rw-r--r--src/lua-apk.c6
-rw-r--r--src/meson.build52
-rw-r--r--src/package.c894
-rw-r--r--src/pathbuilder.c11
-rw-r--r--src/print.c81
-rw-r--r--src/solver.c147
-rw-r--r--src/tar.c (renamed from src/io_archive.c)179
-rw-r--r--src/trust.c30
-rw-r--r--src/version.c346
-rw-r--r--test/Makefile4
-rw-r--r--test/basic17.installed13
-rw-r--r--test/basic17.repo26
-rw-r--r--test/basic17.test9
-rw-r--r--test/basic18.test9
-rw-r--r--test/basic8.test1
-rw-r--r--test/conflict.installed14
-rw-r--r--test/conflict2.repo20
-rw-r--r--test/conflict3.test9
-rw-r--r--test/fuzzy.repo11
-rw-r--r--test/fuzzy1.test6
-rw-r--r--test/fuzzy2.test7
-rw-r--r--test/fuzzy3.test6
-rw-r--r--test/installif6.repo34
-rw-r--r--test/installif6.test10
-rw-r--r--test/provides.repo35
-rw-r--r--test/provides10.test2
-rw-r--r--test/provides11.test2
-rw-r--r--test/provides14.test10
-rw-r--r--test/provides15.test7
-rw-r--r--test/provides16.test10
-rw-r--r--test/provides17.test7
-rw-r--r--test/provides9.test2
-rwxr-xr-xtest/solver.sh4
-rw-r--r--test/version.data56
-rwxr-xr-xtest/version.sh17
-rw-r--r--tests/meson.build2
-rwxr-xr-x[-rw-r--r--]tests/test-basic.sh2
-rw-r--r--tests/version.data1
165 files changed, 9013 insertions, 4226 deletions
diff --git a/.gitignore b/.gitignore
index 9c4de7e..e3141c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+build/
src/apk
src/apk-test
src/apk.static
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 3b1d7f4..7fc8656 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,23 +1,29 @@
stages:
- test
+ - build
+ - upload
+ - release
-test:alpine:
+variables:
+ PACKAGE_ENDPOINT: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/"
+
+test-legacy:alpine:
image: alpine
stage: test
script:
- apk update
- - apk add make gcc git musl-dev openssl-dev linux-headers zlib-dev lua5.3-dev lua5.3-lzlib
+ - apk add make gcc git musl-dev openssl-dev linux-headers zlib-dev lua5.3-dev lua5.3-lzlib zstd-dev
- make -j$(nproc) check
tags:
- docker-alpine
- x86_64
-test-meson:alpine:
+test:alpine:
image: alpine
stage: test
script:
- apk update
- - apk add make gcc git musl-dev openssl-dev linux-headers zlib-dev lua5.3-dev lua5.3-lzlib meson zlib-static openssl-libs-static
+ - apk add make gcc git musl-dev openssl-dev linux-headers zlib-dev zstd-dev lua5.3-dev lua5.3-lzlib meson zlib-static zstd-static openssl-libs-static
- meson build
- ninja -C build
tags:
@@ -29,10 +35,78 @@ test:debian:
stage: test
script:
- apt-get update
- - apt-get install -y make gcc git libssl-dev zlib1g-dev lua5.3-dev lua5.2 lua-zlib-dev sudo
+ - apt-get install -y make gcc git libssl-dev zlib1g-dev libzstd-dev lua5.3-dev lua5.2 lua-zlib-dev sudo meson
- unlink /bin/sh
- ln -s /bin/bash /bin/sh
- - make -j$(nproc) check
+ - meson build
+ - ninja -C build
tags:
- docker-alpine
- x86_64
+
+build-static:
+ stage: build
+ image: alpinelinux/build-base:latest-$ARCH
+ script:
+ - abuild-apk add -u make gcc git musl-dev openssl-dev linux-headers zlib-dev zstd-dev lua5.3-dev lua5.3-lzlib zlib-static zstd-static openssl-libs-static
+ - make -j$(nproc) static
+ - install -s -m0755 src/apk.static src/apk.static-$ARCH
+ parallel:
+ matrix:
+ - ARCH:
+ - x86_64
+ - x86
+ - armv7
+ - armhf
+ - aarch64
+ - s390x
+ - ppc64le
+ - riscv64
+ artifacts:
+ paths:
+ - src/apk.static-*
+ tags:
+ - docker-alpine
+ - $ARCH
+
+make-release:
+ stage: release
+ image: registry.gitlab.com/gitlab-org/release-cli:latest
+ script:
+ - |-
+ apk add curl
+ for artifact in src/apk.static-*; do
+ ! [ -f "$artifact" ] && { echo "No artifacts found"; exit 1; }
+ ARCH=${artifact#*-}
+ echo "Uploading binary for $ARCH"
+ curl --fail -H "JOB-TOKEN: $CI_JOB_TOKEN" -T "$artifact" -o /dev/null ${PACKAGE_ENDPOINT}/$CI_COMMIT_TAG/$ARCH/apk.static
+ done
+ - echo "Making a release"
+ release:
+ name: $CI_COMMIT_TAG
+ description: "Release $CI_COMMIT_TAG"
+ tag_name: $CI_COMMIT_TAG
+ ref: $CI_COMMIT_TAG
+ assets:
+ links:
+ - name: apk.static (x86_64)
+ url: $PACKAGE_ENDPOINT/$CI_COMMIT_TAG/x86_64/apk.static
+ - name: apk.static (x86)
+ url: $PACKAGE_ENDPOINT/$CI_COMMIT_TAG/x86/apk.static
+ - name: apk.static (armv7)
+ url: $PACKAGE_ENDPOINT/$CI_COMMIT_TAG/armv7/apk.static
+ - name: apk.static (armhf)
+ url: $PACKAGE_ENDPOINT/$CI_COMMIT_TAG/armhf/apk.static
+ - name: apk.static (aarch64)
+ url: $PACKAGE_ENDPOINT/$CI_COMMIT_TAG/aarch64/apk.static
+ - name: apk.static (s390x)
+ url: $PACKAGE_ENDPOINT/$CI_COMMIT_TAG/s390x/apk.static
+ - name: apk.static (ppc64le)
+ url: $PACKAGE_ENDPOINT/$CI_COMMIT_TAG/ppc64le/apk.static
+ - name: apk.static (riscv64)
+ url: $PACKAGE_ENDPOINT/$CI_COMMIT_TAG/riscv64/apk.static
+ rules:
+ - if: $CI_COMMIT_TAG
+ tags:
+ - docker-alpine
+ - x86_64
diff --git a/Make.rules b/Make.rules
index 94f68f7..d87b435 100644
--- a/Make.rules
+++ b/Make.rules
@@ -53,19 +53,6 @@ export srctree objtree
TAGPREFIX ?= v
-ifneq ($(CI_COMMIT_TAG),)
-FULL_VERSION := $(CI_COMMIT_TAG)
-else ifneq ($(CI_COMMIT_REF_NAME),)
-# GitLab but no tag info, use the 'git describe' from environment variable
-# once https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/1633
-# gets completed and merged upstream.
-FULL_VERSION := $(VERSION)
-else ifneq ($(wildcard .git),)
-FULL_VERSION := $(patsubst $(TAGPREFIX)%,%,$(shell git describe))
-else
-FULL_VERSION := $(VERSION)
-endif
-
RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS -o -name .pc -o -name .hg -o -name .git \) -prune -o
export FULL_VERSION RCS_FIND_IGNORE
@@ -84,7 +71,7 @@ INSTALL := install
INSTALLDIR := $(INSTALL) -d
CFLAGS ?= -g -O2
-CFLAGS_ALL := -Werror -Wall -Wstrict-prototypes -D_GNU_SOURCE -std=gnu99 -fPIC
+CFLAGS_ALL := -Wall -Wstrict-prototypes -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -std=gnu11 -fPIC
CFLAGS_ALL += $(CFLAGS)
LDFLAGS ?= -g
@@ -127,7 +114,7 @@ endif
PHONY += all compile install clean docs FORCE
-# Convinient variables
+# Convenient variables
comma := ,
squote := '
empty :=
diff --git a/Makefile b/Makefile
index 4156ac6..3102cac 100644
--- a/Makefile
+++ b/Makefile
@@ -4,9 +4,9 @@
-include config.mk
PACKAGE := apk-tools
-VERSION := 2.12.0
+VERSION := $(shell ./get-version.sh "$(FULL_VERSION)" "$(VERSION)")
-export VERSION
+export PACKAGE VERSION
##
# Default directories
@@ -47,7 +47,8 @@ static:
$(Q)$(MAKE) STATIC=y
tag: check
- git commit . -m "apk-tools-$(VERSION)"
- git tag -s v$(VERSION) -m "apk-tools-$(VERSION)"
+ TAG_VERSION=$$(cat VERSION); \
+ git commit . -m "apk-tools-$${TAG_VERSION}"; \
+ git tag -s v$${TAG_VERSION} -m "apk-tools-$${TAG_VERSION}"
src/: libfetch/
diff --git a/README.md b/README.md
index be82d4e..fdbaae4 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,24 @@
-# Alpine Package Keeper
+# apk-tools
-Alpine Package Keeper (apk) is a package manager developed for Alpine Linux.
+Alpine Package Keeper (apk) is a package manager originally built for Alpine Linux,
+but now used by several other distributions as well.
+
+## Building
+
+The preferred build system for building apk-tools is Meson:
+
+```
+# meson setup build --prefix=/
+# meson compile -C build
+# meson install -C build
+```
+
+While there is a legacy Makefile-based system available, it only works for musl-linux
+targets, and will be dropped in the apk-tools 3.0 release.
+
+## Documentation
Online documentation is available in the [doc/](doc/) directory in the form of man pages.
+
+The [apk(8)](doc/apk.8.scd) man page provides a basic overview of the package management
+system.
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..e44349a
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+3.0.0_pre2
diff --git a/doc/Makefile b/doc/Makefile
index 21d5e03..4a6fa7a 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -1,7 +1,10 @@
scdocs-y += \
apk-cache.5 \
apk-keys.5 \
+ apk-package.5 \
apk-repositories.5 \
+ apk-v2.5 \
+ apk-v3.5 \
apk-world.5 \
apk.8 \
apk-add.8 \
diff --git a/doc/apk-add.8.scd b/doc/apk-add.8.scd
index 1586f7e..08a52fb 100644
--- a/doc/apk-add.8.scd
+++ b/doc/apk-add.8.scd
@@ -2,20 +2,21 @@ apk-add(8)
# NAME
-apk add - add packages to _world_ and commit changes
+apk add - add or update constraints to _world_ and commit changes
# SYNOPSIS
-*apk add* [<_options_>...] _packages_...
+*apk add* [<_options_>...] _constraints_...
# DESCRIPTION
-*apk add* adds the requested packages to _world_ and installs (or upgrades)
-them if not already present, ensuring all dependencies are met.
+*apk add* adds or updates given constraints to _world_ (see *apk-world*(5))
+and commit changes to disk. This usually involves installing new packages,
+but may also cause other changes to the installed packages.
To upgrade individual packages without modifying _world_ use *apk-fix*(8).
-By default *apk* tries to select a set of packages so that all dependencies
+By default *apk* tries to select a set of packages so that all constraints
are satisfied. This means that some packages can be held back in an older
version if there is direct or implicit dependencies to the older version.
@@ -33,23 +34,33 @@ following options:
the default heuristic and will cause an error to displayed if all
dependencies cannot be satisfied.
+*--no-chown*
+ Deperecated alias for --usermode.
+
*-u, --upgrade*
- Upgrade _packages_ and it's dependencies. Normally *apk add* will
+ Upgrade _packages_ and their dependencies. Normally *apk add* will
avoid changing installed package unless it is required by the newly
added _packages_ or their dependencies. This changes the default
preference to upgrading the package to the latest installable version.
+*--usermode*
+ Create usermode database with --initdb. In usermode, apk will operate
+ in such a way that root access is not required. Currently this implies:
+ - checking that we are running as non-root
+ - not changing file owner or group
+ - not setting system xattrs
+
+ This implies that the installation might not be fully functional.
+ However, this is useful mode for testing purposes or to create
+ chroot where some specific applications can run as non-root.
+
*-t, --virtual* _NAME_
Create virtual package _NAME_ with given dependencies. This new package
will get the _packages_ as dependencies instead of _world_. Finally the
- _NAME_ is added to _world_.
+ _NAME_ is added to _world_. An optional version specifier for the virtual
+ package can be given via syntax _NAME_=_VERSION_. The version defaults
+ to synthesized version based on time.
One can use this to ensure that selected set of packages are installed,
and later the temporary modification be undone with *apk-del*(8) _NAME_
without removing packages that were installed earlier.
-
-*--no-chown*
- Do not change file owner or group. By default apk will manage the file
- ownership when running as root. However, this option is turned on when
- running as non-root user, as changing file ownership is not permitted
- by the system then.
diff --git a/doc/apk-audit.8.scd b/doc/apk-audit.8.scd
index 6c84b87..52e8733 100644
--- a/doc/apk-audit.8.scd
+++ b/doc/apk-audit.8.scd
@@ -13,21 +13,27 @@ apk audit - audit directories for changes
*apk audit* audits the system or specified directories for changes compared to
the package database.
-The audit can be done against configuration files only (--backup) to generate
+The audit can be done against configuration files only (*--backup*) to generate
list of files needed to be stored in the overlay in run-from-tmps configuration.
-Alternatively, it can audit all installed files (--system) to e.g. detect
-unauthorized modifications of system files.
+Alternatively, it can audit all installed files (*--system* or *--full*) to
+e.g. detect unauthorized modifications of system files.
By default, the output format is one file per line, for each modified file.
-A character is printed indicating the change detected, followed by a space,
-then the affected path. The changes detected are:
-
-|[ A
-:< File added
+A character is printed indicating the line type, followed by a space,
+then the affected path or details. The changes detected are:
+
+|[ -
+:< Database detail record
+| \+
+: On-disk detail record
+| A
+: File added
| d
: Directory added
| D
: Directory added (with non-listed files/subdirs)
+| e
+: error occured during audit (e.g. no permissions to read file)
| M
: File metadata changed (uid, gid, or mode)
| m
@@ -49,6 +55,18 @@ then the affected path. The changes detected are:
Check file permissions too. Namely, the uid, gid and file mode will
be checked in addition to the file content.
+*--details*
+ Enable reporting of detail records.
+
+*--full*
+ Audit all system files. Same as *--system*, but in addition reports
+ all added directories and files. A built-in default override for
+ protected paths is used, unless a *--protected-paths* is explicitly
+ specified.
+
+*--ignore-busybox-symlinks*
+ Ignore symlinks whose target is the busybox binary.
+
*--packages*
Print only the packages with changed files. Instead of the full output
each modification, the set of packages with at least one modified file
@@ -57,10 +75,15 @@ then the affected path. The changes detected are:
To repair all packages with modified files, one could use:
apk audit --packages -q | xargs apk fix
+*--protected-paths* _FILE_
+ Use given FILE for protected paths listings. This also makes apk ignore
+ the regular protected_paths.d directories.
+
*--system*
Audit all system files. All files provided by packages are verified
for integrity with the exception of configuration files (listed in
protected_paths.d). This is useful detecting unauthorized file changes.
+ New files or directories are not reported.
*-r, --recursive*
Descend into directories and audit them as well.
diff --git a/doc/apk-cache.5.scd b/doc/apk-cache.5.scd
index 6776697..d0d943a 100644
--- a/doc/apk-cache.5.scd
+++ b/doc/apk-cache.5.scd
@@ -6,8 +6,8 @@ apk-cache(5)
# DESCRIPTION
-If */etc/apk/cache* is a symlink to a local directory, *apk*(8) will it as a
-local cache for downloaded package files and repository indicies. The cache
+If */etc/apk/cache* is a symlink to a local directory, *apk*(8) will use it as
+a local cache for downloaded package files and repository indicies. The cache
must not reside on a tmpfs.
To enable the apk cache, run the following commands as root:
diff --git a/doc/apk-cache.8.scd b/doc/apk-cache.8.scd
index 1381d81..0a04123 100644
--- a/doc/apk-cache.8.scd
+++ b/doc/apk-cache.8.scd
@@ -8,9 +8,11 @@ apk cache - manage a local cache of package files
*apk cache* [<_options_>...] clean
-*apk cache* [<_options_>...] download
+*apk cache* [<_options_>...] download [_dependency_...]
-*apk cache* [<_options_>...] sync
+*apk cache* [<_options_>...] purge
+
+*apk cache* [<_options_>...] sync [_dependency_...]
# DESCRIPTION
@@ -18,16 +20,33 @@ Manage local package cache by removing obsolete packages, or downloading
missing or newer packages from the repositories.
*apk cache download* will fetch package files from the repositories and store
-them in the cache, which must be enabled upfront (see *apk-cache*(5)).
+them in the cache, which must be enabled upfront (see *apk-cache*(5)). By
+default _world_ dependencies are used to determine what to download. If
+_dependency_ arguments are given, they will by default replace the _world_.
+
+*apk cache clean* will remove package files which no longer exist in any
+repository index. Specifying the global option *--purge* will additionally
+remove all uninstalled package on tmpfs installations, and all packages on
+disk installations.
-*apk cache clean* will remove package files which are no longer necessary,
-having been made obsolete by new versions or no longer being transitively
-depended on by packages in _world_.
+*apk cache purge* is a synonym for *clean --purge*.
*apk cache sync* performs both operations.
# OPTIONS
+*--add-dependencies*
+ Add the argument dependencies to _world_ dependencies when determining
+ which packages to download.
+
+*-a, --available*
+ Selected packages to be downloaded from active repositories even if
+ it means replacing or downgrading the installed package.
+
+*--ignore-conflict*
+ Ignore conflicts when resolving dependencies. This can be useful when
+ pre-populating cache for creation of multiple images in one go.
+
*-l, --latest*
Always choose the latest package by version. However, the versions
considered are based on the package pinning. Primarily this overrides
@@ -39,3 +58,6 @@ depended on by packages in _world_.
avoid changing installed package unless it is required by the newly
added _packages_ or their dependencies. This changes the default
preference to upgrading the package to the latest installable version.
+
+*-s, --simulate*
+ Simulate the requested operation without making any changes.
diff --git a/doc/apk-del.8.scd b/doc/apk-del.8.scd
index 143b2f1..ebb12c0 100644
--- a/doc/apk-del.8.scd
+++ b/doc/apk-del.8.scd
@@ -2,16 +2,17 @@ apk-del(8)
# NAME
-apk del - remove packages from _world_ and commit changes
+apk del - remove constraints from _world_ and commit changes
# SYNOPSIS
-*apk del* [<_options_>...] _packages_...
+*apk del* [<_options_>...] _constraints_...
# DESCRIPTION
-*apk del* removes packages from _world_ and uninstalls them if no longer
-required by any other packages.
+*apk del* removes constraints from _world_ (see *apk-world*(5)) and
+commits changes to disk. This usually involves removing unneeded packages,
+but may also cause other changes to the installed packages.
# OPTIONS
diff --git a/doc/apk-fetch.8.scd b/doc/apk-fetch.8.scd
index 2b17f1a..495c3d4 100644
--- a/doc/apk-fetch.8.scd
+++ b/doc/apk-fetch.8.scd
@@ -17,7 +17,11 @@ specified.
# OPTIONS
-*-L, --link*
+*--built-after* _TIMESPEC_
+ Only fetch packages that have buildtime more recent than TIMESPEC.
+ TIMESPEC can be a "YYYY-MM-DD HH:MM:SS" date, or seconds since epoch.
+
+*-l, --link*
Create hard links if possible.
*-o, --output* _DIR_
@@ -32,8 +36,14 @@ specified.
*Note*: this option is incompatible with *-o*, *-R*, and the global
*--progress* option.
+*-w, --world*
+ Download packages needed to satisfy _world_. Implies *--recursive*.
+
*--simulate*
Simulate the requested operation without making any changes.
*Note*: this option is unreliable if needed indexes are not up-to-date
as this omits refresing or downloading of missing indexes.
+
+*--url*
+ Print the full URL for downloaded packages.
diff --git a/doc/apk-index.8.scd b/doc/apk-index.8.scd
index 82a6a48..d81a2f7 100644
--- a/doc/apk-index.8.scd
+++ b/doc/apk-index.8.scd
@@ -23,9 +23,16 @@ will accept it. See *abuild-sign*(1) for details.
information based on the git commit SHA of aports HEAD at the time of
index generation.
+*--merge*
+ Merge _packages_ into the existing _INDEX_.
+
*-o, --output* _FILE_
Output generated index to _FILE_.
+*--prune-origin*
+ Prune packages from the existing _INDEX_ with same origin as any of
+ the new _packages_ during merge.
+
*-x, --index* _INDEX_
Read an existing index from _INDEX_ to speed up the creation of the new
index by reusing data when possible.
@@ -37,3 +44,8 @@ will accept it. See *abuild-sign*(1) for details.
*--rewrite-arch* _ARCH_
Set all package's architecture to _ARCH_.
+
+# ENVIRONMENT
+
+*SOURCE_DATE_EPOCH*
+ Used as the build time for tar file headers in APKINDEX.tar.gz.
diff --git a/doc/apk-info.8.scd b/doc/apk-info.8.scd
index ffef26d..bbd703b 100644
--- a/doc/apk-info.8.scd
+++ b/doc/apk-info.8.scd
@@ -71,5 +71,5 @@ display the appropriate information, then an empty line terminates that field.
*--rinstall-if*
List other packages whose install_if rules refer to this package.
-*--triggers*
+*-t, --triggers*
Print active triggers for the package.
diff --git a/doc/apk-list.8.scd b/doc/apk-list.8.scd
index e011825..7f4d724 100644
--- a/doc/apk-list.8.scd
+++ b/doc/apk-list.8.scd
@@ -18,23 +18,26 @@ globbing.
# OPTIONS
-*-I, --installed*
- Consider only installed packages.
-
-*-O, --orphaned*
- Consider only orphaned packages.
-
*-a, --available*
Consider only available packages.
-*-u, --upgradable*
- Consider only upgradable packages.
+*-d, --depends*
+ List packages by dependency.
+
+*-I, --installed*
+ Consider only installed packages.
+
+*--manifest*
+ List installed packages in format `<name> <version>`.
*-o, --origin*
List packages by origin.
-*-d, --depends*
- List packages by dependency.
+*-O, --orphaned*
+ Consider only orphaned packages.
*-P, --providers*
List packages by provider.
+
+*-u, --upgradable, --upgradeable*
+ Consider only upgradable packages.
diff --git a/doc/apk-package.5.scd b/doc/apk-package.5.scd
new file mode 100644
index 0000000..653a5cb
--- /dev/null
+++ b/doc/apk-package.5.scd
@@ -0,0 +1,279 @@
+apk-package(5)
+
+# NAME
+
+apk package - apk package metadata fields
+
+# DESCRIPTION
+
+The apk package metadata contains the package info metadata substructure
+and various other metadata fields.
+
+The package info metadata structure is the portion of package metadata which
+will be copied to the repository index when the package is being indexed.
+These fields will be available form the index even if the package is not
+installed.
+
+The rest of the package metadata is kept in the package and installed
+database. These fields are available only if the package is installed.
+
+The remainder of the document explains each field with the notation:
+*v3-field-name* (*v2-pkginfo-field-name*, *v2-index-character*).
+
+It is mentioned explicitly if APK uses each fields for something meaningful.
+Some fields are not used internally by APK and from the APK point of view
+are just blobs of data associated with specified name which are meaningful
+the user.
+
+# PACKAGE NAMES AND VERSIONS
+
+APK will often display concatenation of *name*-*version* in its verbose
+output mode. The rule below on how a valid version number is defined allow
+that this format can be uniquely splitted back to the two components by
+finding the *last* occurance of *-[0-9]*. The dash in the beginning of this
+match is the splitting point: first portion is the *name* and second
+portion is the *version*.
+
+Unfortunately it is not possible to deduce if a given string is of format
+*name* or *name-version* (*name* alone can also contain *-[:digit:]* in it).
+
+# PACKAGE INFO METADATA
+
+*name* (*pkgname*, *P*)
+ Package name. This is the primary package name. The name shall
+ consist only of the following characters [a-zA-Z0-9.\_+-].
+ The name must start with an alphanumeric character [a-zA-Z0-9].
+
+*version* (*pkgver*, *V*)
+ Package version. The Alpine version specification originally
+ followed the Gentoo package version specification.
+
+ Currently the APK version specification is as follows:
+ *number{.number}...{letter}{\_suffix{number}}...{~hash}{-r#}*
+
+ Each *number* component is a sequence of digits (0-9).
+
+ The *letter* portion can follow only after end of all the numeric
+ version components. The *letter* is a single lower case letter (a-z).
+ This can follow one or more *\_suffix{number}* components. The list
+ of valid suffixes (and their sorting order) is:
+ *alpha*, *beta*, *pre*, *rc*, <no suffix>, *cvs*, *svn*, *git*, *hg*, *p*
+
+ This can be follows with an optional *{~hash}* to indicate a commit
+ hash from where it was built. This can be any length string of
+ lower case hexdecimal digits (0-9a-f).
+
+ Finally an optional package build component *-r{number}* can follow.
+
+*unique-id* (*C*)
+ Unique identifier for the package. This changes for each unique build
+ of the package. Apk *mkpkg* will calculate this field deterministically
+ from the package contents and other metadata at package build time.
+ In APKv2 packages this field is not present, but is calculated
+ directly from specific portions of the package data. APKv2 used to also
+ call this the package identity hash.
+
+ APK uses this fields in multiple ways:
+ - determine if same identical package is available from multiple
+ repositories
+ - make package filename unique when storing a copy in the package
+ cache
+
+*description* (*pkgdesc*, *T*)
+ The description is a single line describing the package.
+ APK displays this string in various command querying information about
+ the package, repository or installed database.
+
+*arch* (*arch*, *A*)
+ Package architecture for which the package was built. Currently apk
+ uses the following default architectures:
+ - noarch
+ - aarch64
+ - armel
+ - armhf
+ - armv7
+ - mips
+ - mipsel
+ - mips64
+ - mips64el
+ - ppc
+ - ppc64
+ - ppc64le
+ - riscv32
+ - riscv64
+ - s390x
+ - loongarchx32
+ - loongarch64
+ - x86
+ - x86_64
+
+ APK currently uses the architecture to construct the package download
+ URL from a repository base path.
+
+ The APK does not currently validate package architecture against the
+ running system or the database's architecture. However, this will be
+ soon changed that APK will consider only compatible packages for
+ installation.
+
+*license* (*license*, *L*)
+ Package license. This is informative field for the user and APK does
+ not validate or use this field internally. It is recommended to use
+ standard license descriptors such as SPDX.
+
+*origin* (*origin*, *o*)
+ Package's source package name. APK uses this field as follows:
+ - If two separate binary packages share same source package, APK allows
+ overwriting the package to overwrite files from another package. This
+ serves the purpose of moving files from one subpackage to another.
+ - Several query commands allow printing or matching the original package name.
+ - Indexing command (when updating index incrementally) uses this field
+ determine when to delete old package (that is to delete subpackages
+ that no longer exist).
+
+*maintainer* (*maintainer*, *m*)
+ Package's maintainer information. Usually the name and email address.
+
+*url* (*url*, *U*)
+ Package URL. A link to website containing information about the package.
+
+*repo-commit* (*commit*, *c*)
+ Repository commit hash from which the package was built from.
+
+*build-time* (*builddate*, *t*)
+ UNIX timestamp when the package was built. Apk fetch can filter packages
+ to download based on the build time. This is useful to download incremental
+ repository snapshots.
+
+*installed-size* (*size*, *I*)
+ Estimate of how much disk space is required when the package is installed.
+ APK displays this information in various places, and based the commit
+ transaction disk usage changed on this information.
+
+ Packages with the installed size being zero as meta packages that do not
+ have any other data than indexed data. APK may choose to not download the
+ package and handle everything based on the data available in the index.
+
+*file-size* (*S*)
+ This field is present meaningful only in the repository index copy of
+ the package info. APK index will fill this field at indexing time with the
+ size of the package file (.apk). Technically this field should be a repository
+ index specific field, and such change might be done in the future.
+
+*provider-priority* (*provider_priority*, *k*)
+ This determines the default installation priority for the non-versioned
+ package names the packages lists in the *provides* field. By default
+ a non-versioned provides will not be selected automatically for installation.
+ But specifying *provider-priority* enables this automatic selection, and is
+ used to determine which of the packages to install in case multiple packages
+ provide the same non-versioned package name.
+
+*depends* (*depend*, *D*)
+ List of dependencies for the package. Installing this package will
+ require APK to first satisfy the list of all its dependencies.
+
+ The dependencies are used by various APK components:
+ - The solver will try to find a solution that all package dependencies
+ are satisfied (as well as the world dependencies)
+ - When apk is committing changes to the file system, it will install
+ or remove packages in such order that all dependencies of the package
+ will be satisfied (assuming there are no circular dependencies)
+ - When apk runs the package trigger scripts, they will be ordered
+ so that the triggers of all dependencies before running the trigger
+ for this package
+
+*provides* (*provides*, *p*)
+ List of package names (and optionally its version) this package
+ provides in addition to its primary name and version. The provided
+ name can contain additionally characters: comma (,), brackets ([]),
+ colons (:) and slashes (/) in the name. This allows using namespaces
+ for automatically generated names.
+
+ If the provided name contains a version number:
+ - the solver will treat it as-if a real package with the provided
+ name is installed
+ - the package becomes automatically selectable by anything depending
+ on the provided name
+ - the package will automatically become the single possible owner
+ for the provided name
+ - the package will automatically conflict with any package with
+ the same primary or provided package name
+
+ If the provided name does not include version:
+ - the package is not automatically selectable for installation
+ by that fact that there is a dependency on the provided name
+ - specifying *provides_priority* will allow automatic selection
+ - otherwise user is expected to manually select one of the
+ concrete package names in world which allows selection
+ - the package is not considered to own provided name
+ - multiple packages provided the same name without a version are
+ allowed to be installed simultaneously
+ - apk internally considers a package name with only non-versioned
+ providers as a "virtual package name"
+
+*replaces* (*r*)
+ List of package names this package is allowed to replace files from.
+ Normally apk treats it as an error if multiple packages contain the
+ same file. Specifying a replaces declartion allows the package to
+ silently overwrite files from the listed packages.
+
+*install-if* (*install_if*, *i*)
+ APK will automatically select and install the package if all of
+ the install-if dependencies are satisfied. There should be at least
+ two dependencies in *install_if* dependencies, and one of them must
+ have a equality (*=*) operator.
+
+ Typical use case is that there is a global repository meta package
+ e.g. *docs*. And then there are multiple packages that have a subpackage
+ like *package-doc*. These *-doc* packages can then have a *install-if*
+ rule to get automatically installed if such as "*package=$name-$ver docs*"
+ to install the documentation package automatically if the main package
+ and the documentation meta package is installed.
+
+*layer*
+ An integer specifying the database layer this package installs to:
+ - *root* (0) is the default and indicates the normal file system
+ - *uvol* (1) indicates that the package contains an uvol image and
+ the uvol volume manager should be used to install the images
+
+ In addition to controlling where the package content goes, this also
+ affects the installad database where the metadata of these packages
+ go. Each layer has a separate installed database.
+
+# PACKAGE METADATA
+
+*info*
+ This is the logical structure containing the package info metadata
+ as defined in the previous section.
+
+*paths*
+ This contains listing of all the paths and files along with the file
+ specific metadata (owner, permissions, xattrs, content hashes).
+
+*scripts*
+ Scripts contains the executable files (usually shell scripts) that
+ are executed before or after package installation, removal, upgrade
+ as well as to handle trigger conditions.
+
+ Currently defined script types:
+ - trigger
+ - pre-install
+ - post-install
+ - pre-deinstall
+ - post-deinstall
+ - pre-upgrade
+ - post-upgrade
+
+*triggers*
+ List of directory globs. APK will execute the trigger script with
+ list of matched directories when any action (package installation,
+ removal) has modified content of that directory. When package is
+ being fixed or installed it will get list of all matching directories.
+
+*replaces-priority*
+ If two packages both contain the same file, and they both have replaces
+ directive allow them to overwrite packages. This priority determines
+ which packages file is takes precedence.
+
+# SEE ALSO
+
+*abuild*(1), *apk*(1), *apk-v2*(5), *apk-v3*(5)
diff --git a/doc/apk-policy.8.scd b/doc/apk-policy.8.scd
index 6896e16..fa3b858 100644
--- a/doc/apk-policy.8.scd
+++ b/doc/apk-policy.8.scd
@@ -12,7 +12,7 @@ apk policy - show repository policy for packages
*apk policy* shows apk's repository policy for the specified packages. It
prints matching packages and their versions available from configured
-repositories (see *apk-repositories*(5)), in order of installation preference.
+repositories (see *apk-repositories*(5)), sorted by ascending version.
# OPTIONS
diff --git a/doc/apk-repositories.5.scd b/doc/apk-repositories.5.scd
index a53965e..7b3d3ca 100644
--- a/doc/apk-repositories.5.scd
+++ b/doc/apk-repositories.5.scd
@@ -2,7 +2,8 @@ apk-repositories(5)
# NAME
-*/etc/apk/repositories* - list of package repositories
+*/etc/apk/repositories*, */etc/apk/repositories.d/\*.list* - list of package
+repositories
# DESCRIPTION
@@ -31,5 +32,5 @@ those signatures.
# UPDATING INDICIES
*apk*(8) fetches and stores the index for each package repository at
-/var/lib/cache. To fetch fresh indicies for all configured repositories, use
+*/var/cache/apk*. To fetch fresh indicies for all configured repositories, use
*apk-update*(8).
diff --git a/doc/apk-search.8.scd b/doc/apk-search.8.scd
index 890dac1..b2617da 100644
--- a/doc/apk-search.8.scd
+++ b/doc/apk-search.8.scd
@@ -11,8 +11,9 @@ apk search - search for packages by name or description
# DESCRIPTION
*apk search* searches all repositories for packages matching at least one
-pattern. If no pattern is given, it lists all packages in the repository. A
-pattern matches if it is a case-sensitive substring of the package name.
+pattern. If no pattern is given, it lists all packages in the configured
+repositories (see *apk-repositories*(5)). A pattern matches if it is a
+case-insensitive substring of the package name.
# OPTIONS
diff --git a/doc/apk-v2.5.scd b/doc/apk-v2.5.scd
new file mode 100644
index 0000000..b842d42
--- /dev/null
+++ b/doc/apk-v2.5.scd
@@ -0,0 +1,85 @@
+apk-v2(5)
+
+# NAME
+
+apk v2 - overview of apk v2 format
+
+# DESCRIPTION
+
+A v2 .apk file contains a single package's contents, some metadata, and
+some signatures. The .apk file contains three concatenated gzip streams,
+which together form a single tar archive. The tar archive contains three
+sections: the signatures, the control section, and the data section.
+
+# THE SIGNATURES
+
+The signatures are a sequence of files whose names start with ".SIGN.",
+which must come before any other data in the tarball. These filenames
+look like:
+
+ *.SIGN.<algorithm>.<keyid>*
+
+where <algorithm> must be one of *DSA*, *RSA*, *RSA256*, and *RSA512*
+and <keyid> must be the name of the key's file in /etc/apk/keys (see
+*apk-keys*(5)).
+
+The signature can be computed over either the metadata (if the metadata
+contains a data hash for the data), or over the metadata and data
+together (if the metadata contains no data hash).
+
+A single signature from a trusted key is sufficient, so an apk can be
+signed by multiple different keys if need be, as long as clients trust
+at least one of them.
+
+# THE CONTROL SECTION
+
+In a v2 apk file, the package metadata is stored in a single file called
+.PKGINFO. That file uses a key-value format, in which keys and values
+are separated by " = " and lines beginning with "#" are comments. There
+are many allowed keys and there is no centralized list of known keys;
+the source of *abuild*(1) is the best reference.
+
+One key is important for understanding the v2 format because it affects
+the interpretation of the signature: if there is a "datahash" key in
+PKGINFO, its value is the sha256 hash of the data part of the apk.
+Packages are supposed to have a datahash, but indexes do not.
+
+The control section is also where pre/post hook scripts for install, deinstall,
+and upgrade live, and where triggers live.
+
+# THE DATA SECTION
+
+The data section is simply a tar archive of the package's contents, as
+produced by the build process. These files are postprocessed by
+*abuild-tar*(1) and use pax extended headers to include per-file
+checksums in a header named APK-TOOLS.checksum.*<hash>*.
+
+# EXAMPLE
+
+As an example, the v2 apk for *scdoc*(1) itself contains these files in
+this order:
+
+ .SIGN.RSA.alpine-devel@lists.alpinelinux.org-6165ee59.rsa.pub
+ .PKGINFO
+ usr/
+ usr/bin/
+ usr/bin/scdoc
+ usr/share/
+ usr/share/pkgconfig/
+ usr/share/pkgconfig/scdoc.pc
+
+Since v2 apk files are simply tarballs (broken into multiple gzip
+streams), they can be inspected and unpacked with *tar*(1), although
+care must be taken when changing them not to reorder the sections or
+invalidate the signature. It is better to use *abuild*(1) to modify
+them. If you want to take them apart into their constituent gzip
+streams, you can use *abuild-gzsplit*(1).
+
+# NOTES
+
+Only the "RSA" (meaning RSA + SHA1) signature scheme is currently used
+by *abuild*(1).
+
+# SEE ALSO
+
+*abuild*(1), *apk*(1), *apk-package*(5), *apk-v3*(5)
diff --git a/doc/apk-v3.5.scd b/doc/apk-v3.5.scd
new file mode 100644
index 0000000..01b8952
--- /dev/null
+++ b/doc/apk-v3.5.scd
@@ -0,0 +1,118 @@
+apk-v3(5)
+
+# NAME
+
+apk v3 - overview of apk v3 format
+
+# DECRIPTION
+
+A v3 .apk file contains a single package's contents, some metadata, and
+some signatures. The .apk file contains a tree of objects, represented
+in a custom binary format and conforming overall to a pre-defined
+schema. This file format is referred to inside *apk*(5) as "adb".
+
+# WIRE FORMAT
+
+A v3 apk file is composed of sequences of serialized values, each of
+which begins with a 32-bit little-endian word - the value's tag. The
+high 4 bits of the tag are a type code, and the low 28 bits are used for
+an immediate value. Defined type codes are:
+
+ 0x0 Special (direct)
+ 0x1 Int (direct)
+ 0x2 Int32 (indirect)
+ 0x3 Int64 (indirect)
+ 0x8 Blob8 (indirect)
+ 0x9 Blob16 (indirect)
+ 0xa Blob32 (indirect)
+ 0xd Array (indirect)
+ 0xe Object (indirect)
+
+A direct value is packed into the low 28 bits of the tag word; an
+indirect value is instead stored elsewhere in the file, and the offset
+of that indirect value is packed into the low 28 bits of the tag word.
+
+Arrays and objects are represented with a sequence of numbered slots;
+the value packed into their tag word is the offset at which this
+sequence starts. The first slot is always the total number of slots, so
+all arrays and objects contain at least one item.
+
+The only real difference between arrays and objects in the wire encoding
+is that arrays are homogenous, whereas objects are heterogenous with a
+separate defined type for each slot.
+
+The special type is used to represent three atoms:
+
+ 0x0 NULL
+ 0x1 TRUE
+ 0x2 FALSE
+
+# FILE SCHEMAS
+
+A schema is a representation of what data elements are expected in an
+adb file. Schemas form a tree, where nodes are either scalar schemas
+(which are leaves in the tree) or array/object schemas, which themselves
+have children. For example, the schema for a package object might
+declare that it contains fields which themselves conform to the string
+array schema, or the pkginfo schema, or similar.
+
+The schemas themselves are not represented in the adb file in any way;
+they exist in the parts of *apk*(1) that read and write such files. A
+full description of all of apk's schemas would be lengthy, but as an
+example, here is the schema for a single file inside a package:
+
+ ADBI_FI_NAME "name" string
+ ADBI_FI_ACL "acl" acl
+ ADBI_FI_SIZE "size" int
+ ADBI_FI_MTIME "mtime" int
+ ADBI_FI_HASHES "hash" hexblob
+ ADBI_FI_TARGET "target" hexblob
+
+Here, all of the fields except for "acl" are scalars, and acl is itself
+a schema looking like:
+
+ ADBI_ACL_MODE "mode" oct
+ ADBI_ACL_USER "user" string
+ ADBI_ACL_GROUP "group" string
+
+# BLOCKS
+
+An actual adb file is composed of a sequence of typed blocks; a block
+also begins with a 32-bit little-endian tag word, which has two bits of
+type and 30 bits of size. The two type bits are:
+
+ 0x0 ADB
+ 0x1 SIG
+ 0x2 DATA
+ 0x3 DATAX
+
+The adb file must begin with one ADB block, then optionally one SIG
+block, then one or more DATA blocks. The ADB block must begin with a
+magic number indicating the schema for the entire ADB block's root
+object. The ADB block also contains, outside the root object, some
+metadata describing the version of the adb format in use.
+
+The SIG block contains a signature of the ADB block. Unlike the v2
+format, the key used for the signature is not explicitly specified, so
+verifiers must try all trusted keys until they find one. Also unlike the
+v2 format, the only supported hash algorithm is SHA512, and the
+signature scheme is implied by the signing key in use rather than being
+derived from the signature block.
+
+The DATA blocks are used to store package file data only; all file
+metadata, including content hashes, is stored in the ADB block instead.
+The contents of the DATA blocks are therefore protected by the hashes
+given in the ADB block, which is itself protected by the signature in
+the SIG block.
+
+It is currently illegal for a DATAX block to appear.
+
+# NOTES
+
+The v3 file format is entangled with C struct layout, since it sometimes
+directly writes structs into the adb section, including any
+compiler-added padding and such.
+
+# SEE ALSO
+
+*abuild*(1), *apk*(1), *apk-package*(5), *apk-v2*(5)
diff --git a/doc/apk-version.8.scd b/doc/apk-version.8.scd
index 81e5cd0..acd0d47 100644
--- a/doc/apk-version.8.scd
+++ b/doc/apk-version.8.scd
@@ -36,7 +36,7 @@ specified).
*-a, --all*
Consider packages from all repository tags.
-*-c, --check* _versions_...
+*-c, --check*
Check versions for validity. If a given version is invalid, it is
printed. Exits with status code zero if all versions are valid, and
non-zero otherwise.
@@ -49,7 +49,7 @@ specified).
Limit to packages with output matching given _operand_. The _operand_
can be specified as any combination of *>*, *=*, and *<*.
-*-t, --test* _version1_ _version2_
+*-t, --test*
Compare two version strings. Does not consult the database. Prints one
of *>*, *=*, or *<*, if _version1_ is, respectively, greater than,
equal to, or lesser than _version2_.
diff --git a/doc/apk-world.5.scd b/doc/apk-world.5.scd
index 9ab68dd..4a185bd 100644
--- a/doc/apk-world.5.scd
+++ b/doc/apk-world.5.scd
@@ -2,40 +2,48 @@ apk-world(5)
# NAME
-*/etc/apk/world* - list of explicitly installed packages
+*/etc/apk/world* - list of constraints for package selection
# DESCRIPTION
-At /etc/apk/world, apk maintains the _world_, or list of explicitly installed
-packages. This is a plaintext file with one package spec per line.
+At /etc/apk/world, apk maintains the _world_, that is, a list of constraints
+the package selection needs to fulfill.
If you edit this file manually, you should run *apk-fix*(8) to apply the
changes.
# PACKAGE SPECIFICATION
-Specifying a package name with no other modifiers will install the latest
-version of that package from the first repository in which it is available.
+This is a plaintext file with one constraint using dependency notation per line.
+Each line has the format: *name{@tag}{[<>~=]version}*.
-To pin a package to a tagged repository, use the format *pkgname@tagname*, e.g.
-*busybox@edge*. See *apk-repositories*(5) for details on tagged package
-repositories.
+When modifying existing installation, the installed version is preferred unless
+an upgrade is requested or a world constraint or package dependency requires
+an alternate version.
+
+To enable selection from a tagged repository, use the format *name@tag*,
+e.g. *busybox@edge*. See *apk-repositories*(5) for details on tagged package
+repositories. Untagged repositories are also considered for constraints with
+a tag. The tag is inherited to all dependencies as an allowed repository.
+That is, the dependencies are selected from the tagged repository if the
+*name@tag* has a dependency with version constraint requiring a version
+available only from the tagged repository. If the dependency can be satisfied
+from non-tagged repository it will be preferred.
To constrain the acceptable versions of the package, use the *=*, *<*, *>*,
-*>=*, or *~=* operators. Respectively, these require the package is equal to,
-less than, greater than, or greater than or equal to the specified version. The
-*~=* operator constrains the package to the version numbers specified, but will
-not constrain any unspecified version numbers.
+*>=*, *~*, *>~* or *<~* operators. Respectively, these require the package is
+equal to, less than, greater than, greater than or equal, prefix match, greater
+than or prefix match, or less than or prefix match to the specified version.
+The *~* operator constrains the package to the prefix match of the version number.
*busybox*
- Installs busybox from the first repository from which it is available.
+ Installs busybox from the untagged repository from which it is
+ available.
*busybox@edge*
- Installs busybox from the first repository tagged "edge" from which
- it's available. If busybox is _not_ available in repositories with this
- tag, it will cause an error. When resolving dependencies of tagged
- requirements, untagged repositories are preferred, but repositories
- sharing the same tag will be considered if necessary.
+ Allows installation of busybox and it's dependencies from a repository
+ tagged with "edge". Tagged repositories will not be prioritized. If a
+ version from an untagged repository is a better fit it will be used.
*busybox=1.6.1*
Install busybox version 1.6.1.
@@ -43,12 +51,18 @@ not constrain any unspecified version numbers.
*busybox>1.6.1*
Install a busybox version greater than 1.6.1.
-*busybox>1.6.1*
+*busybox>=1.6.1*
Install a busybox version greater than or equal to 1.6.1.
*busybox<1.6.1*
Install a busybox version less than 1.6.1.
-*busybox~=1.6*
- Install any busybox version between 1.6.0 (inclusive) and 1.7.0
- (exclusive).
+*busybox~1.6*
+ Install any busybox version starting with 1.6. Examples of match:
+ 1.6, 1.6.0_pre1, 1.6.0, 1.6.5, 1.6.9_p1.
+
+*busybox>~1.6*
+ Install a busybox version greater than or prefix match of 1.6.
+
+*busybox<~1.6*
+ Install a busybox version less than or prefix match of 1.6.
diff --git a/doc/apk.8.scd b/doc/apk.8.scd
index f119313..b250d11 100644
--- a/doc/apk.8.scd
+++ b/doc/apk.8.scd
@@ -10,13 +10,18 @@ apk - Alpine Package Keeper
# DESCRIPTION
-*apk* manages packages installed on the system. The set of top level packages
-to install is called the _world_ (see *apk-world*(5)). *apk* supports various
-sub-commands to query and manipulate _world_ and local & remote package
+*apk* manages packages installed on the system. The set of top level constraints
+on system packages is called the _world_ (see *apk-world*(5)).
+
+*apk* supports various sub-commands to query and manipulate _world_ and package
repositories.
All apk commands which modify the database are logged to /var/log/apk.log.
+By default apk is non-interactive. See *FILES* or *--interactive* on changing
+this default to be interactive.
+
+
# COMMANDS
Each command is documented in detail on its manual page.
@@ -24,9 +29,9 @@ Each command is documented in detail on its manual page.
## PACKAGE INSTALLATION AND REMOVAL
|[ *apk-add*(8)
-:< Add packages to _world_ and commit changes
+:< Add or modify constraints in _world_ and commit changes
| *apk-del*(8)
-: Remove packages from _world_ and commit changes
+: Remove constraints from _world_ and commit changes
## SYSTEM MAINTENANCE
@@ -57,7 +62,7 @@ Each command is documented in detail on its manual page.
|[ *apk-index*(8)
:< Create repository index file from packages
| *apk-fetch*(8)
-: Download packages from global repositories to a local directory
+: Download packages from repositories to a local directory
| *apk-manifest*(8)
: Show checksums of package contents
| *apk-verify*(8)
@@ -76,13 +81,18 @@ Each command is documented in detail on its manual page.
The following options are available for all commands.
+*-h, --help*
+ Print the list of all commands with descriptions.
+
*-f, --force*
Enable selected --force-\* options (deprecated).
*-i, --interactive*
Ask confirmation before performing certain operations.
+ Interactive mode can be made the default when running on a tty,
+ by creating /etc/apk/interactive as an empty file.
-*-p, --root* <_ROOT_>
+*-p, --root* _ROOT_
Manage file system at _ROOT_.
*-q, --quiet*
@@ -97,7 +107,7 @@ The following options are available for all commands.
*-V, --version*
Print program version and exit.
-*-X, --repository* <_REPO_>
+*-X, --repository* _REPO_
Specify additional package repository. This option can be specified
multiple times.
@@ -105,7 +115,8 @@ The following options are available for all commands.
Install packages with untrusted signature or no signature.
*--arch* _ARCH_
- Temporarily override architecture, to be combined with --root.
+ Temporarily override architecture. When used with --root the
+ architecture will be saved.
*--cache-dir* _CACHEDIR_
Temporarily override the cache directory. _CACHEDIR_ is treated relative
@@ -118,7 +129,27 @@ The following options are available for all commands.
Continue even if binary data will be printed to the terminal.
*--force-broken-world*
- Continue even if _world_ cannot be satisfied.
+ DANGEROUS: Delete world constraints until a solution without conflicts
+ is found. This does not allow installation of packages with unsatisfiable
+ dependencies and is mainly intended to be used initramfs boot and is
+ implied by *--initramfs-diskless-boot*. The primary purpose is to allow
+ run-from-tmpfs systems to boot if media was upgraded and some packages
+ are no longer available in the new release.
+
+ APK will try to determine the world constraints that causes packages
+ with conflicting dependencies and start disabling the world constraints
+ in this order until a satisfiable set of constraints is left. Using this
+ switch on hard disk installation will likely result in unexpected removal
+ of some packages. If uncertain, use with *--interactive* or *--simulate*
+ first.
+
+*--force-missing-repositories*
+ Continue even if some of the repository indexes are not available.
+
+*--force-no-chroot*
+ Disable chroot for scripts. This can be used for rootfs creation when
+ chroot is not available. Scripts running outside a chroot environment
+ may modify and damage the host system.
*--force-non-repository*
Continue even if packages may be lost on reboot. This can happen when
@@ -139,12 +170,24 @@ The following options are available for all commands.
*--no-cache*
Do not use any local cache path.
+*--no-check-certificate*
+ Do not validate the HTTPS server certificates.
+
+*--no-interactive*
+ Disable interactive mode.
+
+*--no-logfile*
+ Disable writing to the log file.
+
*--no-network*
Do not use the network. The cache is still used when possible.
*--no-progress*
Disable progress bar even for TTYs.
+*--preserve-env*
+ Pass user environment down to scripts.
+
*--print-arch*
Print default arch and exit.
@@ -155,8 +198,10 @@ The following options are available for all commands.
Write progress to the specified file descriptor.
*--purge*
- Delete modified configuration files on package removal and uninstalled
- packages from cache on cache clean.
+ Purge modified configuration and cached packages. Enables deletion of
+ modified configuration files on package removal. On cache clean action
+ this enables deletion of unneeded cached packages (uninstalled packages
+ on tmpfs installations or all packages on disk installations).
*--repositories-file* _REPOFILE_
Override system repositories, see *apk-repositories*(8). Specifying this
@@ -164,6 +209,10 @@ The following options are available for all commands.
processing. The given _REPOFILE_ is relative to the startup directory since
apk 2.12.0_rc2.
+*--timeout* _TIME_
+ Timeout network connections if no progress is made in TIME seconds.
+ The default is 60 seconds.
+
*--wait* _TIME_
Wait for TIME seconds to get an exclusive repository lock before
failing.
@@ -173,7 +222,10 @@ The following options are available for all commands.
The following options are available for all commands which commit the database.
*-s, --simulate*
- Simulate the requested operation without making any changes.
+ Simulate the requested operation without making any changes. The database
+ is opened in read only mode, and auto-updating of indexes is disabled.
+ You may want to run "apk update" before running a simulation to make sure
+ it is done with up-to-date repository indexes.
*--clean-protected*
Do not create .apk-new files in configuration directories.
@@ -182,18 +234,173 @@ The following options are available for all commands which commit the database.
Read list of overlay files from stdin. Normally this is used only during
initramfs when booting run-from-tmpfs installation.
+*--no-commit-hooks*
+ Skip pre/post hook scripts (but not other scripts).
+
*--no-scripts*
Do not execute any scripts. Useful for extracting a system image for
different architecture on alternative _ROOT_.
-*--no-commit-hooks*
- Skip pre/post hook scripts (but not other scripts).
-
*--initramfs-diskless-boot*
Used by initramfs when it's recreating root tmpfs. This enables selected
force options to minimize failure, and disables commit hooks, among
other features.
+
+# SOURCE OPTIONS
+
+The following options are available for all commands which operate on the
+package indexes only.
+
+*--from* _FROMSPEC_
+ Search packages from: *system* (all system sources), *repositories*
+ (exclude installed database), *installed* (exclude normal repositories)
+ or *none* (commandline repositories only).
+
+# SIGNING OPTIONS
+
+The following options are available for all commands which sign files.
+
+*--sign-key* _KEYFILE_
+ Sign files with the specified _KEYFILE_.
+
+# ENVIRONMENT
+
+*LANG*
+ Used to determine if UTF-8 is supported, and set the default progress
+ character accordingly.
+
+*SOURCE_DATE_EPOCH*
+ See *apk-index*(8).
+
+*TERM*
+ Used to determine if the terminal is dumb or not. Progress bar is not
+ enabled on dumb terminals by default.
+
+## Variables to configure built-in libfetch
+
+*FETCH_BIND_ADDRESS*
+ A local IP address to which libfetch will bind all sockets it creates.
+ Can be useful for source routing.
+
+*FTP_PROXY*, *ftp_proxy*
+ If set, these variables should contain the proxy URL for *ftp*
+ connections.
+
+*NETRC*
+ Specify the *.netrc* file to read for authentication secrets. If not
+ set, defaults to *$HOME/.netrc*.
+
+*HTTP_AUTH*++
+*HTTP_REFERER*++
+*HTTP_USER_AGENT*
+ Specify a custom HTTP level *Authorization*, *Referer* or *User-Agent* header.
+
+*HTTP_PROXY*, *http_proxy*++
+*HTTPS_PROXY*, *https_proxy*++
+ If set, these variables should contain the proxy URL for *http*
+ and *https* connections respectively.
+
+*HTTP_PROXY_AUTH*
+ Specify a HTTP *Proxy-Authorization* header. Used only if the connection
+ is established through a configured HTTP proxy.
+
+*NO_PROXY*, *no_proxy*
+ Comma-separated list of domain extensions or CIDR notation IP addresses
+ to which a proxy should _not_ be used for. This is used explicitly to
+ test the URL hostname portion only. That is, specifying an IP address
+ or CIDR block will not match a DNS name that resolves to the IP address.
+
+*SSL_CLIENT_CERT_FILE*++
+*SSL_CLIENT_KEY_FILE*
+ Override default SSL client certificate and corresponding private key
+ filename.
+
+*SSL_NO_VERIFY_HOSTNAME*
+ If set to anything, disables the server certificate name verification.
+
+# FILES
+
+## Configuration files
+
+*/etc/apk/arch*
+ The CPU architecture for this database. See *apk-package*(5) section
+ on package metadata field *arch* for the list.
+
+*/etc/apk/cache*
+ This is expected to be a symlink to directory what apk will use
+ as package cache. See also *apk-cache*(5) and *apk-cache*(8).
+
+*/etc/apk/commit_hooks.d/\*.pre-commit*++
+*/etc/apk/commit_hooks.d/\*.post-commit*
+ Hook scripts which are executed before or after changes to database are
+ committed. If a pre-commit script returns failure, the commit is aborted.
+
+ If *--no-scripts* or *--no-commit-hooks* option is specified, these
+ hook scripts are not executed.
+
+*/etc/apk/interactive*
+ If this file exists and apk is running on a tty, *--interactive*
+ mode is enabled by default.
+
+*/etc/apk/keys*
+ A directory containing trusted signing keys for apk.
+
+*/etc/apk/protected_paths.d/\*.list*
+ Configuration files to specify how to treat changes to specified
+ directory or file masks.
+
+*/etc/apk/repositories*++
+*/etc/apk/repositories.d/\*.list*
+ Configuration files to specify repositories. See *apk-repositories*(5)
+ for details.
+
+*/etc/apk/world*
+ Top level requirements and constraints on what should be installed.
+ See *apk-world*(5) for details.
+
+## Configuration files for built-in libfetch
+
+*/etc/apk/ca.pem*
+ CA certificate store bundle for verifying server certificates.
+ If not present, the default system CA store is used.
+
+*/etc/apk/crl.pem*
+ CRL store to check the server certificates against.
+
+*/etc/apk/cert.key*
+ Client certificate private key.
+
+*/etc/apk/cert.pem*
+ Client certificate to use for authentication.
+
+## System files
+
+*/lib/apk/db/lock*
+ A lock file used to allow only one concurrent write transaction on
+ the system.
+
+*/lib/apk/db/installed*
+ Database of installed packages and their contents.
+
+*/lib/apk/db/scripts.tar*
+ Collection of all package scripts from currently installed packages.
+
+*/lib/apk/db/triggers*
+ List of triggers rules for currently installed packages.
+
+*/lib/apk/db-uvol*
+ Database symlink or a directory with similar structure as */lib/apk/db/*,
+ but which used for package content when managed using OpenWRT *uvol*
+ volume manager.
+
+*/lib/apk/exec*
+ Temporary directory for extraction and execution of package scripts
+ and triggers.
+
+*/var/log/apk.log*
+ Log file for changes done to the system.
+
# NOTES
This apk has coffee making abilities.
diff --git a/doc/meson.build b/doc/meson.build
index 6e2173a..8e1c125 100644
--- a/doc/meson.build
+++ b/doc/meson.build
@@ -1,4 +1,4 @@
-man_files = files(
+man_filenames = [
'apk.8.scd',
'apk-add.8.scd',
'apk-audit.8.scd',
@@ -13,34 +13,38 @@ man_files = files(
'apk-keys.5.scd',
'apk-list.8.scd',
'apk-manifest.8.scd',
+ 'apk-package.5.scd',
'apk-policy.8.scd',
'apk-repositories.5.scd',
'apk-search.8.scd',
'apk-stats.8.scd',
'apk-update.8.scd',
'apk-upgrade.8.scd',
+ 'apk-v2.5.scd',
+ 'apk-v3.5.scd',
'apk-verify.8.scd',
'apk-version.8.scd',
'apk-world.5.scd',
-)
+]
+man_files = files(man_filenames)
if scdoc_dep.found()
- scdoc_prog = find_program(scdoc_dep.get_pkgconfig_variable('scdoc'), native: true)
+ scdoc_prog = find_program(scdoc_dep.get_variable(pkgconfig: 'scdoc'), native: true)
sh = find_program('sh', native: true)
mandir = get_option('mandir')
- foreach filename : man_files
+ foreach filename : man_filenames
topic = '@0@'.format(filename).split('.')[-3].split('/')[-1]
section = '@0@'.format(filename).split('.')[-2]
output = '@0@.@1@'.format(topic, section)
custom_target(
output,
- input: filename,
+ input: files(filename),
capture: true,
output: output,
command: [
- sh, '-c', '@0@ < @INPUT@'.format(scdoc_prog.path())
+ sh, '-c', '@0@ < @INPUT@'.format(scdoc_prog.full_path())
],
install: true,
install_dir: '@0@/man@1@'.format(mandir, section)
diff --git a/get-version.sh b/get-version.sh
new file mode 100755
index 0000000..f85e7e1
--- /dev/null
+++ b/get-version.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+try_version() {
+ # usable version?
+ [ "${#1}" -gt 0 ] || return 0
+ # strip the git tag prefix
+ echo "${1#v}"
+ exit 0
+}
+
+# check for build system provided forced version
+for version in "$@"; do
+ try_version "$version"
+done
+try_version "${VERSION}"
+try_version "${CI_COMMIT_TAG}"
+# GitLab but no tag info, use the 'git describe' from environment variable
+# once https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/1633
+# gets completed and merged upstream.
+[ "$CI_COMMIT_REF_NAME" ] && try_version "$(cat VERSION)"
+[ -d .git ] && try_version "$(git describe)"
+try_version "$(cat VERSION)"
+exit 1
diff --git a/libfetch/Makefile b/libfetch/Makefile
index c4b56f5..0c2617f 100644
--- a/libfetch/Makefile
+++ b/libfetch/Makefile
@@ -4,6 +4,13 @@ generate-y += ftperr.h httperr.h
CFLAGS_common.o += -DCA_CERT_FILE=\"$(CONFDIR)/ca.pem\" -DCA_CRL_FILE=\"$(CONFDIR)/crl.pem\"
CFLAGS_common.o += -DCLIENT_CERT_FILE=\"$(CONFDIR)/cert.pem\" -DCLIENT_KEY_FILE=\"$(CONFDIR)/cert.key\"
+PKG_CONFIG ?= pkg-config
+
+OPENSSL_CFLAGS := $(shell $(PKG_CONFIG) --cflags openssl)
+OPENSSL_LIBS := $(shell $(PKG_CONFIG) --libs openssl)
+
+CFLAGS_ALL += $(OPENSSL_CFLAGS)
+
quiet_cmd_generr = GENERR $@
cmd_generr = $(src)/errlist.sh $(basename $(<F))_errlist $(shell echo $(basename $(<F)) | tr a-z A-Z) $< > $@
diff --git a/libfetch/common.c b/libfetch/common.c
index bcba889..174ba79 100644
--- a/libfetch/common.c
+++ b/libfetch/common.c
@@ -32,6 +32,7 @@
*/
#include <poll.h>
+#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
@@ -55,14 +56,17 @@
/*** Local data **************************************************************/
+static int ssl_verify_mode = SSL_VERIFY_PEER;
+
/*
* Error messages for resolver errors
*/
static struct fetcherr netdb_errlist[] = {
- { EAI_NODATA, FETCH_RESOLV, "Host not found" },
+ { EAI_ADDRFAMILY, FETCH_RESOLV, "Address family for host not supported" },
+ { EAI_NODATA, FETCH_RESOLV, "No address for host" },
{ EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" },
{ EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" },
- { EAI_NONAME, FETCH_RESOLV, "No address record" },
+ { EAI_NONAME, FETCH_RESOLV, "Host does not resolve" },
{ -1, FETCH_UNKNOWN, "Unknown resolver error" }
};
@@ -79,6 +83,12 @@ fetch_finderr(struct fetcherr *p, int e)
return (p);
}
+void
+fetch_no_check_certificate(void)
+{
+ ssl_verify_mode = SSL_VERIFY_NONE;
+}
+
/*
* Set error code
*/
@@ -171,6 +181,30 @@ fetch_info(const char *fmt, ...)
/*** Network-related utility functions ***************************************/
+uintmax_t
+fetch_parseuint(const char *str, const char **endptr, int radix, uintmax_t max)
+{
+ uintmax_t val = 0, maxx = max / radix, d;
+ const char *p;
+
+ for (p = str; isxdigit((unsigned char)*p); p++) {
+ unsigned char ch = (unsigned char)*p;
+ if (isdigit(ch))
+ d = ch - '0';
+ else d = tolower(ch) - 'a' + 10;
+ if (d > radix || val > maxx) goto err;
+ val *= radix;
+ if (val > max-d) goto err;
+ val += d;
+ }
+ if (p == str || val > max) goto err;
+ *endptr = p;
+ return val;
+err:
+ *endptr = "\xff";
+ return 0;
+}
+
/*
* Return the default port for a scheme
*/
@@ -247,6 +281,18 @@ fetch_bind(int sd, int af, const char *addr)
}
+static int
+compute_timeout(const struct timeval *tv)
+{
+ struct timeval cur;
+ int timeout;
+
+ gettimeofday(&cur, NULL);
+ timeout = (tv->tv_sec - cur.tv_sec) * 1000 + (tv->tv_usec - cur.tv_usec) / 1000;
+ return timeout;
+}
+
+
/*
* Establish a TCP connection to the specified port on the specified host.
*/
@@ -257,7 +303,7 @@ fetch_connect(struct url *cache_url, struct url *url, int af, int verbose)
char pbuf[10];
const char *bindaddr;
struct addrinfo hints, *res, *res0;
- int sd, error;
+ int sd, error, sock_flags = SOCK_CLOEXEC;
if (verbose)
fetch_info("looking up %s", url->host);
@@ -277,9 +323,12 @@ fetch_connect(struct url *cache_url, struct url *url, int af, int verbose)
if (verbose)
fetch_info("connecting to %s:%d", url->host, url->port);
+ if (fetchTimeout)
+ sock_flags |= SOCK_NONBLOCK;
+
/* try to connect */
for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
- if ((sd = socket(res->ai_family, res->ai_socktype,
+ if ((sd = socket(res->ai_family, res->ai_socktype | sock_flags,
res->ai_protocol)) == -1)
continue;
if (bindaddr != NULL && *bindaddr != '\0' &&
@@ -288,8 +337,41 @@ fetch_connect(struct url *cache_url, struct url *url, int af, int verbose)
close(sd);
continue;
}
+
if (connect(sd, res->ai_addr, res->ai_addrlen) == 0)
break;
+
+ if (fetchTimeout) {
+ struct timeval timeout_end;
+ struct pollfd pfd = { .fd = sd, .events = POLLOUT };
+ int r = -1;
+
+ gettimeofday(&timeout_end, NULL);
+ timeout_end.tv_sec += fetchTimeout;
+
+ do {
+ int timeout_cur = compute_timeout(&timeout_end);
+ if (timeout_cur < 0) {
+ errno = ETIMEDOUT;
+ break;
+ }
+ errno = 0;
+ r = poll(&pfd, 1, timeout_cur);
+ if (r == -1) {
+ if (errno == EINTR && fetchRestartCalls)
+ continue;
+ break;
+ }
+ } while (pfd.revents == 0);
+
+ if (r == 1 && (pfd.revents & POLLOUT) == POLLOUT) {
+ socklen_t len = sizeof(error);
+ if (getsockopt(sd, SOL_SOCKET, SO_ERROR, &error, &len) == 0 &&
+ error == 0)
+ break;
+ errno = error;
+ }
+ }
close(sd);
}
freeaddrinfo(res0);
@@ -298,6 +380,9 @@ fetch_connect(struct url *cache_url, struct url *url, int af, int verbose)
return (NULL);
}
+ if (sock_flags & SOCK_NONBLOCK)
+ fcntl(sd, F_SETFL, fcntl(sd, F_GETFL) & ~O_NONBLOCK);
+
if ((conn = fetch_reopen(sd)) == NULL) {
fetch_syserr();
close(sd);
@@ -442,7 +527,7 @@ static int fetch_ssl_setup_peer_verification(SSL_CTX *ctx, int verbose)
else
SSL_CTX_set_default_verify_paths(ctx);
- SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, 0);
+ SSL_CTX_set_verify(ctx, ssl_verify_mode, 0);
return 1;
}
@@ -545,7 +630,8 @@ fetch_ssl(conn_t *conn, const struct url *URL, int verbose)
NULL) != 1) {
fprintf(stderr, "SSL certificate subject doesn't match host %s\n",
URL->host);
- return -1;
+ if (ssl_verify_mode != SSL_VERIFY_NONE)
+ return -1;
}
}
@@ -567,17 +653,6 @@ fetch_ssl(conn_t *conn, const struct url *URL, int verbose)
return (0);
}
-static int
-compute_timeout(const struct timeval *tv)
-{
- struct timeval cur;
- int timeout;
-
- gettimeofday(&cur, NULL);
- timeout = (tv->tv_sec - cur.tv_sec) * 1000 + (tv->tv_usec - cur.tv_usec) / 1000;
- return timeout;
-}
-
/*
* Read a character from a connection w/ timeout
*/
diff --git a/libfetch/common.h b/libfetch/common.h
index dd5c14c..2c16bf7 100644
--- a/libfetch/common.h
+++ b/libfetch/common.h
@@ -38,6 +38,8 @@
#define FTP_DEFAULT_PROXY_PORT 21
#define HTTP_DEFAULT_PROXY_PORT 3128
+#include <sys/types.h>
+#include <limits.h>
#include "openssl-compat.h"
#if defined(__GNUC__) && __GNUC__ >= 3
@@ -53,6 +55,14 @@
#define HAVE_SA_LEN
#endif
+#ifndef IPPORT_MAX
+# define IPPORT_MAX 65535
+#endif
+
+#ifndef OFF_MAX
+# define OFF_MAX (((((off_t)1 << (sizeof(off_t) * CHAR_BIT - 2)) - 1) << 1) + 1)
+#endif
+
/* Connection */
typedef struct fetchconn conn_t;
@@ -86,6 +96,7 @@ struct fetcherr {
void fetch_seterr(struct fetcherr *, int);
void fetch_syserr(void);
void fetch_info(const char *, ...) LIBFETCH_PRINTFLIKE(1, 2);
+uintmax_t fetch_parseuint(const char *p, const char **endptr, int radix, uintmax_t max);
int fetch_default_port(const char *);
int fetch_default_proxy_port(const char *);
int fetch_bind(int, int, const char *);
@@ -125,7 +136,6 @@ fetchIO *http_request(struct url *, const char *,
fetchIO *ftp_request(struct url *, const char *, const char *,
struct url_stat *, struct url *, const char *);
-
/*
* Check whether a particular flag is set
*/
diff --git a/libfetch/fetch.c b/libfetch/fetch.c
index a0d4dbd..efee05f 100644
--- a/libfetch/fetch.c
+++ b/libfetch/fetch.c
@@ -39,6 +39,7 @@
#include "fetch.h"
#include "common.h"
+fetch_redirect_t fetchRedirectMethod;
auth_t fetchAuthMethod;
int fetchLastErrCode;
char fetchLastErrString[MAXERRSTRING];
@@ -354,7 +355,55 @@ fetchCopyURL(const struct url *src)
}
/*
- * Split an URL into components. URL syntax is:
+ * Return value of the given hex digit.
+ */
+static int
+fetch_hexval(char ch)
+{
+ if (ch >= '0' && ch <= '9')
+ return (ch - '0');
+ else if (ch >= 'a' && ch <= 'f')
+ return (ch - 'a' + 10);
+ else if (ch >= 'A' && ch <= 'F')
+ return (ch - 'A' + 10);
+ return (-1);
+}
+
+/*
+ * Decode percent-encoded URL component from src into dst, stopping at end
+ * of string or one of the characters contained in brk. Returns a pointer
+ * to the unhandled part of the input string (null terminator, specified
+ * character). No terminator is written to dst (it is the caller's
+ * responsibility).
+ */
+static const char *
+fetch_pctdecode(char *dst, const char *src, const char *brk, size_t dlen)
+{
+ int d1, d2;
+ char c;
+ const char *s;
+
+ for (s = src; *s != '\0' && !strchr(brk, *s); s++) {
+ if (s[0] == '%' && (d1 = fetch_hexval(s[1])) >= 0 &&
+ (d2 = fetch_hexval(s[2])) >= 0 && (d1 > 0 || d2 > 0)) {
+ c = d1 << 4 | d2;
+ s += 2;
+ } else if (s[0] == '%') {
+ /* Invalid escape sequence. */
+ return (NULL);
+ } else {
+ c = *s;
+ }
+ if (!dlen)
+ return NULL;
+ dlen--;
+ *dst++ = c;
+ }
+ return (s);
+}
+
+/*
+ * Split a URL into components. URL syntax is:
* [method:/][/[user[:pwd]@]host[:port]/][document]
* This almost, but not quite, RFC1738 URL syntax.
*/
@@ -428,25 +477,25 @@ find_user:
p = strpbrk(URL, "/@");
if (p != NULL && *p == '@') {
/* username */
- for (q = URL, i = 0; (*q != ':') && (*q != '@'); q++) {
- if (i >= URL_USERLEN) {
- url_seterr(URL_BAD_AUTH);
- goto ouch;
- }
- u->user[i++] = *q;
+ q = URL;
+ q = fetch_pctdecode(u->user, q, ":@", URL_USERLEN);
+ if (q == NULL) {
+ url_seterr(URL_BAD_AUTH);
+ goto ouch;
}
/* password */
if (*q == ':') {
- for (q++, i = 0; (*q != '@'); q++) {
- if (i >= URL_PWDLEN) {
- url_seterr(URL_BAD_AUTH);
- goto ouch;
- }
- u->pwd[i++] = *q;
+ q = fetch_pctdecode(u->pwd, q + 1, "@", URL_PWDLEN);
+ if (q == NULL) {
+ url_seterr(URL_BAD_AUTH);
+ goto ouch;
}
}
-
+ if (*q != '@') {
+ url_seterr(URL_BAD_AUTH);
+ goto ouch;
+ }
p++;
} else {
p = URL;
@@ -473,15 +522,12 @@ find_user:
/* port */
if (*p == ':') {
- for (q = ++p; *q && (*q != '/'); q++)
- if (isdigit((unsigned char)*q))
- u->port = u->port * 10 + (*q - '0');
- else {
- /* invalid port */
- url_seterr(URL_BAD_PORT);
- goto ouch;
- }
- p = q;
+ u->port = fetch_parseuint(p + 1, &p, 10, IPPORT_MAX);
+ if (*p && *p != '/') {
+ /* invalid port */
+ url_seterr(URL_BAD_PORT);
+ goto ouch;
+ }
}
/* document */
diff --git a/libfetch/fetch.h b/libfetch/fetch.h
index 66b77f4..15c60e9 100644
--- a/libfetch/fetch.h
+++ b/libfetch/fetch.h
@@ -101,6 +101,8 @@ struct url_list {
extern "C" {
#endif
+void fetch_no_check_certificate(void);
+
void fetchIO_close(fetchIO *);
ssize_t fetchIO_read(fetchIO *, void *, size_t);
ssize_t fetchIO_write(fetchIO *, const void *, size_t);
@@ -162,6 +164,10 @@ char *fetchUnquoteFilename(struct url *);
void fetchConnectionCacheInit(int, int);
void fetchConnectionCacheClose(void);
+/* Redirects */
+typedef void (*fetch_redirect_t)(int, const struct url *, const struct url *);
+extern fetch_redirect_t fetchRedirectMethod;
+
/* Authentication */
typedef int (*auth_t)(struct url *);
extern auth_t fetchAuthMethod;
diff --git a/libfetch/ftp.c b/libfetch/ftp.c
index 8f9f04f..14323dc 100644
--- a/libfetch/ftp.c
+++ b/libfetch/ftp.c
@@ -471,8 +471,7 @@ ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
}
for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
/* nothing */ ;
- for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++)
- us->size = us->size * 10 + *ln - '0';
+ us->size = fetch_parseuint(ln, (const char **) &ln, 10, OFF_MAX);
if (*ln && !isspace((unsigned char)*ln)) {
ftp_seterr(FTP_PROTOCOL_ERROR);
us->size = -1;
@@ -693,14 +692,14 @@ ftp_transfer(conn_t *conn, const char *oper, const char *file, const char *op_ar
retry_mode:
/* open data socket */
- if ((sd = socket(u.ss.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+ if ((sd = socket(u.ss.ss_family, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP)) == -1) {
fetch_syserr();
return (NULL);
}
if (pasv) {
unsigned char addr[64];
- char *ln, *p;
+ const char *ln, *p;
unsigned int i;
int port;
@@ -737,10 +736,15 @@ retry_mode:
for (p = ln + 3; *p && !isdigit((unsigned char)*p); p++)
/* nothing */ ;
if (!*p) goto protocol_error;
- l = (e == FTP_PASSIVE_MODE ? 6 : 21);
- for (i = 0; *p && i < l; i++, p++)
- addr[i] = strtol(p, &p, 10);
- if (i < l) goto protocol_error;
+ l = (e == FTP_PASSIVE_MODE ? 6 : 21) - 1;
+ for (i = 0; *p && i < l; i++, p++) {
+ while (isspace((unsigned char)*p)) p++;
+ addr[i] = fetch_parseuint(p, &p, 10, UCHAR_MAX);
+ if (*p != ',') goto protocol_error;
+ }
+ while (isspace((unsigned char)*p)) p++;
+ addr[i] = fetch_parseuint(p, &p, 10, UCHAR_MAX);
+ if (*p && *p != ')') goto protocol_error;
break;
case FTP_EPASSIVE_MODE:
for (p = ln + 3; *p && *p != '('; p++)
diff --git a/libfetch/http.c b/libfetch/http.c
index 59d6292..e8d2fc6 100644
--- a/libfetch/http.c
+++ b/libfetch/http.c
@@ -134,29 +134,19 @@ struct httpio
static int
http_new_chunk(struct httpio *io)
{
- char *p;
+ const char *p;
if (fetch_getln(io->conn) == -1)
- return (-1);
+ return -1;
- if (io->conn->buflen < 2 || !isxdigit((unsigned char)*io->conn->buf))
- return (-1);
+ if (io->conn->buflen < 2)
+ return -1;
- for (p = io->conn->buf; *p && !isspace((unsigned char)*p); ++p) {
- if (*p == ';')
- break;
- if (!isxdigit((unsigned char)*p))
- return (-1);
- if (isdigit((unsigned char)*p)) {
- io->chunksize = io->chunksize * 16 +
- *p - '0';
- } else {
- io->chunksize = io->chunksize * 16 +
- 10 + tolower((unsigned char)*p) - 'a';
- }
- }
+ io->chunksize = fetch_parseuint(io->conn->buf, &p, 16, SIZE_MAX);
+ if (*p && *p != ';' && !isspace(*p))
+ return -1;
- return (io->chunksize);
+ return io->chunksize;
}
/*
@@ -490,11 +480,12 @@ http_parse_mtime(const char *p, time_t *mtime)
char *locale, *r;
struct tm tm;
- locale = strdupa(setlocale(LC_TIME, NULL));
+ locale = strdup(setlocale(LC_TIME, NULL));
setlocale(LC_TIME, "C");
r = strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm);
/* XXX should add support for date-2 and date-3 */
setlocale(LC_TIME, locale);
+ free(locale);
if (r == NULL)
return (-1);
*mtime = timegm(&tm);
@@ -502,22 +493,6 @@ http_parse_mtime(const char *p, time_t *mtime)
}
/*
- * Parse a content-length header
- */
-static int
-http_parse_length(const char *p, off_t *length)
-{
- off_t len;
-
- for (len = 0; *p && isdigit((unsigned char)*p); ++p)
- len = len * 10 + (*p - '0');
- if (*p)
- return (-1);
- *length = len;
- return (0);
-}
-
-/*
* Parse a content-range header
*/
static int
@@ -532,17 +507,14 @@ http_parse_range(const char *p, off_t *offset, off_t *length, off_t *size)
first = last = -1;
++p;
} else {
- for (first = 0; *p && isdigit((unsigned char)*p); ++p)
- first = first * 10 + *p - '0';
+ first = fetch_parseuint(p, &p, 10, OFF_MAX);
if (*p != '-')
return (-1);
- for (last = 0, ++p; *p && isdigit((unsigned char)*p); ++p)
- last = last * 10 + *p - '0';
+ last = fetch_parseuint(p+1, &p, 10, OFF_MAX);
}
if (first > last || *p != '/')
return (-1);
- for (len = 0, ++p; *p && isdigit((unsigned char)*p); ++p)
- len = len * 10 + *p - '0';
+ len = fetch_parseuint(p+1, &p, 10, OFF_MAX);
if (*p || len < last - first + 1)
return (-1);
if (first == -1)
@@ -571,13 +543,12 @@ http_base64(const char *src)
"0123456789+/";
char *str, *dst;
size_t l;
- int t, r;
+ int t;
l = strlen(src);
if ((str = malloc(((l + 2) / 3) * 4 + 1)) == NULL)
return (NULL);
dst = str;
- r = 0;
while (l >= 3) {
t = (src[0] << 16) | (src[1] << 8) | src[2];
@@ -586,7 +557,7 @@ http_base64(const char *src)
dst[2] = base64[(t >> 6) & 0x3f];
dst[3] = base64[(t >> 0) & 0x3f];
src += 3; l -= 3;
- dst += 4; r += 4;
+ dst += 4;
}
switch (l) {
@@ -597,7 +568,6 @@ http_base64(const char *src)
dst[2] = base64[(t >> 6) & 0x3f];
dst[3] = '=';
dst += 4;
- r += 4;
break;
case 1:
t = src[0] << 16;
@@ -605,7 +575,6 @@ http_base64(const char *src)
dst[1] = base64[(t >> 12) & 0x3f];
dst[2] = dst[3] = '=';
dst += 4;
- r += 4;
break;
case 0:
break;
@@ -850,7 +819,7 @@ http_request(struct url *URL, const char *op, struct url_stat *us,
int e, i, n;
off_t offset, clength, length, size;
time_t mtime;
- const char *p;
+ const char *p, *q;
fetchIO *f;
hdr_t h;
char hbuf[URL_HOSTLEN + 7], *host;
@@ -1050,13 +1019,16 @@ http_request(struct url *URL, const char *op, struct url_stat *us,
keep_alive = (strcasecmp(p, "keep-alive") == 0);
break;
case hdr_content_length:
- http_parse_length(p, &clength);
+ clength = fetch_parseuint(p, &q, 10, OFF_MAX);
+ if (*q) goto protocol_error;
break;
case hdr_content_range:
- http_parse_range(p, &offset, &length, &size);
+ if (http_parse_range(p, &offset, &length, &size) < 0)
+ goto protocol_error;
break;
case hdr_last_modified:
- http_parse_mtime(p, &mtime);
+ if (http_parse_mtime(p, &mtime) < 0)
+ goto protocol_error;
break;
case hdr_location:
if (!HTTP_REDIRECT(conn->err))
@@ -1087,6 +1059,8 @@ http_request(struct url *URL, const char *op, struct url_stat *us,
}
new->offset = url->offset;
new->length = url->length;
+ if (fetchRedirectMethod)
+ fetchRedirectMethod(conn->err, url, new);
break;
case hdr_transfer_encoding:
/* XXX weak test*/
diff --git a/libfetch/meson.build b/libfetch/meson.build
index 90e133b..431ba19 100644
--- a/libfetch/meson.build
+++ b/libfetch/meson.build
@@ -7,7 +7,7 @@ libfetch_src = [
'openssl-compat.c'
]
-errlist_generator = find_program('./errlist.sh')
+errlist_generator = find_program('errlist.sh')
ftperr_h = custom_target(
'ftperr.h',
@@ -38,6 +38,10 @@ libfetch = static_library(
'fetch',
libfetch_src,
c_args: libfetch_cargs,
+ dependencies: [
+ libportability_dep.partial_dependency(compile_args: true, includes: true),
+ openssl_dep.partial_dependency(compile_args: true, includes: true)
+ ],
)
libfetch_dep = declare_dependency(
diff --git a/meson.build b/meson.build
index 902854e..1a44c11 100644
--- a/meson.build
+++ b/meson.build
@@ -1,9 +1,9 @@
project(
'apk-tools',
['c'],
- default_options : ['c_std=gnu99', 'optimization=2'],
- version: '3.0.0_pre0',
- meson_version: '>=0.52'
+ default_options : ['c_std=gnu11', 'optimization=2', 'warning_level=1'],
+ version: run_command('./get-version.sh', check: true).stdout().strip(),
+ meson_version: '>=0.55'
)
pkgc = import('pkgconfig')
@@ -13,23 +13,26 @@ apk_libdir = get_option('libdir')
lua_bin = find_program('lua' + get_option('lua_version'), required: get_option('help'))
lua_dep = dependency('lua' + get_option('lua_version'), required: get_option('lua'))
scdoc_dep = dependency('scdoc', version: '>=1.10', required: get_option('docs'))
+openssl_dep = dependency('openssl')
+openssl_static_dep = dependency('openssl', static: true)
+zlib_dep = dependency('zlib')
+zlib_static_dep = dependency('zlib', static: true)
+libzstd_dep = dependency('libzstd', required: get_option('zstd'))
+libzstd_static_dep = dependency('libzstd', required: get_option('zstd'), static: true)
-shared_deps = [
- dependency('zlib'),
- dependency('openssl'),
-]
-
-static_deps = [
- dependency('openssl', static: true),
- dependency('zlib', static: true),
-]
+shared_deps = [ openssl_dep, zlib_dep, libzstd_dep ]
+static_deps = [ openssl_static_dep, zlib_static_dep, libzstd_static_dep ]
add_project_arguments('-D_GNU_SOURCE', language: 'c')
+# Needed for 64-bit off_t and friends on glibc.
+add_project_arguments('-D_FILE_OFFSET_BITS=64', language: 'c')
+
# If we're a subproject we only want the static lib and not files
subproject = meson.is_subproject()
subdir('doc')
+subdir('portability')
subdir('libfetch')
subdir('src')
subdir('tests')
diff --git a/meson_options.txt b/meson_options.txt
index 9d803d9..693f46e 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,5 +1,9 @@
+option('arch_prefix', description: 'Define a custom arch prefix for default arch', type: 'string')
+option('compressed-help', description: 'Compress help database, needs lua-zlib', type: 'boolean', value: true)
option('docs', description: 'Build manpages with scdoc', type: 'feature', value: 'auto')
-option('help', description: 'Build help into apk binaries, needs lua and lua-zlib', type: 'feature', value: 'auto')
+option('help', description: 'Build help into apk binaries, needs lua', type: 'feature', value: 'auto')
option('lua', description: 'Build luaapk (lua bindings)', type: 'feature', value: 'auto')
option('lua_version', description: 'Lua version to build against', type: 'string', value: '5.3')
option('static_apk', description: 'Also build apk.static', type: 'boolean', value: false)
+option('uvol_db_target', description: 'Default target for uvol database layer', type: 'string')
+option('zstd', description: 'Build with zstd support', type: 'boolean', value: true)
diff --git a/portability/endian.h b/portability/endian.h
new file mode 100644
index 0000000..a9cc380
--- /dev/null
+++ b/portability/endian.h
@@ -0,0 +1,61 @@
+/* endian.h - portable endian routines
+ *
+ * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
+ * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
+ * Copyright (C) 2011 Rich Felker
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#ifdef __linux__
+# include_next <endian.h>
+#else
+
+#pragma once
+#include <stdint.h>
+
+static __inline uint16_t __portable_bswap16(uint16_t __x)
+{
+ return (__x<<8) | (__x>>8);
+}
+
+static __inline uint32_t __portable_bswap32(uint32_t __x)
+{
+ return (__x>>24) | (__x>>8&0xff00) | (__x<<8&0xff0000) | (__x<<24);
+}
+
+static __inline uint64_t __portable_bswap64(uint64_t __x)
+{
+ return (__portable_bswap32(__x)+0ULL)<<32 | __portable_bswap32(__x>>32);
+}
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define htobe16(x) __portable_bswap16(x)
+# define be16toh(x) __portable_bswap16(x)
+# define htobe32(x) __portable_bswap32(x)
+# define be32toh(x) __portable_bswap32(x)
+# define htobe64(x) __portable_bswap64(x)
+# define be64toh(x) __portable_bswap64(x)
+# define htole16(x) (uint16_t)(x)
+# define le16toh(x) (uint16_t)(x)
+# define htole32(x) (uint32_t)(x)
+# define le32toh(x) (uint32_t)(x)
+# define htole64(x) (uint64_t)(x)
+# define le64toh(x) (uint64_t)(x)
+#else
+# define htobe16(x) (uint16_t)(x)
+# define be16toh(x) (uint16_t)(x)
+# define htobe32(x) (uint32_t)(x)
+# define be32toh(x) (uint32_t)(x)
+# define htobe64(x) (uint64_t)(x)
+# define be64toh(x) (uint64_t)(x)
+# define htole16(x) __portable_bswap16(x)
+# define le16toh(x) __portable_bswap16(x)
+# define htole32(x) __portable_bswap32(x)
+# define le32toh(x) __portable_bswap32(x)
+# define htole64(x) __portable_bswap64(x)
+# define le64toh(x) __portable_bswap64(x)
+#endif
+
+#endif
diff --git a/portability/memrchr.c b/portability/memrchr.c
new file mode 100644
index 0000000..70547f9
--- /dev/null
+++ b/portability/memrchr.c
@@ -0,0 +1,9 @@
+#include <stddef.h>
+
+void *memrchr(const void *m, int c, size_t n)
+{
+ const unsigned char *s = m;
+ c = (unsigned char)c;
+ while (n--) if (s[n]==c) return (void *)(s+n);
+ return 0;
+}
diff --git a/portability/meson.build b/portability/meson.build
new file mode 100644
index 0000000..89957c3
--- /dev/null
+++ b/portability/meson.build
@@ -0,0 +1,50 @@
+cc = meson.get_compiler('c')
+
+libportability_src = []
+
+check_symbols = [
+ ['memrchr', 'memrchr.c', 'NEED_MEMRCHR', 'string.h'],
+ ['mknodat', 'mknodat.c', 'NEED_MKNODAT', 'sys/stat.h'],
+ ['pipe2', 'pipe2.c', 'NEED_PIPE2', 'unistd.h'],
+ ['qsort_r', 'qsort_r.c', 'NEED_QSORT_R', 'stdlib.h'],
+ ['reallocarray', 'reallocarray.c', 'NEED_REALLOCARRAY', 'stdlib.h'],
+ ['strchrnul', 'strchrnul.c', 'NEED_STRCHRNUL', 'string.h'],
+ ['strlcpy', 'strlcpy.c', 'NEED_STRLCPY', 'string.h'],
+ ['SOCK_CLOEXEC', 'socket.c', 'NEED_SOCK_CLOEXEC', 'sys/socket.h'],
+]
+
+foreach f : check_symbols
+ if not cc.has_header_symbol(f.get(3), f.get(0), args: ['-D_GNU_SOURCE'])
+ add_project_arguments('-D' + f.get(2), language: 'c')
+ libportability_src += [f.get(1)]
+ endif
+endforeach
+
+# Check for wrong (non-POSIX) qsort_r prototype
+qsort_r_test = '''
+ #define _GNU_SOURCE
+ #include <stdlib.h>
+ _Static_assert(_Generic((qsort_r),
+ void (*)(void *, size_t, size_t, void *,
+ int (*)(void *, const void *, const void *)) : 1, default: 0),
+ "Bad prototype not matched");
+'''
+if cc.compiles(qsort_r_test, name: 'Test qsort_r non-POSIX prototype')
+ add_project_arguments('-DHAVE_BROKEN_QSORT_R', language: 'c')
+endif
+
+if libportability_src.length() > 0
+ libportability = static_library(
+ 'portability',
+ libportability_src,
+ )
+
+ libportability_dep = declare_dependency(
+ link_whole: libportability,
+ include_directories: include_directories('.'),
+ )
+else
+ libportability_dep = declare_dependency(
+ include_directories: include_directories('.'),
+ )
+endif
diff --git a/portability/mknodat.c b/portability/mknodat.c
new file mode 100644
index 0000000..0d5c459
--- /dev/null
+++ b/portability/mknodat.c
@@ -0,0 +1,30 @@
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev)
+{
+ int ret = 0;
+ int curdir_fd = open(".", O_DIRECTORY | O_CLOEXEC);
+ if (curdir_fd < 0)
+ return -1;
+
+ if (fchdir(dirfd) < 0) {
+ ret = -1;
+ goto cleanup;
+ }
+
+ /* if mknod fails, fall through and restore the original dirfd */
+ if (mknod(pathname, mode, dev) < 0) {
+ ret = -1;
+ }
+
+ if (fchdir(curdir_fd) < 0) {
+ ret = -1;
+ goto cleanup;
+ }
+
+cleanup:
+ close(curdir_fd);
+ return ret;
+}
diff --git a/portability/pipe2.c b/portability/pipe2.c
new file mode 100644
index 0000000..e39834f
--- /dev/null
+++ b/portability/pipe2.c
@@ -0,0 +1,22 @@
+#include <fcntl.h>
+#include <unistd.h>
+
+int pipe2(int pipefd[2], int flags)
+{
+ int r;
+
+ if ((r = pipe(pipefd)) < 0)
+ return r;
+
+ if (flags & O_CLOEXEC) {
+ (void) fcntl(pipefd[0], F_SETFD, FD_CLOEXEC);
+ (void) fcntl(pipefd[1], F_SETFD, FD_CLOEXEC);
+ }
+
+ if (flags & O_NONBLOCK) {
+ (void) fcntl(pipefd[0], F_SETFL, O_NONBLOCK);
+ (void) fcntl(pipefd[1], F_SETFL, O_NONBLOCK);
+ }
+
+ return 0;
+}
diff --git a/portability/qsort_r.c b/portability/qsort_r.c
new file mode 100644
index 0000000..7c5df27
--- /dev/null
+++ b/portability/qsort_r.c
@@ -0,0 +1,26 @@
+#include <stdlib.h>
+
+struct qsortr_ctx {
+ int (*compar)(const void *, const void *, void *);
+ void *arg;
+};
+
+static __thread struct qsortr_ctx *__ctx;
+
+static int cmp_wrapper(const void *a, const void *b)
+{
+ return __ctx->compar(a, b, __ctx->arg);
+}
+
+void qsort_r(void *base, size_t nmemb, size_t size,
+ int (*compar)(const void *, const void *, void *),
+ void *arg)
+{
+ struct qsortr_ctx ctx = {
+ .compar = compar,
+ .arg = arg,
+ };
+ __ctx = &ctx;
+ qsort(base, nmemb, size, cmp_wrapper);
+ __ctx = 0;
+}
diff --git a/portability/reallocarray.c b/portability/reallocarray.c
new file mode 100644
index 0000000..986a050
--- /dev/null
+++ b/portability/reallocarray.c
@@ -0,0 +1,11 @@
+#include <errno.h>
+#include <stdlib.h>
+
+void *reallocarray(void *ptr, size_t m, size_t n)
+{
+ if (n && m > -1 / n) {
+ errno = ENOMEM;
+ return 0;
+ }
+ return realloc(ptr, m * n);
+}
diff --git a/portability/socket.c b/portability/socket.c
new file mode 100644
index 0000000..5cc3c9a
--- /dev/null
+++ b/portability/socket.c
@@ -0,0 +1,12 @@
+#include <sys/socket.h>
+#include <fcntl.h>
+#undef socket
+
+int __portable_socket(int domain, int type, int protocol)
+{
+ int fd = socket(domain, type & ~(SOCK_CLOEXEC|SOCK_NONBLOCK), protocol);
+ if (fd < 0) return fd;
+ if (type & SOCK_CLOEXEC) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
+ if (type & SOCK_NONBLOCK) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+ return fd;
+}
diff --git a/portability/stdlib.h b/portability/stdlib.h
new file mode 100644
index 0000000..3f437dc
--- /dev/null
+++ b/portability/stdlib.h
@@ -0,0 +1,37 @@
+#pragma once
+#include_next <stdlib.h>
+
+#ifdef NEED_REALLOCARRAY
+void *reallocarray(void *ptr, size_t m, size_t n);
+#endif
+
+#ifdef NEED_QSORT_R
+void qsort_r(void *base, size_t nmemb, size_t size,
+ int (*compar)(const void *, const void *, void *),
+ void *arg);
+#endif
+
+#ifdef HAVE_BROKEN_QSORT_R
+struct __portable_qsort_r_compat_arg {
+ int (*compar)(const void *, const void *, void *);
+ void *arg;
+};
+
+static inline int __portable_qsort_r_compar_compat(void *arg, const void *a, const void *b)
+{
+ struct __portable_qsort_r_compat_arg *compat_arg = arg;
+ return compat_arg->compar(a, b, compat_arg->arg);
+}
+
+static inline void __portable_qsort_r(void *base, size_t nmemb, size_t size,
+ int (*compar)(const void *, const void *, void *),
+ void *arg)
+{
+ struct __portable_qsort_r_compat_arg compat_arg = {
+ .compar = compar,
+ .arg = arg,
+ };
+ qsort_r(base, nmemb, size, &compat_arg, __portable_qsort_r_compar_compat);
+}
+#define qsort_r(...) __portable_qsort_r(__VA_ARGS__)
+#endif
diff --git a/portability/strchrnul.c b/portability/strchrnul.c
new file mode 100644
index 0000000..cb55462
--- /dev/null
+++ b/portability/strchrnul.c
@@ -0,0 +1,6 @@
+#include <string.h>
+
+char *strchrnul(const char *s, int c)
+{
+ return strchr(s, c) ?: (char *)s + strlen(s);
+}
diff --git a/portability/string.h b/portability/string.h
new file mode 100644
index 0000000..37f7820
--- /dev/null
+++ b/portability/string.h
@@ -0,0 +1,13 @@
+#include_next <string.h>
+
+#ifdef NEED_MEMRCHR
+void *memrchr(const void *m, int c, size_t n);
+#endif
+
+#ifdef NEED_STRCHRNUL
+char *strchrnul(const char *s, int c);
+#endif
+
+#ifdef NEED_STRLCPY
+size_t strlcpy(char *dst, const char *src, size_t size);
+#endif
diff --git a/portability/strlcpy.c b/portability/strlcpy.c
new file mode 100644
index 0000000..6ce46e3
--- /dev/null
+++ b/portability/strlcpy.c
@@ -0,0 +1,13 @@
+#include <stddef.h>
+#include <string.h>
+
+size_t strlcpy(char *dst, const char *src, size_t size)
+{
+ size_t ret = strlen(src), len;
+ if (!size) return ret;
+ len = ret;
+ if (len >= size) len = size - 1;
+ memcpy(dst, src, len);
+ dst[len] = 0;
+ return ret;
+}
diff --git a/portability/sys/socket.h b/portability/sys/socket.h
new file mode 100644
index 0000000..9eed851
--- /dev/null
+++ b/portability/sys/socket.h
@@ -0,0 +1,9 @@
+#include_next <sys/socket.h>
+
+#ifdef NEED_SOCK_CLOEXEC
+#define SOCK_CLOEXEC 02000000
+#define SOCK_NONBLOCK 04000
+
+int __portable_socket(int domain, int type, int protocol);
+#define socket(...) __portable_socket(__VA_ARGS__)
+#endif
diff --git a/portability/sys/stat.h b/portability/sys/stat.h
new file mode 100644
index 0000000..1d3e5f8
--- /dev/null
+++ b/portability/sys/stat.h
@@ -0,0 +1,5 @@
+#include_next <sys/stat.h>
+
+#ifdef NEED_MKNODAT
+int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev);
+#endif
diff --git a/portability/sys/sysmacros.h b/portability/sys/sysmacros.h
new file mode 100644
index 0000000..863972e
--- /dev/null
+++ b/portability/sys/sysmacros.h
@@ -0,0 +1,9 @@
+#ifdef __linux__
+# include_next <sys/sysmacros.h>
+#else
+# include <stdint.h>
+# include <sys/types.h>
+# define major(x) ((int32_t)(((u_int32_t)(x) >> 24) & 0xff))
+# define minor(x) ((int32_t)((x) & 0xffffff))
+# define makedev(x, y) ((dev_t)(((x) << 24) | (y)))
+#endif
diff --git a/portability/unistd.h b/portability/unistd.h
new file mode 100644
index 0000000..f87ff71
--- /dev/null
+++ b/portability/unistd.h
@@ -0,0 +1,10 @@
+#include_next <unistd.h>
+
+#ifdef NEED_PIPE2
+int pipe2(int pipefd[2], int flags);
+#endif
+
+#ifdef __APPLE__
+# include <crt_externs.h>
+# define environ (*_NSGetEnviron())
+#endif
diff --git a/src/Makefile b/src/Makefile
index 970354b..f7873cb 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -20,12 +20,20 @@ libapk_soname := 2.99.0
libapk_so := $(obj)/libapk.so.$(libapk_soname)
libapk.so.$(libapk_soname)-objs := \
adb.o adb_comp.o adb_walk_adb.o adb_walk_genadb.o adb_walk_gentext.o adb_walk_text.o apk_adb.o \
- atom.o blob.o commit.o common.o context.o crypto_openssl.o database.o hash.o \
- io.o io_archive.o io_gunzip.o io_url.o \
- package.o pathbuilder.o print.o solver.o trust.o version.o
+ atom.o blob.o commit.o common.o context.o crypto.o crypto_openssl.o ctype.o database.o hash.o \
+ extract_v2.o extract_v3.o fs_fsys.o fs_uvol.o io.o io_gunzip.o io_url_libfetch.o \
+ tar.o package.o pathbuilder.o print.o solver.o trust.o version.o
libapk.so.$(libapk_soname)-libs := libfetch/libfetch.a
+# ZSTD support can be disabled
+ifneq ($(ZSTD),no)
+ZSTD_CFLAGS := $(shell $(PKG_CONFIG) --cflags libzstd)
+ZSTD_LIBS := $(shell $(PKG_CONFIG) --libs libzstd)
+CFLAGS_adb_comp.o += -DHAVE_ZSTD
+libapk.so.$(libapk_soname)-objs += io_zstd.o
+endif
+
shlibs-y += libapk.so.$(libapk_soname)
LDFLAGS_libapk.so.$(libapk_soname) += -Wl,-soname,libapk.so.$(libapk_soname)
@@ -51,7 +59,7 @@ LUA_PC ?= lua$(LUA_VERSION)
LUA_LIBDIR ?= /usr/lib/lua/$(LUA_VERSION)
shlibs-y += apk.so
apk.so-objs := lua-apk.o
-CFLAGS_lua-apk.o := -DAPK_VERSION=\"$(FULL_VERSION)\" \
+CFLAGS_lua-apk.o := -DAPK_VERSION=\"$(VERSION)\" \
$(shell $(PKG_CONFIG) $(LUA_PC) --cflags)
apklua_so := $(obj)/apk.so
install-apklua_so := $(INSTALLDIR) $(DESTDIR)$(LUA_LIBDIR) && \
@@ -67,20 +75,14 @@ apk-objs := \
app_mkpkg.o app_policy.o app_update.o app_upgrade.o app_search.o \
app_stats.o app_verify.o app_version.o app_vertest.o applet.o
-ifeq ($(ADB),y)
-libapk.so.$(libapk_soname)-objs += apk_adb.o
-apk-objs += app_adbdump.o app_adbsign.o app_mkndx.o \
- app_convdb.o app_convndx.o
-endif
-
LIBS_apk := -lapk
LIBS_apk-test := -lapk
LIBS_apk.so := -L$(obj) -lapk
-CFLAGS_ALL += -D_ATFILE_SOURCE -Ilibfetch
-CFLAGS_apk.o := -DAPK_VERSION=\"$(FULL_VERSION)\"
-CFLAGS_apk-static.o := -DAPK_VERSION=\"$(FULL_VERSION)\" -DOPENSSL_NO_ENGINE
-CFLAGS_apk-test.o := -DAPK_VERSION=\"$(FULL_VERSION)\" -DOPENSSL_NO_ENGINE -DTEST_MODE
+CFLAGS_ALL += -D_ATFILE_SOURCE -Ilibfetch -Iportability
+CFLAGS_apk.o := -DAPK_VERSION=\"$(VERSION)\"
+CFLAGS_apk-static.o := -DAPK_VERSION=\"$(VERSION)\" -DOPENSSL_NO_ENGINE
+CFLAGS_apk-test.o := -DAPK_VERSION=\"$(VERSION)\" -DOPENSSL_NO_ENGINE -DTEST_MODE
progs-$(STATIC) += apk.static
apk.static-objs := $(filter-out apk.o,$(apk-objs)) apk-static.o
@@ -90,9 +92,9 @@ LIBS_apk.static := -Wl,--as-needed -ldl -Wl,--no-as-needed
LDFLAGS_apk += -L$(obj)
LDFLAGS_apk-test += -L$(obj)
-CFLAGS_ALL += $(OPENSSL_CFLAGS) $(ZLIB_CFLAGS)
+CFLAGS_ALL += $(OPENSSL_CFLAGS) $(ZLIB_CFLAGS) $(ZSTD_CFLAGS)
LIBS := -Wl,--as-needed \
- $(OPENSSL_LIBS) $(ZLIB_LIBS) \
+ $(OPENSSL_LIBS) $(ZLIB_LIBS) $(ZSTD_LIBS) \
-Wl,--no-as-needed
# Help generation
diff --git a/src/adb.c b/src/adb.c
index 1fc135c..7759ede 100644
--- a/src/adb.c
+++ b/src/adb.c
@@ -1,13 +1,10 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <malloc.h>
-#include <assert.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/stat.h>
-#include <openssl/pem.h>
-#include <openssl/err.h>
+#include <sys/uio.h>
#include "adb.h"
#include "apk_blob.h"
@@ -18,11 +15,13 @@ static char padding_zeroes[ADB_BLOCK_ALIGNMENT] = {0};
/* Block enumeration */
static inline struct adb_block *adb_block_validate(struct adb_block *blk, apk_blob_t b)
{
- size_t pos = (char *)blk - b.ptr;
+ size_t pos = (char *)blk - b.ptr, len = (size_t)(b.len - pos);
if (pos == b.len) return NULL;
- if (sizeof(struct adb_block) > b.len - pos) return ERR_PTR(-APKE_ADB_BLOCK);
- if (adb_block_rawsize(blk) < sizeof(struct adb_block)) return ERR_PTR(-APKE_ADB_BLOCK);
- if (adb_block_size(blk) > b.len - pos) return ERR_PTR(-APKE_ADB_BLOCK);
+ if (sizeof(uint32_t) > len) return ERR_PTR(-APKE_ADB_BLOCK);
+ size_t hdrlen = adb_block_hdrsize(blk);
+ if (hdrlen > len) return ERR_PTR(-APKE_ADB_BLOCK);
+ if (adb_block_rawsize(blk) < hdrlen) return ERR_PTR(-APKE_ADB_BLOCK);
+ if (adb_block_size(blk) > len) return ERR_PTR(-APKE_ADB_BLOCK);
return blk;
}
@@ -37,7 +36,7 @@ static struct adb_block *adb_block_next(struct adb_block *cur, apk_blob_t b)
}
#define adb_foreach_block(__blk, __adb) \
- for (__blk = adb_block_first(__adb); !IS_ERR_OR_NULL(__blk); __blk = adb_block_next(__blk, __adb))
+ for (__blk = adb_block_first(__adb); __blk && !IS_ERR(__blk); __blk = adb_block_next(__blk, __adb))
/* Init stuff */
int adb_free(struct adb *db)
@@ -82,7 +81,7 @@ static int __adb_m_parse(struct adb *db, apk_blob_t data, struct apk_trust *t,
struct adb_verify_ctx vfy = {};
struct adb_block *blk;
struct apk_istream is;
- int r = 0, trusted = t ? 0 : 1;
+ int r = 0, trusted = (t && t->allow_untrusted) ? 1 : 0;
uint32_t type, allowed = BIT(ADB_BLOCK_ADB);
adb_foreach_block(blk, data) {
@@ -94,14 +93,14 @@ static int __adb_m_parse(struct adb *db, apk_blob_t data, struct apk_trust *t,
}
switch (type) {
case ADB_BLOCK_ADB:
- allowed = BIT(ADB_BLOCK_SIG) | BIT(ADB_BLOCK_DATA) | BIT(ADB_BLOCK_DATAX);
+ allowed = BIT(ADB_BLOCK_SIG) | BIT(ADB_BLOCK_DATA);
if (b.len < 16) {
r = -APKE_ADB_BLOCK;
- break;
+ goto err;
}
if (((struct adb_hdr*)b.ptr)->adb_compat_ver != 0) {
r = -APKE_ADB_VERSION;
- break;
+ goto err;
}
db->adb = b;
break;
@@ -111,11 +110,11 @@ static int __adb_m_parse(struct adb *db, apk_blob_t data, struct apk_trust *t,
trusted = 1;
break;
case ADB_BLOCK_DATA:
- allowed = BIT(ADB_BLOCK_DATA) | BIT(ADB_BLOCK_DATAX);
- if (!trusted) goto err;
- break;
- case ADB_BLOCK_DATAX:
- r = -APKE_ADB_BLOCK;
+ allowed = BIT(ADB_BLOCK_DATA);
+ if (!trusted) {
+ r = -APKE_SIGNATURE_UNTRUSTED;
+ goto err;
+ }
break;
}
r = cb(db, blk, apk_istream_from_blob(&is, b));
@@ -170,9 +169,8 @@ static int __adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expec
struct adb_block blk;
struct apk_segment_istream seg;
void *sig;
- int r = 0, trusted = t ? 0 : 1;
+ int r = 0, trusted = (t && t->allow_untrusted) ? 1 : 0;
uint32_t type, allowed = BIT(ADB_BLOCK_ADB);
- size_t sz;
if (IS_ERR(is)) return PTR_ERR(is);
@@ -190,32 +188,36 @@ static int __adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expec
}
do {
- r = apk_istream_read_max(is, &blk, sizeof blk);
- if (r != sizeof blk) break;
+ size_t hdrsize = sizeof blk;
+ void *hdrptr = apk_istream_peek(is, sizeof(blk.type_size));
+ if (!IS_ERR(hdrptr)) hdrsize = adb_block_hdrsize(hdrptr);
+ r = apk_istream_read_max(is, &blk, hdrsize);
+ if (r != hdrsize) break;
type = adb_block_type(&blk);
if (!(BIT(type) & allowed)) {
r = -APKE_ADB_BLOCK;
break;
}
- sz = adb_block_size(&blk) - sizeof blk;
+
+ uint64_t sz = adb_block_length(&blk);
switch (type) {
case ADB_BLOCK_ADB:
- allowed = BIT(ADB_BLOCK_SIG) | BIT(ADB_BLOCK_DATA) | BIT(ADB_BLOCK_DATAX);
+ allowed = BIT(ADB_BLOCK_SIG) | BIT(ADB_BLOCK_DATA);
db->adb.ptr = malloc(sz);
- db->adb.len = adb_block_length(&blk);
+ db->adb.len = sz;
if (db->adb.len < 16) {
r = -APKE_ADB_BLOCK;
- break;
+ goto err;
}
if ((r = apk_istream_read(is, db->adb.ptr, sz)) < 0) goto err;
if (((struct adb_hdr*)db->adb.ptr)->adb_compat_ver != 0) {
r = -APKE_ADB_VERSION;
- break;
+ goto err;
}
r = cb(db, &blk, apk_istream_from_blob(&seg.is, db->adb));
if (r < 0) goto err;
- continue;
+ goto skip_padding;
case ADB_BLOCK_SIG:
sig = apk_istream_peek(is, sz);
if (IS_ERR(sig)) {
@@ -223,31 +225,34 @@ static int __adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expec
goto err;
}
if (!trusted &&
- adb_trust_verify_signature(t, db, &vfy, APK_BLOB_PTR_LEN(sig, adb_block_length(&blk))) == 0)
+ adb_trust_verify_signature(t, db, &vfy, APK_BLOB_PTR_LEN(sig, sz)) == 0)
trusted = 1;
break;
case ADB_BLOCK_DATA:
- allowed = BIT(ADB_BLOCK_DATA) | BIT(ADB_BLOCK_DATAX);
- if (!trusted) goto err;
- break;
- case ADB_BLOCK_DATAX:
- r = -APKE_ADB_BLOCK;
+ allowed = BIT(ADB_BLOCK_DATA);
+ if (!trusted) {
+ r = -APKE_SIGNATURE_UNTRUSTED;
+ goto err;
+ }
break;
}
apk_istream_segment(&seg, is, sz, 0);
r = cb(db, &blk, &seg.is);
+ r = apk_istream_close_error(&seg.is, r);
if (r < 0) break;
- r = apk_istream_close(&seg.is);
+
+ skip_padding:
+ r = apk_istream_read(is, 0, adb_block_padding(&blk));
if (r < 0) break;
} while (1);
err:
if (r > 0) r = -APKE_ADB_BLOCK;
- if (r == 0) {
+ if (r == 0 || r == -ECANCELED) {
if (!trusted) r = -APKE_SIGNATURE_UNTRUSTED;
else if (!db->adb.ptr) r = -APKE_ADB_BLOCK;
}
- if (r != 0) {
+ if (r != 0 && r != -ECANCELED) {
free(db->adb.ptr);
db->adb = APK_BLOB_NULL;
}
@@ -346,9 +351,10 @@ adb_val_t adb_r_root(const struct adb *db)
return ((struct adb_hdr*)db->adb.ptr)->root;
}
-uint32_t adb_r_int(const struct adb *db, adb_val_t v)
+uint64_t adb_r_int(const struct adb *db, adb_val_t v)
{
uint32_t *int4;
+ uint64_t *int8;
switch (ADB_VAL_TYPE(v)) {
case ADB_TYPE_INT:
@@ -357,6 +363,10 @@ uint32_t adb_r_int(const struct adb *db, adb_val_t v)
int4 = adb_r_deref(db, v, 0, sizeof int4);
if (!int4) return 0;
return le32toh(*int4);
+ case ADB_TYPE_INT_64:
+ int8 = adb_r_deref(db, v, 0, sizeof int8);
+ if (!int8) return 0;
+ return le64toh(*int8);
default:
return 0;
}
@@ -437,7 +447,7 @@ adb_val_t adb_ro_val(const struct adb_obj *o, unsigned i)
return o->obj[i];
}
-uint32_t adb_ro_int(const struct adb_obj *o, unsigned i)
+uint64_t adb_ro_int(const struct adb_obj *o, unsigned i)
{
return adb_r_int(o->db, adb_ro_val(o, i));
}
@@ -456,78 +466,88 @@ struct adb_obj *adb_ro_obj(const struct adb_obj *o, unsigned i, struct adb_obj *
schema = container_of(o->schema->fields[0].kind, struct adb_object_schema, kind);
else if (i > 0 && i < o->schema->num_fields)
schema = container_of(o->schema->fields[i-1].kind, struct adb_object_schema, kind);
- assert(schema->kind == ADB_KIND_OBJECT || schema->kind == ADB_KIND_ARRAY);
+ assert(schema && (schema->kind == ADB_KIND_OBJECT || schema->kind == ADB_KIND_ARRAY));
}
return adb_r_obj(o->db, adb_ro_val(o, i), no, schema);
}
-int adb_ro_cmp(const struct adb_obj *o1, const struct adb_obj *o2, unsigned i)
+int adb_ro_cmpobj(const struct adb_obj *tmpl, const struct adb_obj *obj, unsigned mode)
{
- assert(o1->schema->kind == ADB_KIND_OBJECT);
- assert(o1->schema == o2->schema);
- assert(i > 0 && i < o1->schema->num_fields);
+ const struct adb_object_schema *schema = obj->schema;
+ int is_set, r = 0;
- switch (*o1->schema->fields[i-1].kind) {
- case ADB_KIND_BLOB:
- case ADB_KIND_INT:
- return container_of(o1->schema->fields[i-1].kind, struct adb_scalar_schema, kind)->compare(
- o1->db, adb_ro_val(o1, i),
- o2->db, adb_ro_val(o2, i));
- case ADB_KIND_OBJECT: {
- struct adb_obj so1, so2;
- adb_ro_obj(o1, i, &so1);
- adb_ro_obj(o2, i, &so2);
- return so1.schema->compare(&so1, &so2);
+ assert(schema->kind == ADB_KIND_OBJECT);
+ assert(schema == tmpl->schema);
+
+ for (int i = ADBI_FIRST; i < adb_ro_num(tmpl); i++) {
+ is_set = adb_ro_val(tmpl, i) != ADB_VAL_NULL;
+ if (mode == ADB_OBJCMP_EXACT || is_set) {
+ r = adb_ro_cmp(tmpl, obj, i, mode);
+ if (r) return r;
}
+ if (mode == ADB_OBJCMP_INDEX && !is_set)
+ return 0;
+ if (mode != ADB_OBJCMP_EXACT && i >= schema->num_compare)
+ return 0;
}
- assert(0);
+ return 0;
}
-static struct adb *__db1, *__db2;
-static const struct adb_object_schema *__schema;
-
-static int wacmp(const void *p1, const void *p2)
+int adb_ro_cmp(const struct adb_obj *tmpl, const struct adb_obj *obj, unsigned i, unsigned mode)
{
- struct adb_obj o1, o2;
- adb_r_obj(__db1, *(adb_val_t *)p1, &o1, __schema);
- adb_r_obj(__db2, *(adb_val_t *)p2, &o2, __schema);
- return o1.schema->compare(&o1, &o2);
-}
+ const struct adb_object_schema *schema = obj->schema;
-static int wadbcmp(const void *p1, const void *p2)
-{
- struct adb a1, a2;
- struct adb_obj o1, o2;
- adb_m_blob(&a1, adb_r_blob(__db1, *(adb_val_t *)p1), 0);
- adb_m_blob(&a2, adb_r_blob(__db2, *(adb_val_t *)p2), 0);
- adb_r_rootobj(&a1, &o1, __schema);
- adb_r_rootobj(&a2, &o2, __schema);
- return __schema->compare(&o1, &o2);
+ assert(schema->kind == ADB_KIND_OBJECT);
+ assert(schema == tmpl->schema);
+ assert(i > 0 && i < schema->num_fields);
+
+ switch (*schema->fields[i-1].kind) {
+ case ADB_KIND_BLOB:
+ case ADB_KIND_INT:
+ return container_of(schema->fields[i-1].kind, struct adb_scalar_schema, kind)->compare(
+ tmpl->db, adb_ro_val(tmpl, i),
+ obj->db, adb_ro_val(obj, i));
+ case ADB_KIND_OBJECT: {
+ struct adb_obj stmpl, sobj;
+ adb_ro_obj(tmpl, i, &stmpl);
+ adb_ro_obj(obj, i, &sobj);
+ return adb_ro_cmpobj(&stmpl, &sobj, mode);
+ }
+ }
+ assert(!"invalid object field kind");
}
-int adb_ra_find(struct adb_obj *arr, int cur, struct adb *db, adb_val_t val)
+int adb_ra_find(struct adb_obj *arr, int cur, struct adb_obj *tmpl)
{
- adb_val_t *ndx;
+ const struct adb_object_schema *schema = arr->schema, *item_schema;
+ struct adb_obj obj;
- __db1 = db;
- __db2 = arr->db;
- __schema = arr->schema;
- assert(__schema->kind == ADB_KIND_ARRAY);
- __schema = container_of(__schema->fields[0].kind, struct adb_object_schema, kind);
+ assert(schema->kind == ADB_KIND_ARRAY);
+ assert(*schema->fields[0].kind == ADB_KIND_OBJECT);
+ item_schema = container_of(schema->fields[0].kind, struct adb_object_schema, kind),
+ assert(item_schema == tmpl->schema);
if (cur == 0) {
- ndx = bsearch(&val, &arr->obj[ADBI_FIRST], adb_ra_num(arr), sizeof(arr->obj[0]), wacmp);
- if (!ndx) return -1;
- cur = ndx - arr->obj;
- while (cur > 1 && wacmp(&val, &arr->obj[cur-1]) == 0) cur--;
+ unsigned m, l = ADBI_FIRST, r = adb_ra_num(arr) + 1;
+ while (l < r) {
+ m = (l + r) / 2;
+ if (adb_ro_cmpobj(tmpl, adb_ro_obj(arr, m, &obj), ADB_OBJCMP_INDEX) < 0)
+ r = m;
+ else
+ l = m + 1;
+ }
+ cur = r - 1;
} else {
cur++;
- if (wacmp(&val, &arr->obj[cur]) != 0)
- return -1;
}
- return cur;
+ for (; cur <= adb_ra_num(arr); cur++) {
+ adb_ro_obj(arr, cur, &obj);
+ if (adb_ro_cmpobj(tmpl, &obj, ADB_OBJCMP_TEMPLATE) == 0) return cur;
+ if (adb_ro_cmpobj(tmpl, &obj, ADB_OBJCMP_INDEX) != 0) return -1;
+ }
+ return -1;
}
/* Write interface */
@@ -566,7 +586,7 @@ static unsigned iovec_memcmp(struct iovec *vec, size_t nvec, void *base)
static adb_val_t adb_w_error(struct adb *db, int rc)
{
- assert(0);
+ assert(!"adb error");
db->schema = 0;
return ADB_ERROR(rc);
}
@@ -629,43 +649,71 @@ void adb_w_rootobj(struct adb_obj *obj)
adb_w_root(obj->db, adb_w_obj(obj));
}
-adb_val_t adb_w_blob(struct adb *db, apk_blob_t b)
+adb_val_t adb_w_blob_vec(struct adb *db, uint32_t n, apk_blob_t *b)
{
union {
uint32_t u32;
uint16_t u16;
uint8_t u8;
} val;
- uint32_t n = b.len;
- struct iovec vec[2] = {
- { .iov_base = &val, .iov_len = sizeof val },
- { .iov_base = (void *) b.ptr, .iov_len = n },
- };
+ const int max_vec_size = 4;
+ struct iovec vec[1+max_vec_size];
adb_val_t o;
+ uint32_t i, align = 1;
+
+ assert(n <= max_vec_size);
- if (n > 0xffff) {
- val.u32 = htole32(n);
- vec[0].iov_len = sizeof val.u32;
+ vec[0] = (struct iovec) { .iov_base = &val, .iov_len = sizeof val };
+ for (i = 0; i < n; i++)
+ vec[i+1] = (struct iovec) { .iov_base = b[i].ptr, .iov_len = b[i].len };
+
+ size_t sz = iovec_len(&vec[1], n);
+ if (sz > 0xffff) {
+ val.u32 = htole32(sz);
+ vec[0].iov_len = align = sizeof val.u32;
o = ADB_TYPE_BLOB_32;
- } else if (n > 0xff) {
- val.u16 = htole16(n);
- vec[0].iov_len = sizeof val.u16;
+ } else if (sz > 0xff) {
+ val.u16 = htole16(sz);
+ vec[0].iov_len = align = sizeof val.u16;
o = ADB_TYPE_BLOB_16;
- } else if (n > 0) {
- val.u8 = n;
- vec[0].iov_len = sizeof val.u8;
+ } else if (sz > 0) {
+ val.u8 = sz;
+ vec[0].iov_len = align = sizeof val.u8;
o = ADB_TYPE_BLOB_8;
} else {
return ADB_VAL_NULL;
}
- return ADB_VAL(o, adb_w_data(db, vec, ARRAY_SIZE(vec), vec[0].iov_len));
+ return ADB_VAL(o, adb_w_data(db, vec, n+1, align));
}
-adb_val_t adb_w_int(struct adb *db, uint32_t val)
+adb_val_t adb_w_blob(struct adb *db, apk_blob_t b)
{
- if (val >= 0x10000000)
- return ADB_VAL(ADB_TYPE_INT_32, adb_w_data1(db, &val, sizeof val, sizeof val));
+ return adb_w_blob_vec(db, 1, &b);
+}
+
+static adb_val_t adb_w_blob_raw(struct adb *db, apk_blob_t b)
+{
+ adb_val_t val;
+ size_t num_buckets;
+
+ num_buckets = db->num_buckets;
+ db->num_buckets = 0;
+ val = adb_w_blob(db, b);
+ db->num_buckets = num_buckets;
+ return val;
+}
+
+adb_val_t adb_w_int(struct adb *db, uint64_t val)
+{
+ if (val >= 0x100000000) {
+ val = htole64(val);
+ return ADB_VAL(ADB_TYPE_INT_64, adb_w_data1(db, &val, sizeof val, sizeof val));
+ }
+ if (val >= 0x10000000) {
+ uint32_t val32 = htole32(val);
+ return ADB_VAL(ADB_TYPE_INT_32, adb_w_data1(db, &val32, sizeof val32, sizeof val32));
+ }
return ADB_VAL(ADB_TYPE_INT, val);
}
@@ -683,6 +731,9 @@ adb_val_t adb_w_copy(struct adb *db, struct adb *srcdb, adb_val_t v)
case ADB_TYPE_INT_32:
sz = align = sizeof(uint32_t);
goto copy;
+ case ADB_TYPE_INT_64:
+ sz = align = sizeof(uint64_t);
+ goto copy;
case ADB_TYPE_BLOB_8:
ptr = adb_r_deref(srcdb, v, 0, 1);
sz = 1UL + *(uint8_t*) ptr;
@@ -702,7 +753,6 @@ adb_val_t adb_w_copy(struct adb *db, struct adb *srcdb, adb_val_t v)
for (int i = ADBI_FIRST; i < sz; i++) cpy[i] = adb_w_copy(db, srcdb, adb_ro_val(&obj, i));
return ADB_VAL(ADB_VAL_TYPE(v), adb_w_data1(db, cpy, sizeof(adb_val_t[sz]), sizeof(adb_val_t)));
}
- case ADB_TYPE_INT_64:
case ADB_TYPE_BLOB_32:
default:
return adb_w_error(db, ENOSYS);
@@ -718,7 +768,7 @@ adb_val_t adb_w_adb(struct adb *db, struct adb *valdb)
struct adb_block blk = adb_block_init(ADB_BLOCK_ADB, valdb->adb.len);
struct iovec vec[] = {
{ .iov_base = &bsz, .iov_len = sizeof bsz },
- { .iov_base = &blk, .iov_len = sizeof blk },
+ { .iov_base = &blk, .iov_len = adb_block_hdrsize(&blk) },
{ .iov_base = valdb->adb.ptr, .iov_len = valdb->adb.len },
{ .iov_base = padding_zeroes, .iov_len = adb_block_padding(&blk) },
};
@@ -742,7 +792,7 @@ adb_val_t adb_w_fromstring(struct adb *db, const uint8_t *kind, apk_blob_t val)
adb_wo_alloca(&obj, schema, db);
if (!schema->fromstring) return ADB_ERROR(APKE_ADB_NO_FROMSTRING);
r = schema->fromstring(&obj, val);
- if (r) return ADB_ERROR(r);
+ if (r) return ADB_ERROR(-r);
return adb_w_obj(&obj);
}
default:
@@ -785,6 +835,12 @@ struct adb_obj *adb_wo_init_val(struct adb_obj *o, adb_val_t *p, const struct ad
return adb_wo_init(o, p, schema, parent->db);
}
+void adb_wo_free(struct adb_obj *o)
+{
+ if (o->dynamic) free(o->obj);
+ o->obj = 0;
+}
+
void adb_wo_reset(struct adb_obj *o)
{
uint32_t max = o->obj[ADBI_NUM_ENTRIES];
@@ -827,12 +883,26 @@ adb_val_t adb_w_arr(struct adb_obj *o)
return __adb_w_obj(o, ADB_TYPE_ARRAY);
}
-adb_val_t adb_wo_fromstring(struct adb_obj *o, apk_blob_t val)
+int adb_wo_fromstring(struct adb_obj *o, apk_blob_t val)
{
adb_wo_reset(o);
return o->schema->fromstring(o, val);
}
+int adb_wo_copyobj(struct adb_obj *o, struct adb_obj *src)
+{
+ size_t sz = adb_ro_num(src);
+
+ assert(o->schema->kind == ADB_KIND_OBJECT);
+ assert(o->schema == src->schema);
+
+ adb_wo_reset(o);
+ for (unsigned i = ADBI_FIRST; i < sz; i++)
+ adb_wo_val(o, i, adb_w_copy(o->db, src->db, adb_ro_val(src, i)));
+
+ return 0;
+}
+
adb_val_t adb_wo_val(struct adb_obj *o, unsigned i, adb_val_t v)
{
if (i >= o->obj[ADBI_NUM_ENTRIES]) return adb_w_error(o->db, E2BIG);
@@ -848,7 +918,7 @@ adb_val_t adb_wo_val_fromstring(struct adb_obj *o, unsigned i, apk_blob_t val)
return o->obj[i] = adb_w_fromstring(o->db, o->schema->fields[i-1].kind, val);
}
-adb_val_t adb_wo_int(struct adb_obj *o, unsigned i, uint32_t v)
+adb_val_t adb_wo_int(struct adb_obj *o, unsigned i, uint64_t v)
{
return adb_wo_val(o, i, adb_w_int(o->db, v));
}
@@ -859,6 +929,12 @@ adb_val_t adb_wo_blob(struct adb_obj *o, unsigned i, apk_blob_t b)
return adb_wo_val(o, i, adb_w_blob(o->db, b));
}
+adb_val_t adb_wo_blob_raw(struct adb_obj *o, unsigned i, apk_blob_t b)
+{
+ assert(o->schema->kind == ADB_KIND_OBJECT);
+ return adb_wo_val(o, i, adb_w_blob_raw(o->db, b));
+}
+
adb_val_t adb_wo_obj(struct adb_obj *o, unsigned i, struct adb_obj *no)
{
assert(o->schema->kind == ADB_KIND_OBJECT);
@@ -876,9 +952,20 @@ adb_val_t adb_wo_arr(struct adb_obj *o, unsigned i, struct adb_obj *no)
adb_val_t adb_wa_append(struct adb_obj *o, adb_val_t v)
{
assert(o->schema->kind == ADB_KIND_ARRAY);
- if (o->num >= o->obj[ADBI_NUM_ENTRIES]) return adb_w_error(o->db, E2BIG);
if (ADB_IS_ERROR(v)) return adb_w_error(o->db, ADB_VAL_VALUE(v));
- if (v != ADB_VAL_NULL) o->obj[o->num++] = v;
+ if (v == ADB_VAL_NULL) return v;
+
+ if (o->num >= o->obj[ADBI_NUM_ENTRIES]) {
+ int num = o->obj[ADBI_NUM_ENTRIES];
+ adb_val_t *obj = reallocarray(o->dynamic ? o->obj : NULL, num * 2, sizeof(adb_val_t));
+ if (!obj) return adb_w_error(o->db, ENOMEM);
+ if (!o->dynamic) memcpy(obj, o->obj, sizeof(adb_val_t) * num);
+ memset(&obj[num], 0, sizeof(adb_val_t) * num);
+ o->obj = obj;
+ o->obj[ADBI_NUM_ENTRIES] = num * 2;
+ o->dynamic = 1;
+ }
+ o->obj[o->num++] = v;
return v;
}
@@ -895,18 +982,65 @@ adb_val_t adb_wa_append_fromstring(struct adb_obj *o, apk_blob_t b)
return adb_wa_append(o, adb_w_fromstring(o->db, o->schema->fields[0].kind, b));
}
+struct wacmp_param {
+ struct adb *db1, *db2;
+ union {
+ const struct adb_object_schema *schema;
+ int (*compare)(struct adb *db1, adb_val_t v1, struct adb *db2, adb_val_t v2);
+ };
+ int mode;
+};
+
+static int wacmpscalar(const void *p1, const void *p2, void *arg)
+{
+ struct wacmp_param *wp = arg;
+ return wp->compare(wp->db1, *(adb_val_t *)p1, wp->db2, *(adb_val_t *)p2);
+}
+
+static int wacmp(const void *p1, const void *p2, void *arg)
+{
+ struct wacmp_param *wp = arg;
+ struct adb_obj o1, o2;
+ adb_r_obj(wp->db1, *(adb_val_t *)p1, &o1, wp->schema);
+ adb_r_obj(wp->db2, *(adb_val_t *)p2, &o2, wp->schema);
+ return adb_ro_cmpobj(&o1, &o2, wp->mode);
+}
+
+static int wadbcmp(const void *p1, const void *p2, void *arg)
+{
+ struct wacmp_param *wp = arg;
+ struct adb a1, a2;
+ struct adb_obj o1, o2;
+ adb_m_blob(&a1, adb_r_blob(wp->db1, *(adb_val_t *)p1), 0);
+ adb_m_blob(&a2, adb_r_blob(wp->db2, *(adb_val_t *)p2), 0);
+ adb_r_rootobj(&a1, &o1, wp->schema);
+ adb_r_rootobj(&a2, &o2, wp->schema);
+ return adb_ro_cmpobj(&o1, &o2, wp->mode);
+}
+
void adb_wa_sort(struct adb_obj *arr)
{
- assert(arr->schema->kind == ADB_KIND_ARRAY);
- __db1 = __db2 = arr->db;
+ const struct adb_object_schema *schema = arr->schema;
+ struct wacmp_param arg = {
+ .db1 = arr->db,
+ .db2 = arr->db,
+ .mode = ADB_OBJCMP_EXACT,
+ };
+
+ assert(schema->kind == ADB_KIND_ARRAY);
+
switch (*arr->schema->fields[0].kind) {
+ case ADB_KIND_BLOB:
+ arg.compare = container_of(arr->schema->fields[0].kind, struct adb_scalar_schema, kind)->compare;
+ qsort_r(&arr->obj[ADBI_FIRST], adb_ra_num(arr), sizeof(arr->obj[0]), wacmpscalar, &arg);
+ break;
case ADB_KIND_OBJECT:
- __schema = container_of(arr->schema->fields[0].kind, struct adb_object_schema, kind);
- qsort(&arr->obj[ADBI_FIRST], adb_ra_num(arr), sizeof(arr->obj[0]), wacmp);
+ arg.schema = container_of(arr->schema->fields[0].kind, struct adb_object_schema, kind);
+ qsort_r(&arr->obj[ADBI_FIRST], adb_ra_num(arr), sizeof(arr->obj[0]), wacmp, &arg);
break;
case ADB_KIND_ADB:
- __schema = container_of(arr->schema->fields[0].kind, struct adb_adb_schema, kind)->schema;
- qsort(&arr->obj[ADBI_FIRST], adb_ra_num(arr), sizeof(arr->obj[0]), wadbcmp);
+ arg.schema = container_of(arr->schema->fields[0].kind, struct adb_adb_schema, kind)->schema;
+ qsort_r(&arr->obj[ADBI_FIRST], adb_ra_num(arr), sizeof(arr->obj[0]), wadbcmp, &arg);
break;
default:
assert(1);
@@ -949,7 +1083,7 @@ int adb_s_field_by_name(const struct adb_object_schema *schema, const char *name
int adb_c_header(struct apk_ostream *os, struct adb *db)
{
struct adb_file_header hdr = {
- .magic = ADB_FORMAT_MAGIC,
+ .magic = htole32(ADB_FORMAT_MAGIC),
.schema = htole32(db->schema),
};
return apk_ostream_write(os, &hdr, sizeof hdr);
@@ -961,9 +1095,7 @@ int adb_c_block(struct apk_ostream *os, uint32_t type, apk_blob_t val)
size_t padding = adb_block_padding(&blk);
int r;
- if (val.len & ~0x3fffffff) return -APKE_ADB_LIMIT;
-
- r = apk_ostream_write(os, &blk, sizeof blk);
+ r = apk_ostream_write(os, &blk, adb_block_hdrsize(&blk));
if (r < 0) return r;
r = apk_ostream_write(os, val.ptr, val.len);
@@ -977,7 +1109,7 @@ int adb_c_block(struct apk_ostream *os, uint32_t type, apk_blob_t val)
return 0;
}
-int adb_c_block_data(struct apk_ostream *os, apk_blob_t hdr, uint32_t size, struct apk_istream *is)
+int adb_c_block_data(struct apk_ostream *os, apk_blob_t hdr, uint64_t size, struct apk_istream *is)
{
struct adb_block blk = adb_block_init(ADB_BLOCK_DATA, size + hdr.len);
size_t padding = adb_block_padding(&blk);
@@ -986,7 +1118,7 @@ int adb_c_block_data(struct apk_ostream *os, apk_blob_t hdr, uint32_t size, stru
if (IS_ERR(os)) return PTR_ERR(os);
if (IS_ERR(is)) return apk_ostream_cancel(os, PTR_ERR(is));
- r = apk_ostream_write(os, &blk, sizeof blk);
+ r = apk_ostream_write(os, &blk, adb_block_hdrsize(&blk));
if (r < 0) return r;
r = apk_ostream_write(os, hdr.ptr, hdr.len);
@@ -1005,7 +1137,7 @@ int adb_c_block_data(struct apk_ostream *os, apk_blob_t hdr, uint32_t size, stru
int adb_c_block_copy(struct apk_ostream *os, struct adb_block *b, struct apk_istream *is, struct adb_verify_ctx *vfy)
{
- size_t blk_sz = adb_block_length(b);
+ uint64_t blk_sz = adb_block_length(b);
size_t padding = adb_block_padding(b);
int r;
@@ -1080,6 +1212,8 @@ static int adb_digest_v0_signature(struct apk_digest_ctx *dctx, uint32_t schema,
{
int r;
+ /* it is imporant to normalize this before including it in the digest */
+ schema = htole32(schema);
if ((r = apk_digest_ctx_update(dctx, &schema, sizeof schema)) != 0 ||
(r = apk_digest_ctx_update(dctx, sig0, sizeof *sig0)) != 0 ||
(r = apk_digest_ctx_update(dctx, md.ptr, md.len)) != 0)
@@ -1099,8 +1233,6 @@ int adb_trust_write_signatures(struct apk_trust *trust, struct adb *db, struct a
size_t siglen;
int r;
- if (IS_ERR_OR_NULL(trust)) return PTR_ERR(trust);
-
if (!vfy) {
vfy = alloca(sizeof *vfy);
memset(vfy, 0, sizeof *vfy);
@@ -1118,7 +1250,7 @@ int adb_trust_write_signatures(struct apk_trust *trust, struct adb *db, struct a
siglen = sizeof sig.buf - sizeof sig.v0;
- if ((r = apk_sign_start(&trust->dctx, &tkey->key)) != 0 ||
+ if ((r = apk_sign_start(&trust->dctx, APK_DIGEST_SHA512, &tkey->key)) != 0 ||
(r = adb_digest_v0_signature(&trust->dctx, db->schema, &sig.v0, md)) != 0 ||
(r = apk_sign(&trust->dctx, sig.v0.sig, &siglen)) != 0)
goto err;
@@ -1150,7 +1282,7 @@ int adb_trust_verify_signature(struct apk_trust *trust, struct adb *db, struct a
if (memcmp(sig0->id, tkey->key.id, sizeof sig0->id) != 0) continue;
if (adb_digest_adb(vfy, sig->hash_alg, db->adb, &md) != 0) continue;
- if (apk_verify_start(&trust->dctx, &tkey->key) != 0 ||
+ if (apk_verify_start(&trust->dctx, APK_DIGEST_SHA512, &tkey->key) != 0 ||
adb_digest_v0_signature(&trust->dctx, db->schema, sig0, md) != 0 ||
apk_verify(&trust->dctx, sig0->sig, sigb.len - sizeof *sig0) != 0)
continue;
diff --git a/src/adb.h b/src/adb.h
index e97068a..f979dde 100644
--- a/src/adb.h
+++ b/src/adb.h
@@ -44,6 +44,7 @@ typedef uint32_t adb_val_t;
/* File Header */
#define ADB_FORMAT_MAGIC 0x2e424441 // ADB.
+#define ADB_SCHEMA_ANY 0
#define ADB_SCHEMA_IMPLIED 0x80000000
struct adb_file_header {
@@ -56,21 +57,41 @@ struct adb_file_header {
#define ADB_BLOCK_ADB 0
#define ADB_BLOCK_SIG 1
#define ADB_BLOCK_DATA 2
-#define ADB_BLOCK_DATAX 3
+#define ADB_BLOCK_EXT 3
struct adb_block {
uint32_t type_size;
+ uint32_t reserved;
+ uint64_t x_size;
};
-static inline struct adb_block adb_block_init(uint32_t type, uint32_t length) {
- return (struct adb_block) { .type_size = htole32((type << 30) + sizeof(struct adb_block) + length)};
+static inline struct adb_block adb_block_init(uint32_t type, uint64_t length) {
+ if (length <= 0x3fffffff) {
+ return (struct adb_block) {
+ .type_size = htole32((type << 30) + sizeof(uint32_t) + length),
+ };
+ }
+ return (struct adb_block) {
+ .type_size = htole32((ADB_BLOCK_EXT << 30) + type),
+ .x_size = htole64(sizeof(struct adb_block) + length),
+ };
}
-static inline uint32_t adb_block_type(struct adb_block *b) { return le32toh((b)->type_size) >> 30; }
-static inline uint32_t adb_block_rawsize(struct adb_block *b) { return le32toh((b)->type_size) & 0x3fffffff; }
-static inline uint32_t adb_block_size(struct adb_block *b) { return ROUND_UP(adb_block_rawsize(b), ADB_BLOCK_ALIGNMENT); }
-static inline uint32_t adb_block_length(struct adb_block *b) { return adb_block_rawsize(b) - sizeof(struct adb_block); }
+static inline uint32_t adb_block_is_ext(struct adb_block *b) {
+ return (le32toh((b)->type_size) >> 30) == ADB_BLOCK_EXT;
+}
+static inline uint32_t adb_block_type(struct adb_block *b) {
+ return adb_block_is_ext(b) ? (le32toh(b->type_size) & 0x3fffffff) : (le32toh(b->type_size) >> 30);
+}
+static inline uint64_t adb_block_rawsize(struct adb_block *b) {
+ return adb_block_is_ext(b) ? le64toh(b->x_size) : (le32toh(b->type_size) & 0x3fffffff);
+}
+static inline uint32_t adb_block_hdrsize(struct adb_block *b) {
+ return adb_block_is_ext(b) ? sizeof *b : sizeof b->type_size;
+}
+static inline uint64_t adb_block_size(struct adb_block *b) { return ROUND_UP(adb_block_rawsize(b), ADB_BLOCK_ALIGNMENT); }
+static inline uint64_t adb_block_length(struct adb_block *b) { return adb_block_rawsize(b) - adb_block_hdrsize(b); }
static inline uint32_t adb_block_padding(struct adb_block *b) { return adb_block_size(b) - adb_block_rawsize(b); }
-static inline void *adb_block_payload(struct adb_block *b) { return b + 1; }
+static inline void *adb_block_payload(struct adb_block *b) { return (char*)b + adb_block_hdrsize(b); }
static inline apk_blob_t adb_block_blob(struct adb_block *b) {
return APK_BLOB_PTR_LEN(adb_block_payload(b), adb_block_length(b));
}
@@ -101,22 +122,28 @@ struct adb_sign_v0 {
#define ADB_KIND_BLOB 4
#define ADB_KIND_INT 5
-#define ADB_ARRAY_ITEM(_t) { { .kind = &(_t).kind } }
+#define ADB_ARRAY_ITEM(_t) (const struct adb_object_schema_field[1]) { {.kind = &(_t).kind} }
+#define ADB_OBJECT_FIELDS(n) (const struct adb_object_schema_field[n])
#define ADB_FIELD(_i, _n, _t) [(_i)-1] = { .name = _n, .kind = &(_t).kind }
+#define ADB_OBJCMP_EXACT 0 // match all fields
+#define ADB_OBJCMP_TEMPLATE 1 // match fields set on template
+#define ADB_OBJCMP_INDEX 2 // match fields until first non-set one
+
+struct adb_object_schema_field {
+ const char *name;
+ const uint8_t *kind;
+};
+
struct adb_object_schema {
uint8_t kind;
uint16_t num_fields;
+ uint16_t num_compare;
apk_blob_t (*tostring)(struct adb_obj *, char *, size_t);
int (*fromstring)(struct adb_obj *, apk_blob_t);
- int (*compare)(const struct adb_obj *, const struct adb_obj *);
void (*pre_commit)(struct adb_obj *);
-
- struct {
- const char *name;
- const uint8_t *kind;
- } fields[];
+ const struct adb_object_schema_field *fields;
};
struct adb_scalar_schema {
@@ -156,8 +183,9 @@ struct adb {
struct adb_obj {
struct adb *db;
const struct adb_object_schema *schema;
- uint32_t num;
adb_val_t *obj;
+ uint32_t num;
+ uint32_t dynamic : 1;
};
/* Container read interface */
@@ -178,7 +206,7 @@ int adb_w_init_static(struct adb *db, void *buf, size_t bufsz);
/* Primitive read */
adb_val_t adb_r_root(const struct adb *);
struct adb_obj *adb_r_rootobj(struct adb *a, struct adb_obj *o, const struct adb_object_schema *);
-uint32_t adb_r_int(const struct adb *, adb_val_t);
+uint64_t adb_r_int(const struct adb *, adb_val_t);
apk_blob_t adb_r_blob(const struct adb *, adb_val_t);
struct adb_obj *adb_r_obj(struct adb *, adb_val_t, struct adb_obj *o, const struct adb_object_schema *);
@@ -188,17 +216,19 @@ static inline uint32_t adb_ra_num(const struct adb_obj *o) { return (o->num ?: 1
const uint8_t *adb_ro_kind(const struct adb_obj *o, unsigned i);
adb_val_t adb_ro_val(const struct adb_obj *o, unsigned i);
-uint32_t adb_ro_int(const struct adb_obj *o, unsigned i);
+uint64_t adb_ro_int(const struct adb_obj *o, unsigned i);
apk_blob_t adb_ro_blob(const struct adb_obj *o, unsigned i);
struct adb_obj *adb_ro_obj(const struct adb_obj *o, unsigned i, struct adb_obj *);
-int adb_ro_cmp(const struct adb_obj *o1, const struct adb_obj *o2, unsigned i);
-int adb_ra_find(struct adb_obj *arr, int cur, struct adb *db, adb_val_t val);
+int adb_ro_cmpobj(const struct adb_obj *o1, const struct adb_obj *o2, unsigned mode);
+int adb_ro_cmp(const struct adb_obj *o1, const struct adb_obj *o2, unsigned i, unsigned mode);
+int adb_ra_find(struct adb_obj *arr, int cur, struct adb_obj *tmpl);
/* Primitive write */
void adb_w_root(struct adb *, adb_val_t);
void adb_w_rootobj(struct adb_obj *);
+adb_val_t adb_w_blob_vec(struct adb *, uint32_t, apk_blob_t *);
adb_val_t adb_w_blob(struct adb *, apk_blob_t);
-adb_val_t adb_w_int(struct adb *, uint32_t);
+adb_val_t adb_w_int(struct adb *, uint64_t);
adb_val_t adb_w_copy(struct adb *, struct adb *, adb_val_t);
adb_val_t adb_w_adb(struct adb *, struct adb *);
adb_val_t adb_w_fromstring(struct adb *, const uint8_t *kind, apk_blob_t);
@@ -208,15 +238,18 @@ adb_val_t adb_w_fromstring(struct adb *, const uint8_t *kind, apk_blob_t);
struct adb_obj *adb_wo_init(struct adb_obj *, adb_val_t *, const struct adb_object_schema *, struct adb *);
struct adb_obj *adb_wo_init_val(struct adb_obj *, adb_val_t *, const struct adb_obj *, unsigned i);
+void adb_wo_free(struct adb_obj *);
void adb_wo_reset(struct adb_obj *);
void adb_wo_resetdb(struct adb_obj *);
adb_val_t adb_w_obj(struct adb_obj *);
adb_val_t adb_w_arr(struct adb_obj *);
-adb_val_t adb_wo_fromstring(struct adb_obj *o, apk_blob_t);
+int adb_wo_fromstring(struct adb_obj *o, apk_blob_t);
+int adb_wo_copyobj(struct adb_obj *o, struct adb_obj *);
adb_val_t adb_wo_val(struct adb_obj *o, unsigned i, adb_val_t);
adb_val_t adb_wo_val_fromstring(struct adb_obj *o, unsigned i, apk_blob_t);
-adb_val_t adb_wo_int(struct adb_obj *o, unsigned i, uint32_t);
+adb_val_t adb_wo_int(struct adb_obj *o, unsigned i, uint64_t);
adb_val_t adb_wo_blob(struct adb_obj *o, unsigned i, apk_blob_t);
+adb_val_t adb_wo_blob_raw(struct adb_obj *o, unsigned i, apk_blob_t);
adb_val_t adb_wo_obj(struct adb_obj *o, unsigned i, struct adb_obj *);
adb_val_t adb_wo_arr(struct adb_obj *o, unsigned i, struct adb_obj *);
adb_val_t adb_wa_append(struct adb_obj *o, adb_val_t);
@@ -232,7 +265,7 @@ int adb_s_field_by_name(const struct adb_object_schema *, const char *);
/* Creation */
int adb_c_header(struct apk_ostream *os, struct adb *db);
int adb_c_block(struct apk_ostream *os, uint32_t type, apk_blob_t);
-int adb_c_block_data(struct apk_ostream *os, apk_blob_t hdr, uint32_t size, struct apk_istream *is);
+int adb_c_block_data(struct apk_ostream *os, apk_blob_t hdr, uint64_t size, struct apk_istream *is);
int adb_c_block_copy(struct apk_ostream *os, struct adb_block *b, struct apk_istream *is, struct adb_verify_ctx *);
int adb_c_adb(struct apk_ostream *os, struct adb *db, struct apk_trust *t);
int adb_c_create(struct apk_ostream *os, struct adb *db, struct apk_trust *t);
@@ -275,8 +308,8 @@ struct adb_walk_gentext {
struct adb_walk d;
FILE *out;
int nest;
- int line_started : 1;
- int key_printed : 1;
+ unsigned int line_started : 1;
+ unsigned int key_printed : 1;
};
#define ADB_WALK_GENADB_MAX_IDB 2
@@ -299,12 +332,20 @@ int adb_walk_text(struct adb_walk *d, struct apk_istream *is);
// Seamless compression support
-typedef unsigned int adb_comp_t;
+struct adb_compression_spec {
+ uint8_t alg;
+ uint8_t level;
+};
-#define ADB_COMP_NONE 0
-#define ADB_COMP_DEFLATE 1
+// Internally, "none" compression is treated specially:
+// none/0 means "default compression"
+// none/1 is "no compression"
+#define ADB_COMP_NONE 0x00
+#define ADB_COMP_DEFLATE 0x01
+#define ADB_COMP_ZSTD 0x02
-struct apk_istream *adb_decompress(struct apk_istream *is, adb_comp_t *compression);
-struct apk_ostream *adb_compress(struct apk_ostream *os, adb_comp_t compression);
+int adb_parse_compression(const char *spec_string, struct adb_compression_spec *spec);
+struct apk_istream *adb_decompress(struct apk_istream *is, struct adb_compression_spec *spec);
+struct apk_ostream *adb_compress(struct apk_ostream *os, struct adb_compression_spec *spec);
#endif
diff --git a/src/adb_comp.c b/src/adb_comp.c
index 26fb50f..2f92924 100644
--- a/src/adb_comp.c
+++ b/src/adb_comp.c
@@ -9,40 +9,142 @@
#include "apk_defines.h"
#include "adb.h"
-struct apk_istream *adb_decompress(struct apk_istream *is, adb_comp_t *compression)
+struct compression_info {
+ const char *name;
+ uint8_t min_level, max_level;
+ struct apk_ostream *(*compress)(struct apk_ostream *, uint8_t);
+ struct apk_istream *(*decompress)(struct apk_istream *);
+};
+
+static const struct compression_info compression_infos[] = {
+ [ADB_COMP_NONE] = {
+ .name = "none",
+ },
+ [ADB_COMP_DEFLATE] = {
+ .name = "deflate",
+ .compress = apk_ostream_deflate,
+ .decompress = apk_istream_deflate,
+ .min_level = 0, .max_level = 9,
+ },
+#ifdef HAVE_ZSTD
+ [ADB_COMP_ZSTD] = {
+ .name = "zstd",
+ .compress = apk_ostream_zstd,
+ .decompress = apk_istream_zstd,
+ .min_level = 0, .max_level = 22,
+ },
+#endif
+};
+
+static const struct compression_info *compression_info_by_name(const char *name, size_t n, uint8_t *compalg)
+{
+ for (int i = 0; i < ARRAY_SIZE(compression_infos); i++) {
+ const struct compression_info *ci = &compression_infos[i];
+ if (strlen(ci->name) == n && strncmp(name, ci->name, n) == 0) {
+ *compalg = i;
+ return ci;
+ }
+ }
+ return NULL;
+}
+
+static const struct compression_info *compression_info_by_alg(uint8_t alg)
+{
+ if (alg >= ARRAY_SIZE(compression_infos)) return NULL;
+ return &compression_infos[alg];
+}
+
+int adb_parse_compression(const char *spec_string, struct adb_compression_spec *spec)
+{
+ const struct compression_info *ci;
+ const char *delim = strchrnul(spec_string, ':');
+ char *end;
+ long level = 0;
+
+ ci = compression_info_by_name(spec_string, delim - spec_string, &spec->alg);
+ if (!ci) goto err;
+ if (*delim != 0) {
+ if (delim[1] == 0) goto err;
+ if (ci->max_level == 0) goto err;
+
+ level = strtol(delim+1, &end, 0);
+ if (*end != 0) goto err;
+ if (level < ci->min_level || level > ci->max_level) goto err;
+ }
+ if (spec->alg == ADB_COMP_NONE) level = 1;
+ spec->level = level;
+ return 0;
+err:
+ *spec = (struct adb_compression_spec) { .alg = ADB_COMP_NONE };
+ return -APKE_ADB_COMPRESSION;
+}
+
+struct apk_istream *adb_decompress(struct apk_istream *is, struct adb_compression_spec *retspec)
{
- adb_comp_t c = -1;
+ struct adb_compression_spec spec = { .alg = ADB_COMP_NONE };
- if (IS_ERR_OR_NULL(is)) return is;
+ if (IS_ERR(is)) return is;
uint8_t *buf = apk_istream_peek(is, 4);
if (IS_ERR(buf)) return ERR_PTR(apk_istream_close_error(is, PTR_ERR(buf)));
if (memcmp(buf, "ADB", 3) != 0) return ERR_PTR(apk_istream_close_error(is, -APKE_ADB_HEADER));
switch (buf[3]) {
case '.':
- c = ADB_COMP_NONE;
+ spec.alg = ADB_COMP_NONE;
+ spec.level = 1;
break;
case 'd':
- c = ADB_COMP_DEFLATE;
apk_istream_get(is, 4);
- is = apk_istream_deflate(is);
+ spec.alg = ADB_COMP_DEFLATE;
+ break;
+ case 'c':
+ apk_istream_get(is, 4);
+ apk_istream_read(is, &spec, sizeof spec);
break;
+ default:
+ goto err;
}
- if (c == -1) return ERR_PTR(apk_istream_close_error(is, -APKE_ADB_COMPRESSION));
- if (compression) *compression = c;
+
+ const struct compression_info *ci = compression_info_by_alg(spec.alg);
+ if (!ci) goto err;
+
+ if (spec.alg != ADB_COMP_NONE)
+ is = ci->decompress(is);
+
+ if (retspec) *retspec = spec;
+
return is;
+err:
+ return ERR_PTR(apk_istream_close_error(is, -APKE_ADB_COMPRESSION));
}
-struct apk_ostream *adb_compress(struct apk_ostream *os, adb_comp_t compression)
+struct apk_ostream *adb_compress(struct apk_ostream *os, struct adb_compression_spec *spec)
{
- if (IS_ERR_OR_NULL(os)) return os;
- switch (compression) {
+ const struct compression_info *ci;
+
+ if (IS_ERR(os)) return os;
+ if (spec->alg == ADB_COMP_NONE && spec->level == 0) {
+ *spec = (struct adb_compression_spec) {
+ .alg = ADB_COMP_DEFLATE,
+ };
+ }
+ ci = compression_info_by_alg(spec->alg);
+ if (!ci) goto err;
+ if (spec->level < ci->min_level || spec->level > ci->max_level) goto err;
+
+ switch (spec->alg) {
case ADB_COMP_NONE:
return os;
case ADB_COMP_DEFLATE:
+ if (spec->level != 0) break;
if (apk_ostream_write(os, "ADBd", 4) < 0) goto err;
- return apk_ostream_deflate(os);
+ return apk_ostream_deflate(os, 0);
}
+
+ if (apk_ostream_write(os, "ADBc", 4) < 0) goto err;
+ if (apk_ostream_write(os, spec, sizeof *spec) < 0) goto err;
+ return ci->compress(os, spec->level);
+
err:
apk_ostream_cancel(os, -APKE_ADB_COMPRESSION);
return ERR_PTR(apk_ostream_close(os));
diff --git a/src/adb_walk_adb.c b/src/adb_walk_adb.c
index 9a74e7e..1ccb14f 100644
--- a/src/adb_walk_adb.c
+++ b/src/adb_walk_adb.c
@@ -2,6 +2,7 @@
#include <stdio.h>
#include <unistd.h>
+#include <inttypes.h>
#include "apk_adb.h"
#include "apk_applet.h"
#include "apk_print.h"
@@ -110,14 +111,14 @@ static int adb_walk_block(struct adb *db, struct adb_block *b, struct apk_istrea
{
struct adb_walk_ctx *ctx = container_of(db, struct adb_walk_ctx, db);
struct adb_walk *d = ctx->d;
- char tmp[16+ADB_MAX_SIGNATURE_LEN*2];
+ char tmp[160];
struct adb_hdr *hdr;
struct adb_sign_hdr *s;
uint32_t schema_magic = ctx->db.schema;
const struct adb_db_schema *ds;
- int r, len;
- size_t sz = adb_block_length(b);
- apk_blob_t data;
+ uint64_t sz = adb_block_length(b);
+ apk_blob_t data, c = APK_BLOB_BUF(tmp);
+ int r;
switch (adb_block_type(b)) {
case ADB_BLOCK_ADB:
@@ -126,35 +127,37 @@ static int adb_walk_block(struct adb *db, struct adb_block *b, struct apk_istrea
if (ds->magic == schema_magic) break;
hdr = apk_istream_peek(is, sizeof *hdr);
if (IS_ERR(hdr)) return PTR_ERR(hdr);
- len = snprintf(tmp, sizeof tmp, "ADB block, size: %zu, compat: %d, ver: %d",
+ apk_blob_push_fmt(&c, "ADB block, size: %" PRIu64 ", compat: %d, ver: %d",
sz, hdr->adb_compat_ver, hdr->adb_ver);
- d->ops->comment(d, APK_BLOB_PTR_LEN(tmp, len));
+ d->ops->comment(d, apk_blob_pushed(APK_BLOB_BUF(tmp), c));
if (ds->root && hdr->adb_compat_ver == 0) dump_object(ctx, ds->root, adb_r_root(db));
- break;
+ return 0;
case ADB_BLOCK_SIG:
s = (struct adb_sign_hdr*) apk_istream_get(is, sz);
data = APK_BLOB_PTR_LEN((char*)s, sz);
r = adb_trust_verify_signature(ctx->trust, db, &ctx->vfy, data);
- len = snprintf(tmp, sizeof tmp, "sig v%02x h%02x ", s->sign_ver, s->hash_alg);
- for (size_t j = sizeof *s; j < data.len; j++)
- len += snprintf(&tmp[len], sizeof tmp - len, "%02x", (uint8_t)data.ptr[j]);
- len += snprintf(&tmp[len], sizeof tmp - len, ": %s", r ? apk_error_str(r) : "OK");
- d->ops->comment(d, APK_BLOB_PTR_LEN(tmp, len));
+ apk_blob_push_fmt(&c, "sig v%02x h%02x ", s->sign_ver, s->hash_alg);
+ for (size_t j = sizeof *s; j < data.len && c.len > 40; j++)
+ apk_blob_push_fmt(&c, "%02x", (uint8_t)data.ptr[j]);
+ if (c.len <= 40) apk_blob_push_blob(&c, APK_BLOB_STRLIT(".."));
+ apk_blob_push_fmt(&c, ": %s", r ? apk_error_str(r) : "OK");
break;
case ADB_BLOCK_DATA:
- len = snprintf(tmp, sizeof tmp, "data block, size: %zu", sz);
- d->ops->comment(d, APK_BLOB_PTR_LEN(tmp, len));
+ apk_blob_push_fmt(&c, "data block, size: %" PRIu64, sz);
break;
default:
- len = snprintf(tmp, sizeof tmp, "unknown block %d, size: %zu",
- adb_block_type(b), sz);
- d->ops->comment(d, APK_BLOB_PTR_LEN(tmp, len));
+ apk_blob_push_fmt(&c, "unknown block %d, size: %" PRIu64, adb_block_type(b), sz);
+ break;
}
+ d->ops->comment(d, apk_blob_pushed(APK_BLOB_BUF(tmp), c));
return 0;
}
int adb_walk_adb(struct adb_walk *d, struct apk_istream *is, struct apk_trust *trust)
{
+ struct apk_trust allow_untrusted = {
+ .allow_untrusted = 1,
+ };
struct adb_walk_ctx ctx = {
.d = d,
.trust = trust,
@@ -163,7 +166,7 @@ int adb_walk_adb(struct adb_walk *d, struct apk_istream *is, struct apk_trust *t
if (IS_ERR(is)) return PTR_ERR(is);
- r = adb_m_process(&ctx.db, is, 0, 0, adb_walk_block);
+ r = adb_m_process(&ctx.db, is, 0, &allow_untrusted, adb_walk_block);
adb_free(&ctx.db);
return r;
}
diff --git a/src/adb_walk_text.c b/src/adb_walk_text.c
index 1b7f5cf..f1ec7ee 100644
--- a/src/adb_walk_text.c
+++ b/src/adb_walk_text.c
@@ -19,7 +19,7 @@ int adb_walk_text(struct adb_walk *d, struct apk_istream *is)
int r = 0, i, multi_line = 0, nesting = 0, new_item = 0;
uint8_t started[64] = {0};
- if (IS_ERR_OR_NULL(is)) return PTR_ERR(is);
+ if (IS_ERR(is)) return PTR_ERR(is);
if (apk_istream_get_delim(is, token, &l) != 0) goto err;
apk_blob_pull_blob_match(&l, APK_BLOB_STR("#%SCHEMA: "));
if ((r = d->ops->schema(d, apk_blob_pull_uint(&l, 16))) != 0) goto err;
diff --git a/src/apk.c b/src/apk.c
index 2da1788..a26f637 100644
--- a/src/apk.c
+++ b/src/apk.c
@@ -11,7 +11,6 @@
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
-#include <assert.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
@@ -20,14 +19,13 @@
#include <unistd.h>
#include <sys/stat.h>
-#include <fetch.h>
-
#include "apk_defines.h"
#include "apk_database.h"
#include "apk_applet.h"
#include "apk_blob.h"
#include "apk_print.h"
#include "apk_io.h"
+#include "apk_fs.h"
#ifdef TEST_MODE
static const char *test_installed_db = NULL;
@@ -62,6 +60,8 @@ static void version(struct apk_out *out, const char *prefix)
OPT(OPT_GLOBAL_force, APK_OPT_SH("f") "force") \
OPT(OPT_GLOBAL_force_binary_stdout, "force-binary-stdout") \
OPT(OPT_GLOBAL_force_broken_world, "force-broken-world") \
+ OPT(OPT_GLOBAL_force_missing_repositories, "force-missing-repositories") \
+ OPT(OPT_GLOBAL_force_no_chroot, "force-no-chroot") \
OPT(OPT_GLOBAL_force_non_repository, "force-non-repository") \
OPT(OPT_GLOBAL_force_old_apk, "force-old-apk") \
OPT(OPT_GLOBAL_force_overwrite, "force-overwrite") \
@@ -70,8 +70,12 @@ static void version(struct apk_out *out, const char *prefix)
OPT(OPT_GLOBAL_interactive, APK_OPT_SH("i") "interactive") \
OPT(OPT_GLOBAL_keys_dir, APK_OPT_ARG "keys-dir") \
OPT(OPT_GLOBAL_no_cache, "no-cache") \
+ OPT(OPT_GLOBAL_no_check_certificate, "no-check-certificate") \
+ OPT(OPT_GLOBAL_no_interactive, "no-interactive") \
+ OPT(OPT_GLOBAL_no_logfile, "no-logfile") \
OPT(OPT_GLOBAL_no_network, "no-network") \
OPT(OPT_GLOBAL_no_progress, "no-progress") \
+ OPT(OPT_GLOBAL_preserve_env, "preserve-env") \
OPT(OPT_GLOBAL_print_arch, "print-arch") \
OPT(OPT_GLOBAL_progress, "progress") \
OPT(OPT_GLOBAL_progress_fd, APK_OPT_ARG "progress-fd") \
@@ -80,6 +84,7 @@ static void version(struct apk_out *out, const char *prefix)
OPT(OPT_GLOBAL_repositories_file, APK_OPT_ARG "repositories-file") \
OPT(OPT_GLOBAL_repository, APK_OPT_ARG APK_OPT_SH("X") "repository") \
OPT(OPT_GLOBAL_root, APK_OPT_ARG APK_OPT_SH("p") "root") \
+ OPT(OPT_GLOBAL_timeout, APK_OPT_ARG "timeout") \
OPT(OPT_GLOBAL_update_cache, APK_OPT_SH("U") "update-cache") \
OPT(OPT_GLOBAL_verbose, APK_OPT_SH("v") "verbose") \
OPT(OPT_GLOBAL_version, APK_OPT_SH("V") "version") \
@@ -141,15 +146,27 @@ static int option_parse_global(void *ctx, struct apk_ctx *ac, int opt, const cha
case OPT_GLOBAL_force_refresh:
ac->force |= APK_FORCE_REFRESH;
break;
+ case OPT_GLOBAL_force_no_chroot:
+ ac->flags |= APK_NO_CHROOT;
+ break;
case OPT_GLOBAL_force_non_repository:
ac->force |= APK_FORCE_NON_REPOSITORY;
break;
case OPT_GLOBAL_force_binary_stdout:
ac->force |= APK_FORCE_BINARY_STDOUT;
break;
+ case OPT_GLOBAL_force_missing_repositories:
+ ac->force |= APK_FORCE_MISSING_REPOSITORIES;
+ break;
case OPT_GLOBAL_interactive:
ac->flags |= APK_INTERACTIVE;
break;
+ case OPT_GLOBAL_no_interactive:
+ ac->flags &= ~APK_INTERACTIVE;
+ break;
+ case OPT_GLOBAL_preserve_env:
+ ac->flags |= APK_PRESERVE_ENV;
+ break;
case OPT_GLOBAL_progress:
ac->progress.out = &ac->out;
break;
@@ -168,12 +185,18 @@ static int option_parse_global(void *ctx, struct apk_ctx *ac, int opt, const cha
case OPT_GLOBAL_wait:
ac->lock_wait = atoi(optarg);
break;
+ case OPT_GLOBAL_no_logfile:
+ ac->flags |= APK_NO_LOGFILE;
+ break;
case OPT_GLOBAL_no_network:
ac->flags |= APK_NO_NETWORK;
break;
case OPT_GLOBAL_no_cache:
ac->flags |= APK_NO_CACHE;
break;
+ case OPT_GLOBAL_no_check_certificate:
+ apk_io_url_no_check_certificate();
+ break;
case OPT_GLOBAL_cache_dir:
ac->cache_dir = optarg;
break;
@@ -185,6 +208,9 @@ static int option_parse_global(void *ctx, struct apk_ctx *ac, int opt, const cha
case OPT_GLOBAL_cache_max_age:
ac->cache_max_age = atoi(optarg) * 60;
break;
+ case OPT_GLOBAL_timeout:
+ apk_io_url_set_timeout(atoi(optarg));
+ break;
case OPT_GLOBAL_arch:
ac->arch = optarg;
break;
@@ -258,6 +284,43 @@ const struct apk_option_group optgroup_commit = {
.parse = option_parse_commit,
};
+#define SOURCE_OPTIONS(OPT) \
+ OPT(OPT_SOURCE_from, APK_OPT_ARG "from")
+
+APK_OPT_GROUP(optiondesc_source, "Source", SOURCE_OPTIONS);
+
+static int option_parse_source(void *ctx, struct apk_ctx *ac, int opt, const char *optarg)
+{
+ const unsigned long all_flags = APK_OPENF_NO_SYS_REPOS | APK_OPENF_NO_INSTALLED_REPO | APK_OPENF_NO_INSTALLED;
+ unsigned long flags;
+
+ switch (opt) {
+ case OPT_SOURCE_from:
+ if (strcmp(optarg, "none") == 0) {
+ flags = APK_OPENF_NO_SYS_REPOS | APK_OPENF_NO_INSTALLED_REPO | APK_OPENF_NO_INSTALLED;
+ } else if (strcmp(optarg, "repositories") == 0) {
+ flags = APK_OPENF_NO_INSTALLED_REPO | APK_OPENF_NO_INSTALLED;
+ } else if (strcmp(optarg, "installed") == 0) {
+ flags = APK_OPENF_NO_SYS_REPOS;
+ } else if (strcmp(optarg, "system") == 0) {
+ flags = 0;
+ } else
+ return -ENOTSUP;
+
+ ac->open_flags &= ~all_flags;
+ ac->open_flags |= flags;
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ return 0;
+}
+
+const struct apk_option_group optgroup_source = {
+ .desc = optiondesc_source,
+ .parse = option_parse_source,
+};
+
static int usage(struct apk_out *out, struct apk_applet *applet)
{
version(out, NULL);
@@ -393,10 +456,27 @@ static void on_sigwinch(int s)
static void setup_terminal(void)
{
+ static char buf[200];
+ setvbuf(stderr, buf, _IOLBF, sizeof buf);
signal(SIGWINCH, on_sigwinch);
signal(SIGPIPE, SIG_IGN);
}
+static int remove_empty_strings(int count, char **args)
+{
+ int i, j;
+ for (i = j = 0; i < count; i++) {
+ args[j] = args[i];
+ if (args[j][0]) j++;
+ }
+ return j;
+}
+
+static void redirect_callback(int code, const char *url)
+{
+ apk_warn(&ctx.out, "Permanently redirected to %s", url);
+}
+
int main(int argc, char **argv)
{
void *applet_ctx = NULL;
@@ -409,7 +489,6 @@ int main(int argc, char **argv)
#ifdef TEST_MODE
apk_string_array_init(&test_repos);
#endif
- apk_applet_register_builtin();
apk_argv = malloc(sizeof(char*[argc+2]));
memcpy(apk_argv, argv, sizeof(char*[argc]));
@@ -430,7 +509,9 @@ int main(int argc, char **argv)
apk_crypto_init();
setup_automatic_flags(&ctx);
- fetchConnectionCacheInit(32, 4);
+ apk_io_url_init();
+ apk_io_url_set_timeout(60);
+ apk_io_url_set_redirect_callback(redirect_callback);
r = parse_options(argc, argv, applet, applet_ctx, &ctx);
if (r != 0) goto err;
@@ -449,6 +530,8 @@ int main(int argc, char **argv)
argc--;
argv++;
}
+ if (applet->remove_empty_arguments)
+ argc = remove_empty_strings(argc, argv);
apk_db_init(&db);
signal(SIGINT, on_sigint);
@@ -458,6 +541,7 @@ int main(int argc, char **argv)
ctx.open_flags |= APK_OPENF_READ | APK_OPENF_NO_STATE | APK_OPENF_NO_REPOS;
ctx.flags |= APK_SIMULATE;
ctx.flags &= ~APK_INTERACTIVE;
+ db.active_layers = BIT(0);
#endif
r = apk_ctx_prepare(&ctx);
@@ -515,8 +599,10 @@ int main(int argc, char **argv)
apk_string_array_resize(&args, argc);
memcpy(args->item, argv, argc * sizeof(*argv));
+ apk_io_url_set_redirect_callback(NULL);
r = applet->main(applet_ctx, &ctx, args);
+ signal(SIGINT, SIG_IGN);
apk_db_close(&db);
#ifdef TEST_MODE
@@ -528,7 +614,6 @@ err:
if (r == -ESHUTDOWN) r = 0;
if (applet_ctx) free(applet_ctx);
- fetchConnectionCacheClose();
apk_ctx_free(&ctx);
apk_string_array_free(&args);
free(apk_argv);
diff --git a/src/apk_adb.c b/src/apk_adb.c
index 8a887cf..5127b5a 100644
--- a/src/apk_adb.c
+++ b/src/apk_adb.c
@@ -1,28 +1,22 @@
#include <errno.h>
+#include <inttypes.h>
#include "adb.h"
#include "apk_adb.h"
#include "apk_print.h"
#include "apk_version.h"
-
-#define APK_VERSION_CONFLICT 16
+#include "apk_package.h"
+#include "apk_ctype.h"
/* Few helpers to map old database to new one */
int apk_dep_split(apk_blob_t *b, apk_blob_t *bdep)
{
- extern const apk_spn_match_def apk_spn_dependency_separator;
-
- if (APK_BLOB_IS_NULL(*b)) return 0;
- if (apk_blob_cspn(*b, apk_spn_dependency_separator, bdep, b)) {
- /* found separator - update b to skip over after all separators */
- if (!apk_blob_spn(*b, apk_spn_dependency_separator, NULL, b))
- *b = APK_BLOB_NULL;
- } else {
- /* no separator - return this as the last dependency, signal quit */
- *bdep = *b;
- *b = APK_BLOB_NULL;
- }
- return 1;
+ if (b->len == 0) return 0;
+ // skip all separator characters
+ apk_blob_spn(*b, APK_CTYPE_DEPENDENCY_SEPARATOR, NULL, b);
+ // split the dependency string
+ apk_blob_cspn(*b, APK_CTYPE_DEPENDENCY_SEPARATOR, bdep, b);
+ return bdep->len != 0;
}
adb_val_t adb_wo_pkginfo(struct adb_obj *obj, unsigned int f, apk_blob_t val)
@@ -35,7 +29,7 @@ adb_val_t adb_wo_pkginfo(struct adb_obj *obj, unsigned int f, apk_blob_t val)
case ADBI_PI_UNIQUE_ID:
if (!val.ptr || val.len < 4) break;
apk_blob_pull_csum(&val, &csum);
- v = adb_w_int(obj->db, get_unaligned32(csum.data) & ADB_VALUE_MASK);
+ v = adb_w_blob(obj->db, APK_BLOB_CSUM(csum));
break;
case ADBI_PI_REPO_COMMIT:
if (val.len < 40) break;
@@ -67,12 +61,12 @@ unsigned int adb_pkg_field_index(char f)
MAP('D', ADBI_PI_DEPENDS),
MAP('i', ADBI_PI_INSTALL_IF),
MAP('p', ADBI_PI_PROVIDES),
+ MAP('k', ADBI_PI_PROVIDER_PRIORITY),
MAP('o', ADBI_PI_ORIGIN),
MAP('m', ADBI_PI_MAINTAINER),
MAP('t', ADBI_PI_BUILD_TIME),
MAP('c', ADBI_PI_REPO_COMMIT),
MAP('r', ADBI_PI_REPLACES),
- MAP('k', ADBI_PI_PRIORITY),
};
if (f < 'A' || f-'A' >= ARRAY_SIZE(map)) return 0;
return map[(unsigned char)f - 'A'];
@@ -112,13 +106,84 @@ static struct adb_scalar_schema scalar_mstring = {
const struct adb_object_schema schema_string_array = {
.kind = ADB_KIND_ARRAY,
- .num_fields = APK_MAX_PKG_TRIGGERS,
+ .num_fields = 32,
.fields = ADB_ARRAY_ITEM(scalar_string),
};
+static apk_blob_t xattr_tostring(struct adb *db, adb_val_t val, char *buf, size_t bufsz)
+{
+ apk_blob_t b = adb_r_blob(db, val), to = APK_BLOB_PTR_LEN(buf, bufsz), k, v;
+
+ if (APK_BLOB_IS_NULL(b)) return b;
+ if (!apk_blob_split(b, APK_BLOB_BUF(""), &k, &v)) return APK_BLOB_NULL;
+
+ apk_blob_push_blob(&to, k);
+ apk_blob_push_blob(&to, APK_BLOB_PTR_LEN("=", 1));
+ apk_blob_push_hexdump(&to, v);
+ if (!APK_BLOB_IS_NULL(to))
+ return APK_BLOB_PTR_PTR(buf, to.ptr-1);
+ return APK_BLOB_PTR_LEN(buf, snprintf(buf, bufsz, BLOB_FMT "=(%d bytes)",
+ BLOB_PRINTF(k), (int)v.len));
+}
+
+static adb_val_t xattr_fromstring(struct adb *db, apk_blob_t val)
+{
+ char buf[256];
+ apk_blob_t b[2], hex;
+
+ if (!apk_blob_rsplit(val, '=', &b[0], &hex)) return ADB_ERROR(APKE_ADB_SCHEMA);
+ b[0].len++;
+
+ if (hex.len & 1) return ADB_ERROR(EINVAL);
+ if (hex.len/2 > sizeof buf) return ADB_ERROR(E2BIG);
+ b[1] = APK_BLOB_PTR_LEN(buf, hex.len / 2);
+ apk_blob_pull_hexdump(&hex, b[1]);
+ if (APK_BLOB_IS_NULL(hex)) return ADB_ERROR(EINVAL);
+
+ return adb_w_blob_vec(db, ARRAY_SIZE(b), b);
+}
+
+static const struct adb_scalar_schema schema_xattr = {
+ .kind = ADB_KIND_BLOB,
+ .tostring = xattr_tostring,
+ .fromstring = xattr_fromstring,
+ .compare = string_compare,
+};
+
+const struct adb_object_schema schema_xattr_array = {
+ .kind = ADB_KIND_ARRAY,
+ .num_fields = 8,
+ .pre_commit = adb_wa_sort,
+ .fields = ADB_ARRAY_ITEM(schema_xattr),
+};
+
+static adb_val_t name_fromstring(struct adb *db, apk_blob_t val)
+{
+ // Check invalid first character
+ if (val.len == 0 || !isascii(val.ptr[0]) || !isalnum(val.ptr[0])) goto fail;
+ // Shall consist of characters
+ if (apk_blob_spn(val, APK_CTYPE_PACKAGE_NAME, NULL, NULL)) goto fail;
+ return adb_w_blob(db, val);
+fail:
+ return ADB_ERROR(APKE_PKGNAME_FORMAT);
+}
+
+static struct adb_scalar_schema scalar_name = {
+ .kind = ADB_KIND_BLOB,
+ .tostring = string_tostring,
+ .fromstring = name_fromstring,
+ .compare = string_compare,
+};
+
+static adb_val_t version_fromstring(struct adb *db, apk_blob_t val)
+{
+ if (!apk_version_validate(val)) return ADB_ERROR(APKE_PKGVERSION_FORMAT);
+ return adb_w_blob(db, val);
+}
+
static int version_compare(struct adb *db1, adb_val_t v1, struct adb *db2, adb_val_t v2)
{
- switch (apk_version_compare_blob(adb_r_blob(db1, v1), adb_r_blob(db2, v2))) {
+ switch (apk_version_compare(adb_r_blob(db1, v1), adb_r_blob(db2, v2))) {
case APK_VERSION_LESS: return -1;
case APK_VERSION_GREATER: return 1;
default: return 0;
@@ -128,11 +193,10 @@ static int version_compare(struct adb *db1, adb_val_t v1, struct adb *db2, adb_v
static struct adb_scalar_schema scalar_version = {
.kind = ADB_KIND_BLOB,
.tostring = string_tostring,
- .fromstring = string_fromstring,
+ .fromstring = version_fromstring,
.compare = version_compare,
};
-
static apk_blob_t hexblob_tostring(struct adb *db, adb_val_t val, char *buf, size_t bufsz)
{
apk_blob_t b = adb_r_blob(db, val), to = APK_BLOB_PTR_LEN(buf, bufsz);
@@ -150,10 +214,8 @@ static adb_val_t hexblob_fromstring(struct adb *db, apk_blob_t val)
{
char buf[256];
- if (val.len & 1)
- return ADB_ERROR(EINVAL);
- if (val.len > sizeof buf)
- return ADB_ERROR(E2BIG);
+ if (val.len & 1) return ADB_ERROR(EINVAL);
+ if (val.len/2 > sizeof buf) return ADB_ERROR(E2BIG);
apk_blob_t b = APK_BLOB_PTR_LEN(buf, val.len / 2);
apk_blob_pull_hexdump(&val, b);
@@ -167,11 +229,12 @@ static struct adb_scalar_schema scalar_hexblob = {
.kind = ADB_KIND_BLOB,
.tostring = hexblob_tostring,
.fromstring = hexblob_fromstring,
+ .compare = string_compare,
};
static apk_blob_t int_tostring(struct adb *db, adb_val_t val, char *buf, size_t bufsz)
{
- return APK_BLOB_PTR_LEN(buf, snprintf(buf, bufsz, "%u", adb_r_int(db, val)));
+ return APK_BLOB_PTR_LEN(buf, snprintf(buf, bufsz, "%" PRIu64, adb_r_int(db, val)));
}
static adb_val_t int_fromstring(struct adb *db, apk_blob_t val)
@@ -183,8 +246,8 @@ static adb_val_t int_fromstring(struct adb *db, apk_blob_t val)
static int int_compare(struct adb *db1, adb_val_t v1, struct adb *db2, adb_val_t v2)
{
- uint32_t r1 = adb_r_int(db1, v1);
- uint32_t r2 = adb_r_int(db1, v2);
+ uint64_t r1 = adb_r_int(db1, v1);
+ uint64_t r2 = adb_r_int(db1, v2);
if (r1 < r2) return -1;
if (r1 > r2) return 1;
return 0;
@@ -199,7 +262,7 @@ static struct adb_scalar_schema scalar_int = {
static apk_blob_t oct_tostring(struct adb *db, adb_val_t val, char *buf, size_t bufsz)
{
- return APK_BLOB_PTR_LEN(buf, snprintf(buf, bufsz, "%o", adb_r_int(db, val)));
+ return APK_BLOB_PTR_LEN(buf, snprintf(buf, bufsz, "%" PRIo64, adb_r_int(db, val)));
}
static adb_val_t oct_fromstring(struct adb *db, apk_blob_t val)
@@ -213,6 +276,7 @@ static struct adb_scalar_schema scalar_oct = {
.kind = ADB_KIND_INT,
.tostring = oct_tostring,
.fromstring = oct_fromstring,
+ .compare = int_compare,
};
static apk_blob_t hsize_tostring(struct adb *db, adb_val_t val, char *buf, size_t bufsz)
@@ -246,100 +310,59 @@ static struct adb_scalar_schema scalar_hsize = {
static apk_blob_t dependency_tostring(struct adb_obj *obj, char *buf, size_t bufsz)
{
apk_blob_t name, ver;
- unsigned int mask;
+ unsigned int op;
name = adb_ro_blob(obj, ADBI_DEP_NAME);
ver = adb_ro_blob(obj, ADBI_DEP_VERSION);
+ op = adb_ro_int(obj, ADBI_DEP_MATCH) ?: APK_VERSION_EQUAL;
if (APK_BLOB_IS_NULL(name)) return APK_BLOB_NULL;
- if (APK_BLOB_IS_NULL(ver)) return name;
+ if (APK_BLOB_IS_NULL(ver)) {
+ if (op & APK_VERSION_CONFLICT)
+ return APK_BLOB_PTR_LEN(buf,
+ snprintf(buf, bufsz, "!"BLOB_FMT,
+ BLOB_PRINTF(name)));
+ return name;
+ }
- mask = adb_ro_int(obj, ADBI_DEP_MATCH) ?: APK_VERSION_EQUAL;
return APK_BLOB_PTR_LEN(buf,
snprintf(buf, bufsz, "%s"BLOB_FMT"%s"BLOB_FMT,
- (mask & APK_VERSION_CONFLICT) ? "!" : "",
+ (op & APK_VERSION_CONFLICT) ? "!" : "",
BLOB_PRINTF(name),
- apk_version_op_string(mask & ~APK_VERSION_CONFLICT),
+ apk_version_op_string(op),
BLOB_PRINTF(ver)));
}
static int dependency_fromstring(struct adb_obj *obj, apk_blob_t bdep)
{
- extern const apk_spn_match_def apk_spn_dependency_comparer;
- extern const apk_spn_match_def apk_spn_repotag_separator;
- apk_blob_t bname, bop, bver = APK_BLOB_NULL, btag;
- int mask = APK_DEPMASK_ANY;
-
- /* [!]name[<,<=,<~,=,~,>~,>=,>,><]ver */
-
- /* parse the version */
- if (bdep.ptr[0] == '!') {
- bdep.ptr++;
- bdep.len--;
- mask |= APK_VERSION_CONFLICT;
- }
+ apk_blob_t bname, bver;
+ int op;
- if (apk_blob_cspn(bdep, apk_spn_dependency_comparer, &bname, &bop)) {
- int i;
-
- if (mask == 0)
- goto fail;
- if (!apk_blob_spn(bop, apk_spn_dependency_comparer, &bop, &bver))
- goto fail;
-
- mask = 0;
- for (i = 0; i < bop.len; i++) {
- switch (bop.ptr[i]) {
- case '<':
- mask |= APK_VERSION_LESS;
- break;
- case '>':
- mask |= APK_VERSION_GREATER;
- break;
- case '~':
- mask |= APK_VERSION_FUZZY|APK_VERSION_EQUAL;
- break;
- case '=':
- mask |= APK_VERSION_EQUAL;
- break;
- }
- }
- if ((mask & APK_DEPMASK_CHECKSUM) != APK_DEPMASK_CHECKSUM &&
- !apk_version_validate(bver))
- goto fail;
- } else {
- bname = bdep;
- bop = APK_BLOB_NULL;
- bver = APK_BLOB_NULL;
- }
+ if (apk_dep_parse(bdep, &bname, &op, &bver) != 0) goto fail;
+ if ((op & APK_DEPMASK_CHECKSUM) != APK_DEPMASK_CHECKSUM &&
+ !apk_version_validate(bver)) goto fail;
- if (apk_blob_cspn(bname, apk_spn_repotag_separator, &bname, &btag))
- ; /* tag = repository tag */
+ if (apk_blob_spn(bname, APK_CTYPE_DEPENDENCY_NAME, NULL, NULL)) goto fail;
adb_wo_blob(obj, ADBI_DEP_NAME, bname);
- if (mask != APK_DEPMASK_ANY) {
+ if (op != APK_DEPMASK_ANY) {
adb_wo_blob(obj, ADBI_DEP_VERSION, bver);
- if (mask != APK_VERSION_EQUAL)
- adb_wo_int(obj, ADBI_DEP_MATCH, mask);
+ if (op != APK_VERSION_EQUAL)
+ adb_wo_int(obj, ADBI_DEP_MATCH, op);
}
return 0;
fail:
- return -APKE_ADB_DEPENDENCY_FORMAT;
-}
-
-static int dependency_cmp(const struct adb_obj *o1, const struct adb_obj *o2)
-{
- return adb_ro_cmp(o1, o2, ADBI_DEP_NAME);
+ return -APKE_DEPENDENCY_FORMAT;
}
const struct adb_object_schema schema_dependency = {
.kind = ADB_KIND_OBJECT,
.num_fields = ADBI_DEP_MAX,
+ .num_compare = ADBI_DEP_NAME,
.tostring = dependency_tostring,
.fromstring = dependency_fromstring,
- .compare = dependency_cmp,
- .fields = {
+ .fields = ADB_OBJECT_FIELDS(ADBI_DEP_MAX) {
ADB_FIELD(ADBI_DEP_NAME, "name", scalar_string),
ADB_FIELD(ADBI_DEP_VERSION, "version", scalar_version),
ADB_FIELD(ADBI_DEP_MATCH, "match", scalar_int),
@@ -354,7 +377,8 @@ static int dependencies_fromstring(struct adb_obj *obj, apk_blob_t b)
adb_wo_alloca(&dep, &schema_dependency, obj->db);
while (apk_dep_split(&b, &bdep)) {
- adb_wo_fromstring(&dep, bdep);
+ int r = adb_wo_fromstring(&dep, bdep);
+ if (r) return r;
adb_wa_append_obj(obj, &dep);
}
@@ -364,29 +388,19 @@ static int dependencies_fromstring(struct adb_obj *obj, apk_blob_t b)
const struct adb_object_schema schema_dependency_array = {
.kind = ADB_KIND_ARRAY,
.fromstring = dependencies_fromstring,
- .num_fields = APK_MAX_PKG_DEPENDENCIES,
+ .num_fields = 32,
.pre_commit = adb_wa_sort_unique,
.fields = ADB_ARRAY_ITEM(schema_dependency),
};
-static int pkginfo_cmp(const struct adb_obj *o1, const struct adb_obj *o2)
-{
- int r;
- r = adb_ro_cmp(o1, o2, ADBI_PI_NAME);
- if (r) return r;
- r = adb_ro_cmp(o1, o2, ADBI_PI_VERSION);
- if (r) return r;
- return adb_ro_cmp(o1, o2, ADBI_PI_UNIQUE_ID);
-}
-
const struct adb_object_schema schema_pkginfo = {
.kind = ADB_KIND_OBJECT,
.num_fields = ADBI_PI_MAX,
- .compare = pkginfo_cmp,
- .fields = {
- ADB_FIELD(ADBI_PI_NAME, "name", scalar_string),
+ .num_compare = ADBI_PI_UNIQUE_ID,
+ .fields = ADB_OBJECT_FIELDS(ADBI_PI_MAX) {
+ ADB_FIELD(ADBI_PI_NAME, "name", scalar_name),
ADB_FIELD(ADBI_PI_VERSION, "version", scalar_version),
- ADB_FIELD(ADBI_PI_UNIQUE_ID, "unique-id", scalar_int),
+ ADB_FIELD(ADBI_PI_UNIQUE_ID, "unique-id", scalar_hexblob),
ADB_FIELD(ADBI_PI_DESCRIPTION, "description", scalar_string),
ADB_FIELD(ADBI_PI_ARCH, "arch", scalar_string),
ADB_FIELD(ADBI_PI_LICENSE, "license", scalar_string),
@@ -397,18 +411,19 @@ const struct adb_object_schema schema_pkginfo = {
ADB_FIELD(ADBI_PI_BUILD_TIME, "build-time", scalar_int),
ADB_FIELD(ADBI_PI_INSTALLED_SIZE,"installed-size",scalar_hsize),
ADB_FIELD(ADBI_PI_FILE_SIZE, "file-size", scalar_hsize),
- ADB_FIELD(ADBI_PI_PRIORITY, "priority", scalar_int),
+ ADB_FIELD(ADBI_PI_PROVIDER_PRIORITY, "provider-priority", scalar_int),
ADB_FIELD(ADBI_PI_DEPENDS, "depends", schema_dependency_array),
ADB_FIELD(ADBI_PI_PROVIDES, "provides", schema_dependency_array),
ADB_FIELD(ADBI_PI_REPLACES, "replaces", schema_dependency_array),
ADB_FIELD(ADBI_PI_INSTALL_IF, "install-if", schema_dependency_array),
ADB_FIELD(ADBI_PI_RECOMMENDS, "recommends", schema_dependency_array),
+ ADB_FIELD(ADBI_PI_LAYER, "layer", scalar_int),
},
};
const struct adb_object_schema schema_pkginfo_array = {
.kind = ADB_KIND_ARRAY,
- .num_fields = APK_MAX_INDEX_PACKAGES,
+ .num_fields = 128,
.pre_commit = adb_wa_sort,
.fields = ADB_ARRAY_ITEM(schema_pkginfo),
};
@@ -416,7 +431,7 @@ const struct adb_object_schema schema_pkginfo_array = {
const struct adb_object_schema schema_index = {
.kind = ADB_KIND_OBJECT,
.num_fields = ADBI_NDX_MAX,
- .fields = {
+ .fields = ADB_OBJECT_FIELDS(ADBI_NDX_MAX) {
ADB_FIELD(ADBI_NDX_DESCRIPTION, "description", scalar_string),
ADB_FIELD(ADBI_NDX_PACKAGES, "packages", schema_pkginfo_array),
},
@@ -425,24 +440,19 @@ const struct adb_object_schema schema_index = {
const struct adb_object_schema schema_acl = {
.kind = ADB_KIND_OBJECT,
.num_fields = ADBI_ACL_MAX,
- .fields = {
+ .fields = ADB_OBJECT_FIELDS(ADBI_ACL_MAX) {
ADB_FIELD(ADBI_ACL_MODE, "mode", scalar_oct),
ADB_FIELD(ADBI_ACL_USER, "user", scalar_string),
ADB_FIELD(ADBI_ACL_GROUP, "group", scalar_string),
- //ADB_FIELD(ADBI_ACL_XATTRS, "xattr", schema_string_array),
+ ADB_FIELD(ADBI_ACL_XATTRS, "xattrs", schema_xattr_array),
},
};
-static int file_cmp(const struct adb_obj *o1, const struct adb_obj *o2)
-{
- return adb_ro_cmp(o1, o2, ADBI_FI_NAME);
-}
-
const struct adb_object_schema schema_file = {
.kind = ADB_KIND_OBJECT,
.num_fields = ADBI_FI_MAX,
- .compare = file_cmp,
- .fields = {
+ .num_compare = ADBI_FI_NAME,
+ .fields = ADB_OBJECT_FIELDS(ADBI_FI_MAX) {
ADB_FIELD(ADBI_FI_NAME, "name", scalar_string),
ADB_FIELD(ADBI_FI_ACL, "acl", schema_acl),
ADB_FIELD(ADBI_FI_SIZE, "size", scalar_int),
@@ -455,15 +465,15 @@ const struct adb_object_schema schema_file = {
const struct adb_object_schema schema_file_array = {
.kind = ADB_KIND_ARRAY,
.pre_commit = adb_wa_sort,
- .num_fields = APK_MAX_MANIFEST_FILES,
+ .num_fields = 128,
.fields = ADB_ARRAY_ITEM(schema_file),
};
const struct adb_object_schema schema_dir = {
.kind = ADB_KIND_OBJECT,
.num_fields = ADBI_DI_MAX,
- .compare = file_cmp,
- .fields = {
+ .num_compare = ADBI_DI_NAME,
+ .fields = ADB_OBJECT_FIELDS(ADBI_DI_MAX) {
ADB_FIELD(ADBI_DI_NAME, "name", scalar_string),
ADB_FIELD(ADBI_DI_ACL, "acl", schema_acl),
ADB_FIELD(ADBI_DI_FILES, "files", schema_file_array),
@@ -473,14 +483,14 @@ const struct adb_object_schema schema_dir = {
const struct adb_object_schema schema_dir_array = {
.kind = ADB_KIND_ARRAY,
.pre_commit = adb_wa_sort,
- .num_fields = APK_MAX_MANIFEST_PATHS,
+ .num_fields = 128,
.fields = ADB_ARRAY_ITEM(schema_dir),
};
const struct adb_object_schema schema_scripts = {
.kind = ADB_KIND_OBJECT,
.num_fields = ADBI_SCRPT_MAX,
- .fields = {
+ .fields = ADB_OBJECT_FIELDS(ADBI_SCRPT_MAX) {
ADB_FIELD(ADBI_SCRPT_TRIGGER, "trigger", scalar_mstring),
ADB_FIELD(ADBI_SCRPT_PREINST, "pre-install", scalar_mstring),
ADB_FIELD(ADBI_SCRPT_POSTINST, "post-install", scalar_mstring),
@@ -491,21 +501,16 @@ const struct adb_object_schema schema_scripts = {
},
};
-static int package_cmp(const struct adb_obj *o1, const struct adb_obj *o2)
-{
- return adb_ro_cmp(o1, o2, ADBI_PKG_PKGINFO);
-}
-
const struct adb_object_schema schema_package = {
.kind = ADB_KIND_OBJECT,
.num_fields = ADBI_PKG_MAX,
- .compare = package_cmp,
- .fields = {
+ .num_compare = ADBI_PKG_PKGINFO,
+ .fields = ADB_OBJECT_FIELDS(ADBI_PKG_MAX) {
ADB_FIELD(ADBI_PKG_PKGINFO, "info", schema_pkginfo),
ADB_FIELD(ADBI_PKG_PATHS, "paths", schema_dir_array),
ADB_FIELD(ADBI_PKG_SCRIPTS, "scripts", schema_scripts),
ADB_FIELD(ADBI_PKG_TRIGGERS, "triggers", schema_string_array),
- //ADB_FIELD(ADBI_PKG_PASSWD, "passwd", schema_string_array),
+ ADB_FIELD(ADBI_PKG_REPLACES_PRIORITY, "replaces-priority", scalar_int),
},
};
@@ -518,14 +523,14 @@ const struct adb_adb_schema schema_package_adb = {
const struct adb_object_schema schema_package_adb_array = {
.kind = ADB_KIND_ARRAY,
.pre_commit = adb_wa_sort,
- .num_fields = APK_MAX_INDEX_PACKAGES,
+ .num_fields = 128,
.fields = ADB_ARRAY_ITEM(schema_package_adb),
};
const struct adb_object_schema schema_idb = {
.kind = ADB_KIND_OBJECT,
.num_fields = ADBI_IDB_MAX,
- .fields = {
+ .fields = ADB_OBJECT_FIELDS(ADBI_IDB_MAX) {
ADB_FIELD(ADBI_IDB_PACKAGES, "packages", schema_package_adb_array),
},
};
diff --git a/src/apk_adb.h b/src/apk_adb.h
index 68bc92d..74b0577 100644
--- a/src/apk_adb.h
+++ b/src/apk_adb.h
@@ -25,13 +25,14 @@
#define ADBI_PI_BUILD_TIME 0x0b
#define ADBI_PI_INSTALLED_SIZE 0x0c
#define ADBI_PI_FILE_SIZE 0x0d
-#define ADBI_PI_PRIORITY 0x0e
+#define ADBI_PI_PROVIDER_PRIORITY 0x0e
#define ADBI_PI_DEPENDS 0x0f
#define ADBI_PI_PROVIDES 0x10
#define ADBI_PI_REPLACES 0x11
#define ADBI_PI_INSTALL_IF 0x12
#define ADBI_PI_RECOMMENDS 0x13
-#define ADBI_PI_MAX 0x14
+#define ADBI_PI_LAYER 0x14
+#define ADBI_PI_MAX 0x15
/* ACL entries */
#define ADBI_ACL_MODE 0x01
@@ -70,7 +71,7 @@
#define ADBI_PKG_PATHS 0x02
#define ADBI_PKG_SCRIPTS 0x03
#define ADBI_PKG_TRIGGERS 0x04
-#define ADBI_PKG_PASSWD 0x05
+#define ADBI_PKG_REPLACES_PRIORITY 0x05
#define ADBI_PKG_MAX 0x06
struct adb_data_package {
@@ -88,16 +89,10 @@ struct adb_data_package {
#define ADBI_IDB_MAX 0x02
/* */
-#define APK_MAX_PKG_DEPENDENCIES 512
-#define APK_MAX_PKG_REPLACES 32
-#define APK_MAX_PKG_TRIGGERS 32
-#define APK_MAX_INDEX_PACKAGES 20000
-#define APK_MAX_MANIFEST_FILES 8000
-#define APK_MAX_MANIFEST_PATHS 6000
-
extern const struct adb_object_schema
schema_dependency, schema_dependency_array,
schema_pkginfo, schema_pkginfo_array,
+ schema_xattr_array,
schema_acl, schema_file, schema_file_array, schema_dir, schema_dir_array,
schema_string_array, schema_scripts, schema_package, schema_package_adb_array,
schema_index, schema_idb;
diff --git a/src/apk_applet.h b/src/apk_applet.h
index c3d5978..20c8c78 100644
--- a/src/apk_applet.h
+++ b/src/apk_applet.h
@@ -47,23 +47,20 @@ struct apk_applet {
const char *name;
const struct apk_option_group *optgroups[4];
+ unsigned int remove_empty_arguments : 1;
unsigned int open_flags, forced_force;
int context_size;
int (*main)(void *ctx, struct apk_ctx *ac, struct apk_string_array *args);
};
-extern const struct apk_option_group optgroup_global, optgroup_commit, optgroup_signing;
+extern const struct apk_option_group optgroup_global, optgroup_commit, optgroup_signing, optgroup_source;
void apk_applet_register(struct apk_applet *);
-void apk_applet_register_builtin(void);
struct apk_applet *apk_applet_find(const char *name);
void apk_applet_help(struct apk_applet *applet, struct apk_out *out);
-typedef void (*apk_init_func_t)(void);
-
#define APK_DEFINE_APPLET(x) \
-static void __register_##x(void) { apk_applet_register(&x); } \
-static apk_init_func_t __regfunc_##x __attribute__((__section__("initapplets"))) __attribute((used)) = __register_##x;
+__attribute__((constructor)) static void __register_##x(void) { apk_applet_register(&x); }
#endif
diff --git a/src/apk_archive.h b/src/apk_archive.h
deleted file mode 100644
index 3dbd284..0000000
--- a/src/apk_archive.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* apk_archive.c - Alpine Package Keeper (APK)
- *
- * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
- * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
- * All rights reserved.
- *
- * SPDX-License-Identifier: GPL-2.0-only
- */
-
-#ifndef APK_ARCHIVE
-#define APK_ARCHIVE
-
-#include <sys/types.h>
-#include "apk_blob.h"
-#include "apk_print.h"
-#include "apk_io.h"
-
-int apk_tar_parse(struct apk_istream *,
- apk_archive_entry_parser parser, void *ctx,
- struct apk_id_cache *);
-int apk_tar_write_entry(struct apk_ostream *, const struct apk_file_info *ae,
- const char *data);
-int apk_tar_write_padding(struct apk_ostream *, const struct apk_file_info *ae);
-
-#define APK_EXTRACTF_NO_CHOWN 0x0001
-#define APK_EXTRACTF_NO_OVERWRITE 0x0002
-
-int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae,
- const char *extract_name, const char *hardlink_name,
- struct apk_istream *is,
- apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx,
- unsigned int extract_flags,
- struct apk_out *out);
-
-#endif
diff --git a/src/apk_blob.h b/src/apk_blob.h
index 2220a75..1d0d3f0 100644
--- a/src/apk_blob.h
+++ b/src/apk_blob.h
@@ -14,10 +14,6 @@
#include <string.h>
#include "apk_defines.h"
-#include "apk_openssl.h"
-
-typedef const unsigned char *apk_spn_match;
-typedef unsigned char apk_spn_match_def[256 / 8];
struct apk_blob {
long len;
@@ -45,22 +41,6 @@ struct apk_checksum {
unsigned char type;
};
-static inline const EVP_MD *apk_checksum_evp(int type)
-{
- switch (type) {
- case APK_CHECKSUM_MD5:
- return EVP_md5();
- case APK_CHECKSUM_SHA1:
- return EVP_sha1();
- }
- return EVP_md_null();
-}
-
-static inline const EVP_MD *apk_checksum_default(void)
-{
- return apk_checksum_evp(APK_CHECKSUM_DEFAULT);
-}
-
#define APK_BLOB_IS_NULL(blob) ((blob).ptr == NULL)
#define APK_BLOB_NULL ((apk_blob_t){0, NULL})
@@ -87,8 +67,7 @@ static inline apk_blob_t apk_blob_trim(apk_blob_t blob)
}
char *apk_blob_cstr(apk_blob_t str);
-int apk_blob_spn(apk_blob_t blob, const apk_spn_match accept, apk_blob_t *l, apk_blob_t *r);
-int apk_blob_cspn(apk_blob_t blob, const apk_spn_match reject, apk_blob_t *l, apk_blob_t *r);
+apk_blob_t apk_blob_dup(apk_blob_t blob);
int apk_blob_split(apk_blob_t blob, apk_blob_t split, apk_blob_t *l, apk_blob_t *r);
int apk_blob_rsplit(apk_blob_t blob, char split, apk_blob_t *l, apk_blob_t *r);
apk_blob_t apk_blob_pushed(apk_blob_t buffer, apk_blob_t left);
@@ -101,11 +80,6 @@ int apk_blob_ends_with(apk_blob_t str, apk_blob_t suffix);
int apk_blob_for_each_segment(apk_blob_t blob, const char *split,
apk_blob_cb cb, void *ctx);
-static inline void apk_blob_checksum(apk_blob_t b, const EVP_MD *md, struct apk_checksum *csum)
-{
- csum->type = EVP_MD_size(md);
- EVP_Digest(b.ptr, b.len, csum->data, NULL, md, NULL);
-}
static inline char *apk_blob_chr(apk_blob_t b, unsigned char ch)
{
return memchr(b.ptr, ch, b.len);
@@ -116,16 +90,14 @@ void apk_blob_push_uint(apk_blob_t *to, unsigned int value, int radix);
void apk_blob_push_csum(apk_blob_t *to, struct apk_checksum *csum);
void apk_blob_push_base64(apk_blob_t *to, apk_blob_t binary);
void apk_blob_push_hexdump(apk_blob_t *to, apk_blob_t binary);
+void apk_blob_push_fmt(apk_blob_t *to, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
void apk_blob_pull_char(apk_blob_t *b, int expected);
-unsigned int apk_blob_pull_uint(apk_blob_t *b, int radix);
+uint64_t apk_blob_pull_uint(apk_blob_t *b, int radix);
void apk_blob_pull_csum(apk_blob_t *b, struct apk_checksum *csum);
void apk_blob_pull_base64(apk_blob_t *b, apk_blob_t to);
void apk_blob_pull_hexdump(apk_blob_t *b, apk_blob_t to);
int apk_blob_pull_blob_match(apk_blob_t *b, apk_blob_t match);
-#if defined(__GLIBC__) && !defined(__UCLIBC__)
-extern size_t strlcpy(char *dest, const char *src, size_t size);
-#endif
-
#endif
diff --git a/src/apk_context.h b/src/apk_context.h
index 3a6f2f2..493a26f 100644
--- a/src/apk_context.h
+++ b/src/apk_context.h
@@ -9,9 +9,11 @@
#ifndef APK_CONTEXT_H
#define APK_CONTEXT_H
+#include "apk_blob.h"
#include "apk_print.h"
#include "apk_trust.h"
#include "apk_io.h"
+#include "apk_crypto.h"
#include "adb.h"
#define APK_SIMULATE BIT(0)
@@ -26,6 +28,8 @@
#define APK_NO_CACHE BIT(9)
#define APK_NO_COMMIT_HOOKS BIT(10)
#define APK_NO_CHROOT BIT(11)
+#define APK_NO_LOGFILE BIT(12)
+#define APK_PRESERVE_ENV BIT(13)
#define APK_FORCE_OVERWRITE BIT(0)
#define APK_FORCE_OLD_APK BIT(1)
@@ -33,8 +37,7 @@
#define APK_FORCE_REFRESH BIT(3)
#define APK_FORCE_NON_REPOSITORY BIT(4)
#define APK_FORCE_BINARY_STDOUT BIT(5)
-
-struct apk_database;
+#define APK_FORCE_MISSING_REPOSITORIES BIT(6)
#define APK_OPENF_READ 0x0001
#define APK_OPENF_WRITE 0x0002
@@ -46,19 +49,24 @@ struct apk_database;
#define APK_OPENF_NO_INSTALLED_REPO 0x0200
#define APK_OPENF_CACHE_WRITE 0x0400
#define APK_OPENF_NO_AUTOUPDATE 0x0800
+#define APK_OPENF_NO_CMDLINE_REPOS 0x1000
+#define APK_OPENF_USERMODE 0x2000
+#define APK_OPENF_ALLOW_ARCH 0x4000
#define APK_OPENF_NO_REPOS (APK_OPENF_NO_SYS_REPOS | \
+ APK_OPENF_NO_CMDLINE_REPOS | \
APK_OPENF_NO_INSTALLED_REPO)
#define APK_OPENF_NO_STATE (APK_OPENF_NO_INSTALLED | \
APK_OPENF_NO_SCRIPTS | \
APK_OPENF_NO_WORLD)
+struct apk_database;
+
struct apk_ctx {
- unsigned int flags, force, lock_wait;
+ unsigned int flags, force, open_flags;
+ unsigned int lock_wait, cache_max_age;
struct apk_out out;
struct apk_progress progress;
- unsigned int cache_max_age;
- unsigned long open_flags;
const char *root;
const char *arch;
const char *keys_dir;
@@ -66,12 +74,14 @@ struct apk_ctx {
const char *repositories_file;
const char *uvol;
struct apk_string_array *repository_list;
- struct apk_string_array *private_keys;
+ struct apk_istream *protected_paths;
+ struct apk_digest_ctx dctx;
struct apk_trust trust;
struct apk_id_cache id_cache;
struct apk_database *db;
- int root_fd;
+ int root_fd, dest_fd;
+ unsigned int root_set : 1;
};
void apk_ctx_init(struct apk_ctx *ac);
@@ -82,6 +92,7 @@ struct apk_trust *apk_ctx_get_trust(struct apk_ctx *ac);
struct apk_id_cache *apk_ctx_get_id_cache(struct apk_ctx *ac);
static inline int apk_ctx_fd_root(struct apk_ctx *ac) { return ac->root_fd; }
+static inline int apk_ctx_fd_dest(struct apk_ctx *ac) { return ac->dest_fd; }
static inline time_t apk_ctx_since(struct apk_ctx *ac, time_t since) {
return (ac->force & APK_FORCE_REFRESH) ? APK_ISTREAM_FORCE_REFRESH : since;
}
diff --git a/src/apk_crypto.h b/src/apk_crypto.h
index d3ce24b..7de88df 100644
--- a/src/apk_crypto.h
+++ b/src/apk_crypto.h
@@ -9,24 +9,19 @@
#ifndef APK_CRYPTO_H
#define APK_CRYPTO_H
-#include <assert.h>
#include <string.h>
-#include <openssl/evp.h>
#include "apk_defines.h"
-#include "apk_openssl.h"
+#include "apk_blob.h"
+#include "apk_crypto_openssl.h"
// Digest
-struct apk_digest_ctx {
- EVP_MD_CTX *mdctx;
- uint8_t alg;
-};
-
#define APK_DIGEST_NONE 0x00
#define APK_DIGEST_MD5 0x01
#define APK_DIGEST_SHA1 0x02
#define APK_DIGEST_SHA256 0x03
#define APK_DIGEST_SHA512 0x04
+#define APK_DIGEST_SHA256_160 0x05
#define APK_DIGEST_MAX_LENGTH 64 // longest is SHA512
@@ -40,21 +35,13 @@ struct apk_digest {
#define APK_DIGEST_BLOB(d) APK_BLOB_PTR_LEN((void*)((d).data), (d).len)
-static inline const EVP_MD *apk_digest_alg_to_evp(uint8_t alg) {
- switch (alg) {
- case APK_DIGEST_NONE: return EVP_md_null();
- case APK_DIGEST_MD5: return EVP_md5();
- case APK_DIGEST_SHA1: return EVP_sha1();
- case APK_DIGEST_SHA256: return EVP_sha256();
- case APK_DIGEST_SHA512: return EVP_sha512();
- default:
- assert(alg);
- return EVP_md_null();
- }
-}
-
int apk_digest_alg_len(uint8_t alg);
uint8_t apk_digest_alg_by_len(int len);
+uint8_t apk_digest_from_blob(struct apk_digest *d, apk_blob_t b);
+void apk_digest_from_checksum(struct apk_digest *d, const struct apk_checksum *c);
+void apk_checksum_from_digest(struct apk_checksum *csum, const struct apk_digest *d);
+
+int apk_digest_calc(struct apk_digest *d, uint8_t alg, const void *ptr, size_t sz);
static inline int apk_digest_cmp(struct apk_digest *a, struct apk_digest *b) {
if (a->alg != b->alg) return b->alg - a->alg;
@@ -71,104 +58,32 @@ static inline void apk_digest_set(struct apk_digest *d, uint8_t alg) {
d->len = apk_digest_alg_len(alg);
}
-static inline int apk_digest_calc(struct apk_digest *d, uint8_t alg, const void *ptr, size_t sz)
-{
- unsigned int md_sz = sizeof d->data;
- if (EVP_Digest(ptr, sz, d->data, &md_sz, apk_digest_alg_to_evp(alg), 0) != 1)
- return -APKE_CRYPTO_ERROR;
- d->alg = alg;
- d->len = md_sz;
- return 0;
-}
-
-static inline int apk_digest_ctx_init(struct apk_digest_ctx *dctx, uint8_t alg) {
- dctx->mdctx = EVP_MD_CTX_new();
- if (!dctx->mdctx) return -ENOMEM;
- dctx->alg = alg;
-#ifdef EVP_MD_CTX_FLAG_FINALISE
- EVP_MD_CTX_set_flags(dctx->mdctx, EVP_MD_CTX_FLAG_FINALISE);
-#endif
- EVP_DigestInit_ex(dctx->mdctx, apk_digest_alg_to_evp(alg), 0);
- return 0;
-}
-
-static inline void apk_digest_ctx_free(struct apk_digest_ctx *dctx) {
- EVP_MD_CTX_free(dctx->mdctx);
- dctx->mdctx = 0;
-}
-
-static inline int apk_digest_ctx_update(struct apk_digest_ctx *dctx, const void *ptr, size_t sz) {
- return EVP_DigestUpdate(dctx->mdctx, ptr, sz) == 1 ? 0 : -APKE_CRYPTO_ERROR;
-}
-
-static inline int apk_digest_ctx_final(struct apk_digest_ctx *dctx, struct apk_digest *d) {
- unsigned int mdlen = sizeof d->data;
- if (EVP_DigestFinal_ex(dctx->mdctx, d->data, &mdlen) != 1) {
- apk_digest_reset(d);
- return -APKE_CRYPTO_ERROR;
- }
- d->alg = dctx->alg;
- d->len = mdlen;
- return 0;
-}
-
-#include "apk_blob.h"
-uint8_t apk_digest_from_blob(struct apk_digest *d, apk_blob_t b);
static inline int apk_digest_cmp_csum(const struct apk_digest *d, const struct apk_checksum *csum)
{
return apk_blob_compare(APK_DIGEST_BLOB(*d), APK_BLOB_CSUM(*csum));
}
-static inline void apk_checksum_from_digest(struct apk_checksum *csum, const struct apk_digest *d)
-{
- csum->type = d->len;
- memcpy(csum->data, d->data, d->len);
-}
-// Asymmetric keys
+int apk_digest_ctx_init(struct apk_digest_ctx *dctx, uint8_t alg);
+int apk_digest_ctx_reset(struct apk_digest_ctx *dctx);
+int apk_digest_ctx_reset_alg(struct apk_digest_ctx *dctx, uint8_t alg);
+void apk_digest_ctx_free(struct apk_digest_ctx *dctx);
+int apk_digest_ctx_update(struct apk_digest_ctx *dctx, const void *ptr, size_t sz);
+int apk_digest_ctx_final(struct apk_digest_ctx *dctx, struct apk_digest *d);
-struct apk_pkey {
- uint8_t id[16];
- EVP_PKEY *key;
-};
+// Asymmetric keys
-int apk_pkey_init(struct apk_pkey *pkey, EVP_PKEY *key);
void apk_pkey_free(struct apk_pkey *pkey);
int apk_pkey_load(struct apk_pkey *pkey, int dirfd, const char *fn);
// Signing
-int apk_sign_start(struct apk_digest_ctx *, struct apk_pkey *);
+int apk_sign_start(struct apk_digest_ctx *, uint8_t, struct apk_pkey *);
int apk_sign(struct apk_digest_ctx *, void *, size_t *);
-int apk_verify_start(struct apk_digest_ctx *, struct apk_pkey *);
+int apk_verify_start(struct apk_digest_ctx *, uint8_t, struct apk_pkey *);
int apk_verify(struct apk_digest_ctx *, void *, size_t);
// Initializiation
-#if OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
-
-static inline void apk_crypto_cleanup(void)
-{
- EVP_cleanup();
-#ifndef OPENSSL_NO_ENGINE
- ENGINE_cleanup();
-#endif
- CRYPTO_cleanup_all_ex_data();
-}
-
-static inline void apk_crypto_init(void)
-{
- atexit(apk_crypto_cleanup);
- OpenSSL_add_all_algorithms();
-#ifndef OPENSSL_NO_ENGINE
- ENGINE_load_builtin_engines();
- ENGINE_register_all_complete();
-#endif
-}
-
-#else
-
-static inline void apk_crypto_init(void) {}
-
-#endif
+void apk_crypto_init(void);
#endif
diff --git a/src/apk_crypto_openssl.h b/src/apk_crypto_openssl.h
new file mode 100644
index 0000000..396f9b8
--- /dev/null
+++ b/src/apk_crypto_openssl.h
@@ -0,0 +1,25 @@
+/* apk_crypto_openssl.h - Alpine Package Keeper (APK)
+ *
+ * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
+ * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#ifndef APK_CRYPTO_OPENSSL_H
+#define APK_CRYPTO_OPENSSL_H
+
+#include <openssl/evp.h>
+
+struct apk_digest_ctx {
+ EVP_MD_CTX *mdctx;
+ uint8_t alg;
+};
+
+struct apk_pkey {
+ uint8_t id[16];
+ EVP_PKEY *key;
+};
+
+#endif
diff --git a/src/apk_ctype.h b/src/apk_ctype.h
new file mode 100644
index 0000000..b42098e
--- /dev/null
+++ b/src/apk_ctype.h
@@ -0,0 +1,25 @@
+/* apk_ctype.h - Alpine Package Keeper (APK)
+ *
+ * Copyright (C) 2024 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#ifndef APK_CTYPE_H
+#define APK_CTYPE_H
+
+enum {
+ APK_CTYPE_HEXDIGIT = 0,
+ APK_CTYPE_PACKAGE_NAME,
+ APK_CTYPE_VERSION_SUFFIX,
+ APK_CTYPE_DEPENDENCY_NAME,
+ APK_CTYPE_DEPENDENCY_COMPARER,
+ APK_CTYPE_DEPENDENCY_SEPARATOR,
+ APK_CTYPE_REPOSITORY_SEPARATOR,
+};
+
+int apk_blob_spn(apk_blob_t blob, unsigned char ctype, apk_blob_t *l, apk_blob_t *r);
+int apk_blob_cspn(apk_blob_t blob, unsigned char ctype, apk_blob_t *l, apk_blob_t *r);
+
+#endif
diff --git a/src/apk_database.h b/src/apk_database.h
index 94507bc..95372de 100644
--- a/src/apk_database.h
+++ b/src/apk_database.h
@@ -13,7 +13,6 @@
#include "apk_version.h"
#include "apk_hash.h"
#include "apk_atom.h"
-#include "apk_archive.h"
#include "apk_package.h"
#include "apk_io.h"
#include "apk_context.h"
@@ -46,11 +45,17 @@ struct apk_db_file {
enum apk_protect_mode {
APK_PROTECT_NONE = 0,
+ APK_PROTECT_IGNORE,
APK_PROTECT_CHANGED,
APK_PROTECT_SYMLINKS_ONLY,
APK_PROTECT_ALL,
};
+static inline int apk_protect_mode_none(enum apk_protect_mode mode)
+{
+ return mode == APK_PROTECT_NONE || mode == APK_PROTECT_IGNORE;
+}
+
struct apk_protected_path {
char *relative_pattern;
unsigned protect_mode : 3;
@@ -62,21 +67,19 @@ struct apk_db_dir {
unsigned long hash;
struct apk_db_dir *parent;
+ struct apk_db_dir_instance *owner;
+ struct list_head diris;
struct apk_protected_path_array *protected_paths;
- mode_t mode;
- uid_t uid;
- gid_t gid;
unsigned short refs;
unsigned short namelen;
- unsigned protect_mode : 3;
- unsigned has_protected_children : 1;
+ unsigned char protect_mode : 3;
+ unsigned char has_protected_children : 1;
- unsigned seen : 1;
- unsigned created : 1;
- unsigned modified : 1;
- unsigned update_permissions : 1;
+ unsigned char created : 1;
+ unsigned char modified : 1;
+ unsigned char permissions_ok : 1;
char rooted_name[1];
char name[];
@@ -86,6 +89,7 @@ struct apk_db_dir {
#define DIR_FILE_PRINTF(dir,file) (dir)->name, (dir)->namelen ? "/" : "", (file)->name
struct apk_db_dir_instance {
+ struct list_head dir_diri_list;
struct hlist_node pkg_dirs_list;
struct hlist_head owned_files;
struct apk_package *pkg;
@@ -102,20 +106,26 @@ struct apk_name {
unsigned is_dependency : 1;
unsigned auto_select_virtual: 1;
unsigned priority : 2;
+ unsigned solver_flags_set : 1;
+ unsigned providers_sorted : 1;
unsigned int foreach_genid;
union {
struct apk_solver_name_state ss;
- void *state_ptr;
+ unsigned long state_buf[4];
int state_int;
};
};
struct apk_repository {
const char *url;
- struct apk_checksum csum;
+ struct apk_digest hash;
apk_blob_t description;
};
+#define APK_DB_LAYER_ROOT 0
+#define APK_DB_LAYER_UVOL 1
+#define APK_DB_LAYER_NUM 2
+
#define APK_REPOSITORY_CACHED 0
#define APK_REPOSITORY_FIRST_CONFIGURED 1
@@ -136,15 +146,23 @@ struct apk_database {
unsigned long cache_remount_flags;
apk_blob_t *arch;
unsigned int local_repos, available_repos;
- unsigned int repo_update_errors, repo_update_counter;
unsigned int pending_triggers;
unsigned int extract_flags;
- int performing_self_upgrade : 1;
- int permanent : 1;
- int autoupdate : 1;
- int open_complete : 1;
- int compat_newfeatures : 1;
- int compat_notinstallable : 1;
+ unsigned int active_layers;
+ unsigned int num_dir_update_errors;
+
+ unsigned int performing_self_upgrade : 1;
+ unsigned int usermode : 1;
+ unsigned int permanent : 1;
+ unsigned int autoupdate : 1;
+ unsigned int write_arch : 1;
+ unsigned int script_dirs_checked : 1;
+ unsigned int open_complete : 1;
+ unsigned int compat_newfeatures : 1;
+ unsigned int compat_notinstallable : 1;
+ unsigned int compat_depversions : 1;
+ unsigned int sorted_names : 1;
+ unsigned int sorted_installed_packages : 1;
struct apk_dependency_array *world;
struct apk_id_cache *id_cache;
@@ -152,13 +170,20 @@ struct apk_database {
struct apk_repository repos[APK_MAX_REPOS];
struct apk_repository_tag repo_tags[APK_MAX_TAGS];
struct apk_atom_pool atoms;
+ struct apk_string_array *filename_array;
+
+ struct {
+ unsigned stale, updated, unavailable;
+ } repositories;
struct {
+ struct apk_name_array *sorted_names;
struct apk_hash names;
struct apk_hash packages;
} available;
struct {
+ struct apk_package_array *sorted_packages;
struct list_head packages;
struct list_head triggers;
struct apk_hash dirs;
@@ -177,17 +202,25 @@ typedef union apk_database_or_void {
void *ptr;
} apk_database_t __attribute__ ((__transparent_union__));
+static inline int apk_name_cmp_display(const struct apk_name *a, const struct apk_name *b) {
+ return strcasecmp(a->name, b->name) ?: strcmp(a->name, b->name);
+}
+struct apk_provider_array *apk_name_sorted_providers(struct apk_name *);
+
struct apk_name *apk_db_get_name(struct apk_database *db, apk_blob_t name);
struct apk_name *apk_db_query_name(struct apk_database *db, apk_blob_t name);
int apk_db_get_tag_id(struct apk_database *db, apk_blob_t tag);
-struct apk_db_dir *apk_db_dir_ref(struct apk_db_dir *dir);
+void apk_db_dir_update_permissions(struct apk_database *db, struct apk_db_dir_instance *diri);
+void apk_db_dir_prepare(struct apk_database *db, struct apk_db_dir *dir, struct apk_db_acl *expected_acl, struct apk_db_acl *new_acl);
void apk_db_dir_unref(struct apk_database *db, struct apk_db_dir *dir, int allow_rmdir);
+struct apk_db_dir *apk_db_dir_ref(struct apk_db_dir *dir);
struct apk_db_dir *apk_db_dir_get(struct apk_database *db, apk_blob_t name);
struct apk_db_dir *apk_db_dir_query(struct apk_database *db, apk_blob_t name);
struct apk_db_file *apk_db_file_query(struct apk_database *db,
apk_blob_t dir, apk_blob_t name);
+const char *apk_db_layer_name(int layer);
void apk_db_init(struct apk_database *db);
int apk_db_open(struct apk_database *db, struct apk_ctx *ctx);
void apk_db_close(struct apk_database *db);
@@ -196,7 +229,6 @@ int apk_db_permanent(struct apk_database *db);
int apk_db_check_world(struct apk_database *db, struct apk_dependency_array *world);
int apk_db_fire_triggers(struct apk_database *db);
int apk_db_run_script(struct apk_database *db, char *fn, char **argv);
-void apk_db_update_directory_permissions(struct apk_database *db);
static inline time_t apk_db_url_since(struct apk_database *db, time_t since) {
return apk_ctx_since(db->ctx, since);
}
@@ -207,8 +239,8 @@ struct apk_package *apk_db_get_file_owner(struct apk_database *db, apk_blob_t fi
int apk_db_index_read(struct apk_database *db, struct apk_istream *is, int repo);
int apk_db_index_read_file(struct apk_database *db, const char *file, int repo);
-int apk_db_index_write(struct apk_database *db, struct apk_ostream *os);
+int apk_db_repository_check(struct apk_database *db);
int apk_db_add_repository(apk_database_t db, apk_blob_t repository);
struct apk_repository *apk_db_select_repo(struct apk_database *db,
struct apk_package *pkg);
@@ -221,21 +253,54 @@ unsigned int apk_db_get_pinning_mask_repos(struct apk_database *db, unsigned sho
int apk_db_cache_active(struct apk_database *db);
int apk_cache_download(struct apk_database *db, struct apk_repository *repo,
- struct apk_package *pkg, int verify, int autoupdate,
+ struct apk_package *pkg, int autoupdate,
apk_progress_cb cb, void *cb_ctx);
-typedef void (*apk_cache_item_cb)(struct apk_database *db,
+typedef void (*apk_cache_item_cb)(struct apk_database *db, int static_cache,
int dirfd, const char *name,
struct apk_package *pkg);
-int apk_db_cache_foreach_item(struct apk_database *db, apk_cache_item_cb cb);
+int apk_db_cache_foreach_item(struct apk_database *db, apk_cache_item_cb cb, int static_cache);
int apk_db_install_pkg(struct apk_database *db,
struct apk_package *oldpkg,
struct apk_package *newpkg,
apk_progress_cb cb, void *cb_ctx);
-void apk_name_foreach_matching(struct apk_database *db, struct apk_string_array *filter, unsigned int match,
- void (*cb)(struct apk_database *db, const char *match, struct apk_name *name, void *ctx),
- void *ctx);
+
+struct apk_package_array *apk_db_sorted_installed_packages(struct apk_database *db);
+
+typedef int (*apk_db_foreach_name_cb)(struct apk_database *db, const char *match, struct apk_name *name, void *ctx);
+
+int apk_db_foreach_matching_name(struct apk_database *db, struct apk_string_array *filter,
+ apk_db_foreach_name_cb cb, void *ctx);
+
+int apk_db_foreach_sorted_name(struct apk_database *db, struct apk_string_array *filter,
+ apk_db_foreach_name_cb cb, void *ctx);
+
+typedef int (*apk_db_foreach_package_cb)(struct apk_database *db, const char *match, struct apk_package *pkg, void *ctx);
+
+int __apk_db_foreach_sorted_package(struct apk_database *db, struct apk_string_array *filter,
+ apk_db_foreach_package_cb cb, void *cb_ctx, int provides);
+
+static inline int apk_db_foreach_sorted_package(struct apk_database *db, struct apk_string_array *filter,
+ apk_db_foreach_package_cb cb, void *cb_ctx) {
+ return __apk_db_foreach_sorted_package(db, filter, cb, cb_ctx, 0);
+}
+
+static inline int apk_db_foreach_sorted_providers(struct apk_database *db, struct apk_string_array *filter,
+ apk_db_foreach_package_cb cb, void *cb_ctx) {
+ return __apk_db_foreach_sorted_package(db, filter, cb, cb_ctx, 1);
+}
+
+
+static inline uint8_t apk_dbf_digest(struct apk_db_file *dbf)
+{
+ uint8_t alg;
+ if (!dbf) return APK_DIGEST_NONE;
+ alg = apk_digest_alg_by_len(dbf->csum.type);
+ if (alg == APK_DIGEST_SHA1 && dbf->diri->pkg->ipkg->sha256_160)
+ alg = APK_DIGEST_SHA256_160;
+ return alg;
+}
#endif
diff --git a/src/apk_defines.h b/src/apk_defines.h
index f1b0f8d..96e3add 100644
--- a/src/apk_defines.h
+++ b/src/apk_defines.h
@@ -10,14 +10,16 @@
#ifndef APK_DEFINES_H
#define APK_DEFINES_H
+#include <assert.h>
#include <endian.h>
#include <stdint.h>
+#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-#define BIT(x) (1 << (x))
+#define BIT(x) (1U << (x))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
@@ -40,9 +42,14 @@ enum {
APKE_CRYPTO_ERROR,
APKE_CRYPTO_NOT_SUPPORTED,
APKE_CRYPTO_KEY_FORMAT,
- APKE_SIGNATURE_FAIL,
+ APKE_SIGNATURE_GEN_FAILURE,
APKE_SIGNATURE_UNTRUSTED,
APKE_SIGNATURE_INVALID,
+ APKE_FORMAT_INVALID,
+ APKE_FORMAT_NOT_SUPPORTED,
+ APKE_PKGNAME_FORMAT,
+ APKE_PKGVERSION_FORMAT,
+ APKE_DEPENDENCY_FORMAT,
APKE_ADB_COMPRESSION,
APKE_ADB_HEADER,
APKE_ADB_VERSION,
@@ -51,7 +58,6 @@ enum {
APKE_ADB_SIGNATURE,
APKE_ADB_NO_FROMSTRING,
APKE_ADB_LIMIT,
- APKE_ADB_DEPENDENCY_FORMAT,
APKE_ADB_PACKAGE_FORMAT,
APKE_V2DB_FORMAT,
APKE_V2PKG_FORMAT,
@@ -61,14 +67,16 @@ enum {
APKE_INDEX_STALE,
APKE_FILE_INTEGRITY,
APKE_CACHE_NOT_AVAILABLE,
- APKE_UVOL,
+ APKE_UVOL_NOT_AVAILABLE,
+ APKE_UVOL_ERROR,
+ APKE_UVOL_ROOT,
+ APKE_REMOTE_IO,
};
static inline void *ERR_PTR(long error) { return (void*) error; }
static inline void *ERR_CAST(const void *ptr) { return (void*) ptr; }
static inline int PTR_ERR(const void *ptr) { return (int)(long) ptr; }
static inline int IS_ERR(const void *ptr) { return (unsigned long)ptr >= (unsigned long)-4095; }
-static inline int IS_ERR_OR_NULL(const void *ptr) { return IS_ERR(ptr) || !ptr; }
#if defined __GNUC__ && __GNUC__ == 2 && __GNUC_MINOR__ < 96
#define __builtin_expect(x, expected_value) (x)
@@ -97,45 +105,55 @@ static inline int IS_ERR_OR_NULL(const void *ptr) { return IS_ERR(ptr) || !ptr;
/* default architecture for APK packages. */
#if defined(__x86_64__)
-#define APK_DEFAULT_ARCH "x86_64"
+#define APK_DEFAULT_BASE_ARCH "x86_64"
#elif defined(__i386__)
-#define APK_DEFAULT_ARCH "x86"
+#define APK_DEFAULT_BASE_ARCH "x86"
#elif defined(__powerpc__) && !defined(__powerpc64__)
-#define APK_DEFAULT_ARCH "ppc"
+#define APK_DEFAULT_BASE_ARCH "ppc"
#elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-#define APK_DEFAULT_ARCH "ppc64"
+#define APK_DEFAULT_BASE_ARCH "ppc64"
#elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define APK_DEFAULT_ARCH "ppc64le"
+#define APK_DEFAULT_BASE_ARCH "ppc64le"
#elif defined(__arm__) && defined(__ARM_PCS_VFP) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && __ARM_ARCH>=7
-#define APK_DEFAULT_ARCH "armv7"
+#define APK_DEFAULT_BASE_ARCH "armv7"
#elif defined(__arm__) && defined(__ARM_PCS_VFP) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define APK_DEFAULT_ARCH "armhf"
+#define APK_DEFAULT_BASE_ARCH "armhf"
#elif defined(__arm__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define APK_DEFAULT_ARCH "armel"
+#define APK_DEFAULT_BASE_ARCH "armel"
#elif defined(__aarch64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define APK_DEFAULT_ARCH "aarch64"
+#define APK_DEFAULT_BASE_ARCH "aarch64"
#elif defined(__s390x__)
-#define APK_DEFAULT_ARCH "s390x"
+#define APK_DEFAULT_BASE_ARCH "s390x"
#elif defined(__mips64) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-#define APK_DEFAULT_ARCH "mips64"
+#define APK_DEFAULT_BASE_ARCH "mips64"
#elif defined(__mips64) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define APK_DEFAULT_ARCH "mips64el"
+#define APK_DEFAULT_BASE_ARCH "mips64el"
#elif defined(__mips__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-#define APK_DEFAULT_ARCH "mips"
+#define APK_DEFAULT_BASE_ARCH "mips"
#elif defined(__mips__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define APK_DEFAULT_ARCH "mipsel"
+#define APK_DEFAULT_BASE_ARCH "mipsel"
#elif defined(__riscv) && __riscv_xlen == 32
-#define APK_DEFAULT_ARCH "riscv32"
+#define APK_DEFAULT_BASE_ARCH "riscv32"
#elif defined(__riscv) && __riscv_xlen == 64
-#define APK_DEFAULT_ARCH "riscv64"
+#define APK_DEFAULT_BASE_ARCH "riscv64"
#elif defined(__loongarch__) && defined(__loongarch32) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define APK_DEFAULT_ARCH "loongarch32"
+#define APK_DEFAULT_BASE_ARCH "loongarch32"
#elif defined(__loongarch__) && defined(__loongarchx32) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define APK_DEFAULT_ARCH "loongarchx32"
+#define APK_DEFAULT_BASE_ARCH "loongarchx32"
#elif defined(__loongarch__) && defined(__loongarch64) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define APK_DEFAULT_ARCH "loongarch64"
+#define APK_DEFAULT_BASE_ARCH "loongarch64"
+#elif defined(__ARCHS__)
+#define APK_DEFAULT_BASE_ARCH "archs"
+#elif defined(__ARC700__)
+#define APK_DEFAULT_BASE_ARCH "arc700"
#else
-#error APK_DEFAULT_ARCH not detected for this architecture
+#error APK_DEFAULT_BASE_ARCH not detected for this architecture
+#endif
+
+#ifndef APK_ARCH_PREFIX
+#define APK_DEFAULT_ARCH APK_DEFAULT_BASE_ARCH
+#else
+#define APK_DEFAULT_ARCH APK_ARCH_PREFIX "-" APK_DEFAULT_BASE_ARCH
#endif
#define APK_MAX_REPOS 32 /* see struct apk_package */
@@ -171,12 +189,14 @@ static inline uint32_t get_unaligned32(const void *ptr)
return *(const uint32_t *)ptr;
#else
const uint8_t *p = ptr;
- return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+ return p[0] | (uint32_t)p[1] << 8 | (uint32_t)p[2] << 16 | (uint32_t)p[3] << 24;
#endif
}
typedef void (*apk_progress_cb)(void *cb_ctx, size_t);
+time_t apk_get_build_time(void);
+
void *apk_array_resize(void *array, size_t new_size, size_t elem_size);
#define APK_ARRAY(array_type_name, elem_type_name) \
@@ -209,7 +229,7 @@ void *apk_array_resize(void *array, size_t new_size, size_t elem_size);
static inline elem_type_name * \
array_type_name##_add(struct array_type_name **a) \
{ \
- int size = 1 + (*a)->num; \
+ int size = 1 + ((*a) ? (*a)->num : 0); \
*a = apk_array_resize(*a, size, sizeof(elem_type_name));\
return &(*a)->item[size-1]; \
}
@@ -219,6 +239,7 @@ APK_ARRAY(apk_string_array, char *);
#define foreach_array_item(iter, array) \
for (iter = &(array)->item[0]; iter < &(array)->item[(array)->num]; iter++)
+#define LIST_HEAD(name) struct list_head name = { &name, &name }
#define LIST_END (void *) 0xe01
#define LIST_POISON1 (void *) 0xdeadbeef
#define LIST_POISON2 (void *) 0xabbaabba
diff --git a/src/apk_extract.h b/src/apk_extract.h
new file mode 100644
index 0000000..e3fabad
--- /dev/null
+++ b/src/apk_extract.h
@@ -0,0 +1,62 @@
+/* apk_extract.c - Alpine Package Keeper (APK)
+ *
+ * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
+ * Copyright (C) 2008-2021 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#ifndef APK_EXTRACT
+#define APK_EXTRACT
+
+#include "apk_crypto.h"
+#include "apk_print.h"
+#include "apk_io.h"
+
+struct adb_obj;
+struct apk_ctx;
+struct apk_extract_ctx;
+
+struct apk_extract_ops {
+ int (*v2index)(struct apk_extract_ctx *, apk_blob_t *desc, struct apk_istream *is);
+ int (*v2meta)(struct apk_extract_ctx *, struct apk_istream *is);
+ int (*v3index)(struct apk_extract_ctx *, struct adb_obj *);
+ int (*v3meta)(struct apk_extract_ctx *, struct adb_obj *);
+ int (*script)(struct apk_extract_ctx *, unsigned int script, size_t size, struct apk_istream *is);
+ int (*file)(struct apk_extract_ctx *, const struct apk_file_info *fi, struct apk_istream *is);
+};
+
+struct apk_extract_ctx {
+ struct apk_ctx *ac;
+ const struct apk_extract_ops *ops;
+ struct apk_checksum *identity;
+ apk_blob_t desc;
+ void *pctx;
+ unsigned generate_identity : 1;
+ unsigned is_package : 1;
+ unsigned is_index : 1;
+};
+
+static inline void apk_extract_init(struct apk_extract_ctx *ectx, struct apk_ctx *ac, const struct apk_extract_ops *ops) {
+ *ectx = (struct apk_extract_ctx){.ac = ac, .ops = ops};
+}
+static inline void apk_extract_reset(struct apk_extract_ctx *ectx) {
+ apk_extract_init(ectx, ectx->ac, ectx->ops);
+}
+static inline void apk_extract_generate_identity(struct apk_extract_ctx *ctx, struct apk_checksum *id) {
+ ctx->identity = id;
+ ctx->generate_identity = 1;
+}
+static inline void apk_extract_verify_identity(struct apk_extract_ctx *ctx, struct apk_checksum *id) {
+ ctx->identity = id;
+}
+int apk_extract(struct apk_extract_ctx *, struct apk_istream *is);
+
+int apk_extract_v2(struct apk_extract_ctx *, struct apk_istream *is);
+void apk_extract_v2_control(struct apk_extract_ctx *, apk_blob_t, apk_blob_t);
+int apk_extract_v2_meta(struct apk_extract_ctx *ectx, struct apk_istream *is);
+
+int apk_extract_v3(struct apk_extract_ctx *, struct apk_istream *is);
+
+#endif
diff --git a/src/apk_fs.h b/src/apk_fs.h
new file mode 100644
index 0000000..3ad721b
--- /dev/null
+++ b/src/apk_fs.h
@@ -0,0 +1,81 @@
+/* apk_fs.h - Alpine Package Keeper (APK)
+ *
+ * Copyright (C) 2021 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#ifndef APK_FS_H
+#define APK_FS_H
+
+#include "apk_context.h"
+#include "apk_io.h"
+#include "apk_pathbuilder.h"
+
+#define APK_FS_PRIO_DISK 0
+#define APK_FS_PRIO_UVOL 1
+#define APK_FS_PRIO_MAX 2
+
+#define APK_FS_CTRL_COMMIT 1
+#define APK_FS_CTRL_APKNEW 2
+#define APK_FS_CTRL_CANCEL 3
+#define APK_FS_CTRL_DELETE 4
+
+#define APK_FS_DIR_MODIFIED 1
+
+struct apk_fsdir_ops;
+
+struct apk_fsdir {
+ struct apk_ctx *ac;
+ const struct apk_fsdir_ops *ops;
+ struct apk_pathbuilder pb;
+ unsigned int extract_flags;
+ apk_blob_t pkgctx;
+};
+
+struct apk_fsdir_ops {
+ uint8_t priority;
+
+ int (*dir_create)(struct apk_fsdir *, mode_t, uid_t, gid_t);
+ int (*dir_delete)(struct apk_fsdir *);
+ int (*dir_check)(struct apk_fsdir *, mode_t, uid_t, gid_t);
+ int (*dir_update_perms)(struct apk_fsdir *, mode_t, uid_t, gid_t);
+
+ int (*file_extract)(struct apk_ctx *, const struct apk_file_info *, struct apk_istream *, apk_progress_cb, void *, unsigned int, apk_blob_t);
+ int (*file_control)(struct apk_fsdir *, apk_blob_t, int);
+ int (*file_info)(struct apk_fsdir *, apk_blob_t, unsigned int, struct apk_file_info *);
+};
+
+#define APK_FSEXTRACTF_NO_CHOWN 0x0001
+#define APK_FSEXTRACTF_NO_OVERWRITE 0x0002
+#define APK_FSEXTRACTF_NO_SYS_XATTRS 0x0004
+
+int apk_fs_extract(struct apk_ctx *, const struct apk_file_info *, struct apk_istream *, apk_progress_cb, void *, unsigned int, apk_blob_t);
+
+void apk_fsdir_get(struct apk_fsdir *, apk_blob_t dir, unsigned int extract_flags, struct apk_ctx *ac, apk_blob_t pkgctx);
+
+static inline uint8_t apk_fsdir_priority(struct apk_fsdir *fs) {
+ return fs->ops->priority;
+}
+static inline int apk_fsdir_create(struct apk_fsdir *fs, mode_t mode, uid_t uid, gid_t gid) {
+ return fs->ops->dir_create(fs, mode, uid, gid);
+}
+static inline int apk_fsdir_delete(struct apk_fsdir *fs) {
+ return fs->ops->dir_delete(fs);
+}
+static inline int apk_fsdir_check(struct apk_fsdir *fs, mode_t mode, uid_t uid, gid_t gid) {
+ return fs->ops->dir_check(fs, mode, uid, gid);
+}
+static inline int apk_fsdir_update_perms(struct apk_fsdir *fs, mode_t mode, uid_t uid, gid_t gid) {
+ return fs->ops->dir_update_perms(fs, mode, uid, gid);
+}
+
+static inline int apk_fsdir_file_control(struct apk_fsdir *fs, apk_blob_t filename, int ctrl) {
+ return fs->ops->file_control(fs, filename, ctrl);
+}
+static inline int apk_fsdir_file_info(struct apk_fsdir *fs, apk_blob_t filename, unsigned int flags, struct apk_file_info *fi) {
+ return fs->ops->file_info(fs, filename, flags, fi);
+}
+
+#endif
diff --git a/src/apk_hash.h b/src/apk_hash.h
index a8eb33b..b3f358e 100644
--- a/src/apk_hash.h
+++ b/src/apk_hash.h
@@ -68,15 +68,9 @@ static inline apk_hash_item apk_hash_get(struct apk_hash *h, apk_blob_t key)
return apk_hash_get_hashed(h, key, apk_hash_from_key(h, key));
}
-
static inline void apk_hash_insert(struct apk_hash *h, apk_hash_item item)
{
return apk_hash_insert_hashed(h, item, apk_hash_from_item(h, item));
}
-static inline void apk_hash_delete(struct apk_hash *h, apk_blob_t key)
-{
- return apk_hash_delete_hashed(h, key, apk_hash_from_key(h, key));
-}
-
#endif
diff --git a/src/apk_io.h b/src/apk_io.h
index 3ad0790..0520503 100644
--- a/src/apk_io.h
+++ b/src/apk_io.h
@@ -18,6 +18,7 @@
#include "apk_atom.h"
#include "apk_crypto.h"
+int apk_make_dirs(int root_fd, const char *dirname, mode_t dirmode, mode_t parentmode);
ssize_t apk_write_fully(int fd, const void *ptr, size_t size);
struct apk_id_hash {
@@ -44,7 +45,6 @@ struct apk_file_meta {
struct apk_file_info {
const char *name;
const char *link_target;
- const char *uvol_name;
const char *uname;
const char *gname;
off_t size;
@@ -127,6 +127,12 @@ static inline int apk_istream_close_error(struct apk_istream *is, int r)
return apk_istream_close(is);
}
+void apk_io_url_init(void);
+void apk_io_url_set_timeout(int timeout);
+void apk_io_url_set_redirect_callback(void (*cb)(int, const char *));
+void apk_io_url_no_check_certificate(void);
+struct apk_istream *apk_io_url_istream(const char *url, time_t since);
+
struct apk_segment_istream {
struct apk_istream is;
struct apk_istream *pis;
@@ -135,6 +141,15 @@ struct apk_segment_istream {
};
struct apk_istream *apk_istream_segment(struct apk_segment_istream *sis, struct apk_istream *is, size_t len, time_t mtime);
+struct apk_digest_istream {
+ struct apk_istream is;
+ struct apk_istream *pis;
+ struct apk_digest *digest;
+ struct apk_digest_ctx dctx;
+ off_t size_left;
+};
+struct apk_istream *apk_istream_verify(struct apk_digest_istream *dis, struct apk_istream *is, off_t size, struct apk_digest *d);
+
#define APK_ISTREAM_TEE_COPY_META 1
#define APK_ISTREAM_TEE_OPTIONAL 2
@@ -168,8 +183,8 @@ static inline int apk_ostream_close(struct apk_ostream *os)
return os->ops->close(os) ?: rc;
}
-apk_blob_t apk_blob_from_istream(struct apk_istream *istream, size_t size);
-apk_blob_t apk_blob_from_file(int atfd, const char *file);
+int apk_blob_from_istream(struct apk_istream *is, size_t size, apk_blob_t *b);
+int apk_blob_from_file(int atfd, const char *file, apk_blob_t *b);
#define APK_BTF_ADD_EOL 0x00000001
int apk_blob_to_file(int atfd, const char *file, apk_blob_t b, unsigned int flags);
@@ -218,12 +233,15 @@ static inline struct apk_istream *apk_istream_deflate(struct apk_istream *is) {
return apk_istream_zlib(is, 1, NULL, NULL);
}
-struct apk_ostream *apk_ostream_zlib(struct apk_ostream *, int);
+struct apk_ostream *apk_ostream_zlib(struct apk_ostream *, int, uint8_t);
static inline struct apk_ostream *apk_ostream_gzip(struct apk_ostream *os) {
- return apk_ostream_zlib(os, 0);
+ return apk_ostream_zlib(os, 0, 0);
}
-static inline struct apk_ostream *apk_ostream_deflate(struct apk_ostream *os) {
- return apk_ostream_zlib(os, 1);
+static inline struct apk_ostream *apk_ostream_deflate(struct apk_ostream *os, uint8_t level) {
+ return apk_ostream_zlib(os, 1, level);
}
+struct apk_istream *apk_istream_zstd(struct apk_istream *);
+struct apk_ostream *apk_ostream_zstd(struct apk_ostream *, uint8_t);
+
#endif
diff --git a/src/apk_nproc.h b/src/apk_nproc.h
new file mode 100644
index 0000000..6328a25
--- /dev/null
+++ b/src/apk_nproc.h
@@ -0,0 +1,16 @@
+#pragma once
+#include <unistd.h>
+#ifdef __linux__
+#include <sched.h>
+#endif
+
+static inline int apk_get_nproc(void)
+{
+#ifdef __linux__
+ cpu_set_t cset;
+ sched_getaffinity(0, sizeof(cset), &cset);
+ return CPU_COUNT(&cset);
+#else
+ return (int)sysconf(_SC_NPROCESSORS_ONLN);
+#endif
+}
diff --git a/src/apk_openssl.h b/src/apk_openssl.h
deleted file mode 100644
index 3867101..0000000
--- a/src/apk_openssl.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* apk_openssl.h - Alpine Package Keeper (APK)
- *
- * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
- * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
- * All rights reserved.
- *
- * SPDX-License-Identifier: GPL-2.0-only
- */
-
-#ifndef APK_SSL_COMPAT_H
-#define APK_SSL_COMPAT_H
-
-#include <openssl/opensslv.h>
-#include <openssl/crypto.h>
-#include <openssl/evp.h>
-#ifndef OPENSSL_NO_ENGINE
-#include <openssl/engine.h>
-#endif
-
-#if OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
-
-static inline EVP_MD_CTX *EVP_MD_CTX_new(void)
-{
- return EVP_MD_CTX_create();
-}
-
-static inline void EVP_MD_CTX_free(EVP_MD_CTX *mdctx)
-{
- return EVP_MD_CTX_destroy(mdctx);
-}
-
-#endif
-
-#endif
diff --git a/src/apk_package.h b/src/apk_package.h
index 0f7542c..d21f65e 100644
--- a/src/apk_package.h
+++ b/src/apk_package.h
@@ -15,6 +15,7 @@
#include "apk_io.h"
#include "apk_solver_data.h"
+struct adb_obj;
struct apk_database;
struct apk_name;
struct apk_provider;
@@ -30,12 +31,6 @@ struct apk_trust;
#define APK_SCRIPT_TRIGGER 6
#define APK_SCRIPT_MAX 7
-#define APK_SIGN_NONE 0
-#define APK_SIGN_VERIFY 1
-#define APK_SIGN_VERIFY_IDENTITY 2
-#define APK_SIGN_GENERATE 4
-#define APK_SIGN_VERIFY_AND_GENERATE 5
-
#define APK_DEP_IRRELEVANT 0x01
#define APK_DEP_SATISFIES 0x02
#define APK_DEP_CONFLICTS 0x04
@@ -45,36 +40,13 @@ struct apk_trust;
#define APK_FOREACH_DEP 0x80
#define APK_FOREACH_GENID_MASK 0xffffff00
-struct apk_sign_ctx {
- struct apk_trust *trust;
- int action;
- const EVP_MD *md;
- int num_signatures;
- int control_started : 1;
- int data_started : 1;
- int has_data_checksum : 1;
- int control_verified : 1;
- int data_verified : 1;
- int allow_untrusted : 1;
- char data_checksum[EVP_MAX_MD_SIZE];
- struct apk_checksum identity;
- EVP_MD_CTX *mdctx;
-
- struct {
- apk_blob_t data;
- EVP_PKEY *pkey;
- char *identity;
- } signature;
-};
-
struct apk_dependency {
struct apk_name *name;
apk_blob_t *version;
- unsigned broken : 1;
- unsigned repository_tag : 6;
- unsigned conflict : 1;
- unsigned result_mask : 4;
- unsigned fuzzy : 1;
+ uint8_t op;
+ uint16_t broken : 1; // solver state
+ uint16_t repository_tag : 6; // world dependency only: tag
+ uint16_t layer : 4; // solver sets for 'world' dependencies only
};
APK_ARRAY(apk_dependency_array, struct apk_dependency);
@@ -94,31 +66,36 @@ struct apk_installed_package {
unsigned broken_files : 1;
unsigned broken_script : 1;
unsigned broken_xattr : 1;
+ unsigned v3 : 1;
+ unsigned sha256_160 : 1;
};
struct apk_package {
apk_hash_node hash_node;
- unsigned int foreach_genid;
- union {
- struct apk_solver_package_state ss;
- int state_int;
- void *state_ptr;
- };
struct apk_name *name;
struct apk_installed_package *ipkg;
- apk_blob_t *version, *arch, *license;
- apk_blob_t *origin, *maintainer;
- char *url, *description, *commit;
- char *filename;
struct apk_dependency_array *depends, *install_if, *provides;
+ apk_blob_t *version;
size_t installed_size, size;
- time_t build_time;
+
+ union {
+ struct apk_solver_package_state ss;
+ int state_int;
+ };
+ unsigned int foreach_genid;
unsigned short provider_priority;
- unsigned repos : APK_MAX_REPOS;
- unsigned marked : 1;
- unsigned uninstallable : 1;
- unsigned cached_non_repository : 1;
+ unsigned short repos;
+ unsigned short filename_ndx;
+ unsigned char seen : 1;
+ unsigned char marked : 1;
+ unsigned char uninstallable : 1;
+ unsigned char cached_non_repository : 1;
+ unsigned char layer : 4;
struct apk_checksum csum;
+
+ time_t build_time;
+ apk_blob_t *arch, *license, *origin, *maintainer;
+ char *url, *description, *commit;
};
APK_ARRAY(apk_package_array, struct apk_package *);
@@ -126,38 +103,35 @@ APK_ARRAY(apk_package_array, struct apk_package *);
#define APK_PROVIDER_FROM_PROVIDES(pkg,p) (struct apk_provider){(pkg),(p)->version}
#define PKG_VER_FMT "%s-" BLOB_FMT
-#define PKG_VER_PRINTF(pkg) pkg->name->name, BLOB_PRINTF(*pkg->version)
+#define PKG_VER_PRINTF(pkg) (pkg)->name->name, BLOB_PRINTF(*(pkg)->version)
+#define PKG_VER_STRLEN(pkg) (strlen(pkg->name->name) + 1 + pkg->version->len)
#define PKG_FILE_FMT PKG_VER_FMT ".apk"
#define PKG_FILE_PRINTF(pkg) PKG_VER_PRINTF(pkg)
extern const char *apk_script_types[];
-void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action,
- struct apk_checksum *identity, struct apk_trust *trust);
-void apk_sign_ctx_free(struct apk_sign_ctx *ctx);
-int apk_sign_ctx_process_file(struct apk_sign_ctx *ctx,
- const struct apk_file_info *fi,
- struct apk_istream *is);
-int apk_sign_ctx_parse_pkginfo_line(void *ctx, apk_blob_t line);
-int apk_sign_ctx_verify_tar(void *ctx, const struct apk_file_info *fi,
- struct apk_istream *is);
-int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t blob);
-
+static inline int apk_dep_conflict(const struct apk_dependency *dep) { return !!(dep->op & APK_VERSION_CONFLICT); }
void apk_dep_from_pkg(struct apk_dependency *dep, struct apk_database *db,
struct apk_package *pkg);
-int apk_dep_is_materialized(struct apk_dependency *dep, struct apk_package *pkg);
-int apk_dep_is_provided(struct apk_dependency *dep, struct apk_provider *p);
+int apk_dep_is_materialized(const struct apk_dependency *dep, const struct apk_package *pkg);
+int apk_dep_is_provided(const struct apk_dependency *dep, const struct apk_provider *p);
int apk_dep_analyze(struct apk_dependency *dep, struct apk_package *pkg);
char *apk_dep_snprintf(char *buf, size_t n, struct apk_dependency *dep);
void apk_blob_push_dep(apk_blob_t *to, struct apk_database *, struct apk_dependency *dep);
void apk_blob_push_deps(apk_blob_t *to, struct apk_database *, struct apk_dependency_array *deps);
void apk_blob_pull_dep(apk_blob_t *from, struct apk_database *, struct apk_dependency *);
-void apk_blob_pull_deps(apk_blob_t *from, struct apk_database *, struct apk_dependency_array **);
+int apk_blob_pull_deps(apk_blob_t *from, struct apk_database *, struct apk_dependency_array **);
+int apk_deps_write_layer(struct apk_database *db, struct apk_dependency_array *deps,
+ struct apk_ostream *os, apk_blob_t separator, unsigned layer);
int apk_deps_write(struct apk_database *db, struct apk_dependency_array *deps,
struct apk_ostream *os, apk_blob_t separator);
+void apk_dep_from_adb(struct apk_dependency *dep, struct apk_database *db, struct adb_obj *d);
+void apk_deps_from_adb(struct apk_dependency_array **deps, struct apk_database *db, struct adb_obj *da);
+
+int apk_dep_parse(apk_blob_t spec, apk_blob_t *name, int *op, apk_blob_t *version);
void apk_deps_add(struct apk_dependency_array **depends, struct apk_dependency *dep);
void apk_deps_del(struct apk_dependency_array **deps, struct apk_name *name);
int apk_script_type(const char *name);
@@ -165,28 +139,39 @@ int apk_script_type(const char *name);
struct apk_package *apk_pkg_get_installed(struct apk_name *name);
struct apk_package *apk_pkg_new(void);
-int apk_pkg_read(struct apk_database *db, const char *name,
- struct apk_sign_ctx *ctx, struct apk_package **pkg);
+int apk_pkg_read(struct apk_database *db, const char *name, struct apk_package **pkg, int v3ok);
void apk_pkg_free(struct apk_package *pkg);
int apk_pkg_parse_name(apk_blob_t apkname, apk_blob_t *name, apk_blob_t *version);
int apk_pkg_add_info(struct apk_database *db, struct apk_package *pkg,
char field, apk_blob_t value);
+void apk_pkg_from_adb(struct apk_database *db, struct apk_package *pkg, struct adb_obj *pkginfo);
struct apk_installed_package *apk_pkg_install(struct apk_database *db, struct apk_package *pkg);
void apk_pkg_uninstall(struct apk_database *db, struct apk_package *pkg);
+int apk_ipkg_assign_script(struct apk_installed_package *ipkg, unsigned int type, apk_blob_t blob);
int apk_ipkg_add_script(struct apk_installed_package *ipkg,
struct apk_istream *is,
unsigned int type, unsigned int size);
-void apk_ipkg_run_script(struct apk_installed_package *ipkg, struct apk_database *db,
- unsigned int type, char **argv);
+int apk_ipkg_run_script(struct apk_installed_package *ipkg, struct apk_database *db,
+ unsigned int type, char **argv);
struct apk_package *apk_pkg_parse_index_entry(struct apk_database *db, apk_blob_t entry);
+int apk_pkg_write_index_header(struct apk_package *pkg, struct apk_ostream *os);
int apk_pkg_write_index_entry(struct apk_package *pkg, struct apk_ostream *os);
-int apk_pkg_version_compare(struct apk_package *a, struct apk_package *b);
+int apk_pkg_version_compare(const struct apk_package *a, const struct apk_package *b);
+int apk_pkg_cmp_display(const struct apk_package *a, const struct apk_package *b);
+
+enum {
+ APK_PKG_REPLACES_YES,
+ APK_PKG_REPLACES_NO,
+ APK_PKG_REPLACES_CONFLICT,
+};
+int apk_pkg_replaces_dir(const struct apk_package *a, const struct apk_package *b);
+int apk_pkg_replaces_file(const struct apk_package *a, const struct apk_package *b);
unsigned int apk_foreach_genid(void);
int apk_pkg_match_genid(struct apk_package *pkg, unsigned int match);
diff --git a/src/apk_pathbuilder.h b/src/apk_pathbuilder.h
index cabf51c..88e79f0 100644
--- a/src/apk_pathbuilder.h
+++ b/src/apk_pathbuilder.h
@@ -18,7 +18,7 @@ struct apk_pathbuilder {
};
int apk_pathbuilder_pushb(struct apk_pathbuilder *pb, apk_blob_t b);
-void apk_pathbuilder_pop(struct apk_pathbuilder *pb);
+void apk_pathbuilder_pop(struct apk_pathbuilder *pb, int);
static inline int apk_pathbuilder_setb(struct apk_pathbuilder *pb, apk_blob_t b)
diff --git a/src/apk_print.h b/src/apk_print.h
index a8e734a..65ad15c 100644
--- a/src/apk_print.h
+++ b/src/apk_print.h
@@ -10,9 +10,13 @@
#ifndef APK_PRINT_H
#define APK_PRINT_H
+#include <stdio.h>
#include "apk_blob.h"
+#define APK_EXIT_STATUS_MAX_SIZE 128
+
const char *apk_error_str(int error);
+int apk_exit_status_str(int status, char *buf, size_t sz);
int apk_get_human_size_unit(apk_blob_t b);
const char *apk_get_human_size(off_t size, off_t *dest);
@@ -26,7 +30,7 @@ struct apk_url_print {
void apk_url_parse(struct apk_url_print *, const char *);
#define URL_FMT "%.*s%s%s"
-#define URL_PRINTF(u) u.len_before_pw, u.url, u.pwmask, u.url_or_host
+#define URL_PRINTF(u) (int)u.len_before_pw, u.url, u.pwmask, u.url_or_host
struct apk_out {
int verbosity;
@@ -48,7 +52,8 @@ static inline int apk_out_verbosity(struct apk_out *out) { return out->verbosity
#define apk_dbg2(out, args...) do { if (apk_out_verbosity(out) >= 3) { apk_out_fmt(out, NULL, args); } } while (0)
void apk_out_reset(struct apk_out *);
-void apk_out_fmt(struct apk_out *, const char *prefix, const char *format, ...);
+void apk_out_fmt(struct apk_out *, const char *prefix, const char *format, ...)
+ __attribute__ ((format (printf, 3, 4)));
void apk_out_log_argv(struct apk_out *, char **argv);
struct apk_progress {
@@ -62,12 +67,17 @@ struct apk_progress {
void apk_print_progress(struct apk_progress *p, size_t done, size_t total);
struct apk_indent {
- struct apk_out *out;
- int x, indent;
+ FILE *f;
+ unsigned int x, indent, width;
};
+void apk_print_indented_init(struct apk_indent *i, struct apk_out *out, int err);
+void apk_print_indented_line(struct apk_indent *i, const char *fmt, ...);
+void apk_print_indented_group(struct apk_indent *i, int indent, const char *fmt, ...);
+void apk_print_indented_end(struct apk_indent *i);
int apk_print_indented(struct apk_indent *i, apk_blob_t blob);
void apk_print_indented_words(struct apk_indent *i, const char *text);
-void apk_print_indented_fmt(struct apk_indent *i, const char *fmt, ...);
+void apk_print_indented_fmt(struct apk_indent *i, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
#endif
diff --git a/src/apk_tar.h b/src/apk_tar.h
new file mode 100644
index 0000000..c3d951c
--- /dev/null
+++ b/src/apk_tar.h
@@ -0,0 +1,22 @@
+/* apk_tar.h - Alpine Package Keeper (APK)
+ *
+ * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
+ * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#ifndef APK_TAR
+#define APK_TAR
+
+#include "apk_io.h"
+
+int apk_tar_parse(struct apk_istream *,
+ apk_archive_entry_parser parser, void *ctx,
+ struct apk_id_cache *);
+int apk_tar_write_entry(struct apk_ostream *, const struct apk_file_info *ae,
+ const char *data);
+int apk_tar_write_padding(struct apk_ostream *, const struct apk_file_info *ae);
+
+#endif
diff --git a/src/apk_trust.h b/src/apk_trust.h
index 0f612f9..ee39f5a 100644
--- a/src/apk_trust.h
+++ b/src/apk_trust.h
@@ -23,12 +23,13 @@ struct apk_trust {
struct apk_digest_ctx dctx;
struct list_head trusted_key_list;
struct list_head private_key_list;
- int allow_untrusted : 1;
- int initialized : 1;
+ unsigned int allow_untrusted : 1;
+ unsigned int keys_loaded : 1;
};
-int apk_trust_init(struct apk_trust *trust, int keysfd, struct apk_string_array *);
+void apk_trust_init(struct apk_trust *trust);
void apk_trust_free(struct apk_trust *trust);
+int apk_trust_load_keys(struct apk_trust *trust, int keysfd);
struct apk_pkey *apk_trust_key_by_name(struct apk_trust *trust, const char *filename);
#endif
diff --git a/src/apk_version.h b/src/apk_version.h
index 0996207..153d04d 100644
--- a/src/apk_version.h
+++ b/src/apk_version.h
@@ -17,17 +17,17 @@
#define APK_VERSION_LESS 2
#define APK_VERSION_GREATER 4
#define APK_VERSION_FUZZY 8
+#define APK_VERSION_CONFLICT 16
#define APK_DEPMASK_ANY (APK_VERSION_EQUAL|APK_VERSION_LESS|\
- APK_VERSION_GREATER|APK_VERSION_FUZZY)
+ APK_VERSION_GREATER)
#define APK_DEPMASK_CHECKSUM (APK_VERSION_LESS|APK_VERSION_GREATER)
-const char *apk_version_op_string(int result_mask);
+const char *apk_version_op_string(int op);
int apk_version_result_mask(const char *op);
int apk_version_result_mask_blob(apk_blob_t op);
int apk_version_validate(apk_blob_t ver);
-int apk_version_compare_blob_fuzzy(apk_blob_t a, apk_blob_t b, int fuzzy);
-int apk_version_compare_blob(apk_blob_t a, apk_blob_t b);
-int apk_version_compare(const char *str1, const char *str2);
+int apk_version_compare(apk_blob_t a, apk_blob_t b);
+int apk_version_match(apk_blob_t a, int op, apk_blob_t b);
#endif
diff --git a/src/apk_xattr.h b/src/apk_xattr.h
new file mode 100644
index 0000000..595fe68
--- /dev/null
+++ b/src/apk_xattr.h
@@ -0,0 +1,30 @@
+#pragma once
+#include <unistd.h>
+#include <sys/xattr.h>
+
+static inline int apk_fsetxattr(int fd, const char *name, void *value, size_t size)
+{
+#ifdef __APPLE__
+ return fsetxattr(fd, name, value, size, 0, 0);
+#else
+ return fsetxattr(fd, name, value, size, 0);
+#endif
+}
+
+static inline ssize_t apk_fgetxattr(int fd, const char *name, void *value, size_t size)
+{
+#ifdef __APPLE__
+ return fgetxattr(fd, name, value, size, 0, 0);
+#else
+ return fgetxattr(fd, name, value, size);
+#endif
+}
+
+static inline ssize_t apk_flistxattr(int fd, char *namebuf, size_t size)
+{
+#ifdef __APPLE__
+ return flistxattr(fd, namebuf, size, 0);
+#else
+ return flistxattr(fd, namebuf, size);
+#endif
+}
diff --git a/src/app_adbsign.c b/src/app_adbsign.c
index d903c60..67bb83f 100644
--- a/src/app_adbsign.c
+++ b/src/app_adbsign.c
@@ -14,8 +14,8 @@ struct sign_ctx {
struct apk_ostream *os;
struct adb_verify_ctx vfy;
- int reset_signatures : 1;
- int signatures_written : 1;
+ unsigned int reset_signatures : 1;
+ unsigned int signatures_written : 1;
};
#define ADBSIGN_OPTIONS(OPT) \
@@ -78,15 +78,15 @@ static int adbsign_main(void *pctx, struct apk_ctx *ac, struct apk_string_array
{
struct apk_out *out = &ac->out;
struct sign_ctx *ctx = pctx;
- adb_comp_t comp;
+ struct adb_compression_spec spec;
char **arg;
int r;
ctx->ac = ac;
foreach_array_item(arg, args) {
memset(&ctx->vfy, 0, sizeof ctx->vfy);
- struct apk_istream *is = adb_decompress(apk_istream_from_file_mmap(AT_FDCWD, *arg), &comp);
- ctx->os = adb_compress(apk_ostream_to_file(AT_FDCWD, *arg, 0644), comp);
+ struct apk_istream *is = adb_decompress(apk_istream_from_file_mmap(AT_FDCWD, *arg), &spec);
+ ctx->os = adb_compress(apk_ostream_to_file(AT_FDCWD, *arg, 0644), &spec);
apk_ostream_cancel(ctx->os, adb_m_process(&ctx->db, is, 0, 0, process_block));
apk_ostream_cancel(ctx->os, process_signatures(ctx));
adb_free(&ctx->db);
diff --git a/src/app_add.c b/src/app_add.c
index 559475e..b19125c 100644
--- a/src/app_add.c
+++ b/src/app_add.c
@@ -18,14 +18,14 @@
struct add_ctx {
const char *virtpkg;
unsigned short solver_flags;
- unsigned short extract_flags;
};
#define ADD_OPTIONS(OPT) \
OPT(OPT_ADD_initdb, "initdb") \
OPT(OPT_ADD_latest, APK_OPT_SH("l") "latest") \
- OPT(OPT_ADD_no_chown, "no-chown") \
+ OPT(OPT_ADD_no_chown, "no-chown") \
OPT(OPT_ADD_upgrade, APK_OPT_SH("u") "upgrade") \
+ OPT(OPT_ADD_usermode, "usermode") \
OPT(OPT_ADD_virtual, APK_OPT_ARG APK_OPT_SH("t") "virtual")
APK_OPT_APPLET(option_desc, ADD_OPTIONS);
@@ -41,8 +41,9 @@ static int option_parse_applet(void *ctx, struct apk_ctx *ac, int opt, const cha
case OPT_ADD_latest:
actx->solver_flags |= APK_SOLVERF_LATEST;
break;
+ case OPT_ADD_usermode:
case OPT_ADD_no_chown:
- actx->extract_flags |= APK_EXTRACTF_NO_CHOWN;
+ ac->open_flags |= APK_OPENF_USERMODE;
break;
case OPT_ADD_upgrade:
actx->solver_flags |= APK_SOLVERF_UPGRADE;
@@ -78,31 +79,26 @@ static int non_repository_check(struct apk_database *db)
return 1;
}
-static struct apk_package *create_virtual_package(struct apk_database *db, struct apk_name *name)
+static struct apk_package *create_virtual_package(struct apk_database *db, struct apk_dependency *dep)
{
- char ver[32];
struct apk_package *virtpkg;
struct apk_digest_ctx dctx;
struct apk_digest d;
- struct tm tm;
- time_t now = time(NULL);
pid_t pid = getpid();
- gmtime_r(&now, &tm);
- strftime(ver, sizeof ver, "%Y%m%d.%H%M%S", &tm);
-
virtpkg = apk_pkg_new();
if (virtpkg == NULL) return 0;
- virtpkg->name = name;
- virtpkg->version = apk_atomize_dup(&db->atoms, APK_BLOB_STR(ver));
+ virtpkg->name = dep->name;
+ virtpkg->version = dep->version;
virtpkg->description = strdup("virtual meta package");
virtpkg->arch = apk_atomize(&db->atoms, APK_BLOB_STR("noarch"));
+ virtpkg->repos |= BIT(APK_REPOSITORY_CACHED);
apk_digest_ctx_init(&dctx, APK_DIGEST_SHA1);
- apk_digest_ctx_update(&dctx, &tm, sizeof tm);
apk_digest_ctx_update(&dctx, &pid, sizeof pid);
apk_digest_ctx_update(&dctx, virtpkg->name->name, strlen(virtpkg->name->name) + 1);
+ apk_digest_ctx_update(&dctx, dep->version->ptr, dep->version->len);
apk_digest_ctx_final(&dctx, &d);
apk_digest_ctx_free(&dctx);
apk_checksum_from_digest(&virtpkg->csum, &d);
@@ -110,6 +106,17 @@ static struct apk_package *create_virtual_package(struct apk_database *db, struc
return virtpkg;
}
+static apk_blob_t *generate_version(struct apk_database *db)
+{
+ char ver[32];
+ struct tm tm;
+ time_t now = time(NULL);
+
+ gmtime_r(&now, &tm);
+ strftime(ver, sizeof ver, "%Y%m%d.%H%M%S", &tm);
+ return apk_atomize_dup(&db->atoms, APK_BLOB_STR(ver));
+}
+
static int add_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *args)
{
struct apk_out *out = &ac->out;
@@ -123,29 +130,36 @@ static int add_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *args
apk_dependency_array_copy(&world, db->world);
- if (getuid() != 0 || (actx->extract_flags & APK_EXTRACTF_NO_CHOWN))
- db->extract_flags |= APK_EXTRACTF_NO_CHOWN;
-
if (actx->virtpkg) {
apk_blob_t b = APK_BLOB_STR(actx->virtpkg);
apk_blob_pull_dep(&b, db, &virtdep);
- if (APK_BLOB_IS_NULL(b) || virtdep.conflict ||
- virtdep.result_mask != APK_DEPMASK_ANY ||
- virtdep.version != &apk_atom_null) {
- apk_err(out, "%s: bad package specifier");
+
+ if (APK_BLOB_IS_NULL(b) || apk_dep_conflict(&virtdep) ||
+ (virtdep.name->name[0] != '.' && non_repository_check(db)))
+ goto bad_spec;
+
+ switch (virtdep.op) {
+ case APK_DEPMASK_ANY:
+ if (virtdep.version != &apk_atom_null) goto bad_spec;
+ virtdep.op = APK_VERSION_EQUAL;
+ virtdep.version = generate_version(db);
+ break;
+ case APK_VERSION_EQUAL:
+ if (virtdep.version == &apk_atom_null) goto bad_spec;
+ break;
+ default:
+ bad_spec:
+ apk_err(out, "%s: bad package specifier", actx->virtpkg);
return -1;
}
- if (virtdep.name->name[0] != '.' && non_repository_check(db))
- return -1;
- virtpkg = create_virtual_package(db, virtdep.name);
+ virtpkg = create_virtual_package(db, &virtdep);
if (!virtpkg) {
apk_err(out, "Failed to allocate virtual meta package");
return -1;
}
- virtdep.result_mask = APK_VERSION_EQUAL;
- virtdep.version = virtpkg->version;
+ if (!args->num) apk_warn(out, "creating empty virtual package");
}
foreach_array_item(parg, args) {
@@ -153,15 +167,11 @@ static int add_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *args
if (strstr(*parg, ".apk") != NULL) {
struct apk_package *pkg = NULL;
- struct apk_sign_ctx sctx;
if (non_repository_check(db))
return -1;
- apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY_AND_GENERATE,
- NULL, apk_ctx_get_trust(ac));
- r = apk_pkg_read(db, *parg, &sctx, &pkg);
- apk_sign_ctx_free(&sctx);
+ r = apk_pkg_read(db, *parg, &pkg, TRUE);
if (r != 0) {
apk_err(out, "%s: %s", *parg, apk_error_str(r));
return -1;
@@ -205,6 +215,7 @@ static int add_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *args
static struct apk_applet apk_add = {
.name = "add",
.open_flags = APK_OPENF_WRITE,
+ .remove_empty_arguments = 1,
.context_size = sizeof(struct add_ctx),
.optgroups = { &optgroup_global, &optgroup_commit, &optgroup_applet },
.main = add_main,
diff --git a/src/app_audit.c b/src/app_audit.c
index 09d67e0..c99df91 100644
--- a/src/app_audit.c
+++ b/src/app_audit.c
@@ -19,49 +19,98 @@
#include "apk_database.h"
#include "apk_print.h"
-/* Use (unused) highest bit of mode_t as seen flag of our internal
- * database file entries */
-#define S_SEENFLAG 0x80000000
-
enum {
MODE_BACKUP = 0,
- MODE_SYSTEM
+ MODE_SYSTEM,
+ MODE_FULL,
};
struct audit_ctx {
+ struct apk_istream blob_istream;
int verbosity;
- unsigned mode : 1;
+ unsigned mode : 2;
unsigned recursive : 1;
unsigned check_permissions : 1;
unsigned packages_only : 1;
+ unsigned ignore_busybox_symlinks : 1;
+ unsigned details : 1;
};
#define AUDIT_OPTIONS(OPT) \
OPT(OPT_AUDIT_backup, "backup") \
OPT(OPT_AUDIT_check_permissions, "check-permissions") \
+ OPT(OPT_AUDIT_details, "details") \
+ OPT(OPT_AUDIT_full, "full") \
+ OPT(OPT_AUDIT_ignore_busybox_symlinks, "ignore-busybox-symlinks") \
OPT(OPT_AUDIT_packages, "packages") \
+ OPT(OPT_AUDIT_protected_paths, APK_OPT_ARG "protected-paths") \
OPT(OPT_AUDIT_recursive, APK_OPT_SH("r") "recursive") \
OPT(OPT_AUDIT_system, "system")
APK_OPT_APPLET(option_desc, AUDIT_OPTIONS);
+static int protected_paths_istream(struct apk_ctx *ac, struct apk_istream *is)
+{
+ if (ac->protected_paths) apk_istream_close(ac->protected_paths);
+ if (IS_ERR(is)) {
+ ac->protected_paths = NULL;
+ return PTR_ERR(is);
+ }
+ ac->protected_paths = is;
+ return 0;
+}
+
static int option_parse_applet(void *applet_ctx, struct apk_ctx *ac, int opt, const char *optarg)
{
struct audit_ctx *actx = (struct audit_ctx *) applet_ctx;
+ struct apk_out *out = &ac->out;
+ int r;
switch (opt) {
case OPT_AUDIT_backup:
actx->mode = MODE_BACKUP;
break;
+ case OPT_AUDIT_full:
+ actx->mode = MODE_FULL;
+ protected_paths_istream(ac,
+ apk_istream_from_blob(&actx->blob_istream,
+ APK_BLOB_STRLIT(
+ "+etc\n"
+ "@etc/init.d\n"
+ "-dev\n"
+ "-home\n"
+ "-lib/apk\n"
+ "-lib/rc/cache\n"
+ "-proc\n"
+ "-root\n"
+ "-run\n"
+ "-sys\n"
+ "-tmp\n"
+ "-var\n"
+ )));
+ break;
case OPT_AUDIT_system:
actx->mode = MODE_SYSTEM;
break;
case OPT_AUDIT_check_permissions:
actx->check_permissions = 1;
break;
+ case OPT_AUDIT_details:
+ actx->details = 1;
+ break;
+ case OPT_AUDIT_ignore_busybox_symlinks:
+ actx->ignore_busybox_symlinks = 1;
+ break;
case OPT_AUDIT_packages:
actx->packages_only = 1;
break;
+ case OPT_AUDIT_protected_paths:
+ r = protected_paths_istream(ac, apk_istream_from_file(AT_FDCWD, optarg));
+ if (r) {
+ apk_err(out, "unable to read protected path file: %s: %s", optarg, apk_error_str(r));
+ return r;
+ }
+ break;
case OPT_AUDIT_recursive:
actx->recursive = 1;
break;
@@ -87,38 +136,43 @@ struct audit_tree_ctx {
static int audit_file(struct audit_ctx *actx,
struct apk_database *db,
struct apk_db_file *dbf,
- int dirfd, const char *name)
+ int dirfd, const char *name,
+ struct apk_file_info *fi)
{
- struct apk_file_info fi;
+ int digest_type = APK_DIGEST_SHA256;
+ int xattr_csum_type = APK_CHECKSUM_DEFAULT;
int rv = 0;
- if (dbf == NULL)
- return 'A';
-
- dbf->audited = 1;
+ if (dbf) {
+ digest_type = apk_dbf_digest(dbf);
+ xattr_csum_type = dbf->acl->xattr_csum.type ?: APK_CHECKSUM_DEFAULT;
+ } else {
+ if (!actx->details) return 'A';
+ }
if (apk_fileinfo_get(dirfd, name,
APK_FI_NOFOLLOW |
- APK_FI_XATTR_CSUM(dbf->acl->xattr_csum.type ?: APK_CHECKSUM_DEFAULT) |
- APK_FI_CSUM(dbf->csum.type),
- &fi, &db->atoms) != 0)
- return -EPERM;
+ APK_FI_XATTR_CSUM(xattr_csum_type) |
+ APK_FI_DIGEST(digest_type),
+ fi, &db->atoms) != 0)
+ return 'e';
+
+ if (!dbf) return 'A';
if (dbf->csum.type != APK_CHECKSUM_NONE &&
- apk_digest_cmp_csum(&fi.digest, &dbf->csum) != 0)
+ apk_digest_cmp_csum(&fi->digest, &dbf->csum) != 0)
rv = 'U';
- else if (!S_ISLNK(fi.mode) && !dbf->diri->pkg->ipkg->broken_xattr &&
- apk_digest_cmp_csum(&fi.xattr_digest, &dbf->acl->xattr_csum) != 0)
+ else if (!S_ISLNK(fi->mode) && !dbf->diri->pkg->ipkg->broken_xattr &&
+ apk_digest_cmp_csum(&fi->xattr_digest, &dbf->acl->xattr_csum) != 0)
rv = 'x';
- else if (S_ISLNK(fi.mode) && dbf->csum.type == APK_CHECKSUM_NONE)
+ else if (S_ISLNK(fi->mode) && dbf->csum.type == APK_CHECKSUM_NONE)
rv = 'U';
else if (actx->check_permissions) {
- if ((fi.mode & 07777) != (dbf->acl->mode & 07777))
+ if ((fi->mode & 07777) != (dbf->acl->mode & 07777))
rv = 'M';
- else if (fi.uid != dbf->acl->uid || fi.gid != dbf->acl->gid)
+ else if (fi->uid != dbf->acl->uid || fi->gid != dbf->acl->gid)
rv = 'M';
}
- apk_fileinfo_free(&fi);
return rv;
}
@@ -128,32 +182,46 @@ static int audit_directory(struct audit_ctx *actx,
struct apk_db_dir *dbd,
struct apk_file_info *fi)
{
- if (dbd != NULL) dbd->mode |= S_SEENFLAG;
+ if (dbd != NULL) dbd->modified = 1;
if (dbd == NULL || dbd->refs == 1)
return actx->recursive ? 'd' : 'D';
- if (actx->check_permissions &&
- ((dbd->mode & ~S_SEENFLAG) || dbd->uid || dbd->gid)) {
- if ((fi->mode & 07777) != (dbd->mode & 07777))
+ struct apk_db_acl *acl = dbd->owner->acl;
+ if (actx->check_permissions && dbd->modified) {
+ if ((fi->mode & 07777) != (acl->mode & 07777))
return 'm';
- if (fi->uid != dbd->uid || fi->gid != dbd->gid)
+ if (fi->uid != acl->uid || fi->gid != acl->gid)
return 'm';
}
return 0;
}
+static const char *format_checksum(const apk_blob_t csum, apk_blob_t b)
+{
+ const char *ret = b.ptr;
+ if (csum.len == 0) return "";
+ apk_blob_push_blob(&b, APK_BLOB_STR(" hash="));
+ apk_blob_push_hexdump(&b, csum);
+ apk_blob_push_blob(&b, APK_BLOB_PTR_LEN("", 1));
+ return ret;
+}
+
static void report_audit(struct audit_ctx *actx,
- char reason, apk_blob_t bfull, struct apk_package *pkg)
+ char reason, apk_blob_t bfull,
+ struct apk_db_dir *dir,
+ struct apk_db_file *file,
+ struct apk_file_info *fi)
{
+ struct apk_package *pkg = file ? file->diri->pkg : NULL;
+ char csum_buf[8+2*APK_DIGEST_MAX_LENGTH];
int verbosity = actx->verbosity;
if (!reason) return;
if (actx->packages_only) {
- if (pkg == NULL || pkg->state_int != 0)
- return;
+ if (!pkg || pkg->state_int != 0) return;
pkg->state_int = 1;
if (verbosity < 1)
printf("%s\n", pkg->name->name);
@@ -161,8 +229,37 @@ static void report_audit(struct audit_ctx *actx,
printf(PKG_VER_FMT "\n", PKG_VER_PRINTF(pkg));
} else if (verbosity < 1) {
printf(BLOB_FMT "\n", BLOB_PRINTF(bfull));
- } else
+ } else {
+ if (actx->details) {
+ struct apk_db_acl *acl = NULL;
+ if (file) acl = file->acl;
+ else if (dir && reason != 'D' && reason != 'd') acl = dir->owner->acl;
+ if (acl) printf("- mode=%o uid=%d gid=%d%s\n",
+ acl->mode & 07777, acl->uid, acl->gid,
+ file ? format_checksum(APK_BLOB_CSUM(file->csum), APK_BLOB_BUF(csum_buf)) : "");
+ if (fi) printf("+ mode=%o uid=%d gid=%d%s\n",
+ fi->mode & 07777, fi->uid, fi->gid,
+ format_checksum(APK_DIGEST_BLOB(fi->digest), APK_BLOB_BUF(csum_buf)));
+ }
printf("%c " BLOB_FMT "\n", reason, BLOB_PRINTF(bfull));
+ }
+}
+
+static int determine_file_protect_mode(struct apk_db_dir *dir, const char *name)
+{
+ struct apk_protected_path *ppath;
+ int protect_mode = dir->protect_mode;
+
+ /* inherit file's protection mask */
+ foreach_array_item(ppath, dir->protected_paths) {
+ char *slash = strchr(ppath->relative_pattern, '/');
+ if (slash == NULL) {
+ if (fnmatch(ppath->relative_pattern, name, FNM_PATHNAME) != 0)
+ continue;
+ protect_mode = ppath->protect_mode;
+ }
+ }
+ return protect_mode;
}
static int audit_directory_tree_item(void *ctx, int dirfd, const char *name)
@@ -174,43 +271,54 @@ static int audit_directory_tree_item(void *ctx, int dirfd, const char *name)
struct audit_ctx *actx = atctx->actx;
struct apk_database *db = atctx->db;
struct apk_db_dir *dir = atctx->dir, *child = NULL;
+ struct apk_db_file *dbf;
struct apk_file_info fi;
int reason = 0;
if (bdir.len + bent.len + 1 >= sizeof(atctx->path)) return 0;
- if (apk_fileinfo_get(dirfd, name, APK_FI_NOFOLLOW, &fi, &db->atoms) < 0) return 0;
memcpy(&atctx->path[atctx->pathlen], bent.ptr, bent.len);
atctx->pathlen += bent.len;
bfull = APK_BLOB_PTR_LEN(atctx->path, atctx->pathlen);
+ if (apk_fileinfo_get(dirfd, name, APK_FI_NOFOLLOW, &fi, &db->atoms) < 0) {
+ dbf = apk_db_file_query(db, bdir, bent);
+ if (dbf) dbf->audited = 1;
+ report_audit(actx, 'e', bfull, NULL, dbf, NULL);
+ goto done;
+ }
+
if (S_ISDIR(fi.mode)) {
int recurse = TRUE;
- if (actx->mode == MODE_BACKUP) {
+ switch (actx->mode) {
+ case MODE_BACKUP:
child = apk_db_dir_get(db, bfull);
if (!child->has_protected_children)
recurse = FALSE;
- if (child->protect_mode == APK_PROTECT_NONE)
+ if (apk_protect_mode_none(child->protect_mode))
goto recurse_check;
- } else {
+ break;
+ case MODE_SYSTEM:
child = apk_db_dir_query(db, bfull);
- if (child == NULL)
- goto done;
+ if (child == NULL) goto done;
child = apk_db_dir_ref(child);
+ break;
+ case MODE_FULL:
+ child = apk_db_dir_get(db, bfull);
+ if (child->protect_mode == APK_PROTECT_NONE) break;
+ goto recurse_check;
}
reason = audit_directory(actx, db, child, &fi);
- if (reason < 0)
- goto done;
recurse_check:
atctx->path[atctx->pathlen++] = '/';
bfull.len++;
- report_audit(actx, reason, bfull, NULL);
+ report_audit(actx, reason, bfull, child, NULL, &fi);
if (reason != 'D' && recurse) {
atctx->dir = child;
- reason = apk_dir_foreach_file(
+ apk_dir_foreach_file(
openat(dirfd, name, O_RDONLY|O_CLOEXEC),
audit_directory_tree_item, atctx);
atctx->dir = dir;
@@ -218,49 +326,62 @@ recurse_check:
bfull.len--;
atctx->pathlen--;
} else {
- struct apk_db_file *dbf;
- struct apk_protected_path *ppath;
- int protect_mode = dir->protect_mode;
-
- /* inherit file's protection mask */
- foreach_array_item(ppath, dir->protected_paths) {
- char *slash = strchr(ppath->relative_pattern, '/');
- if (slash == NULL) {
- if (fnmatch(ppath->relative_pattern, name, FNM_PATHNAME) != 0)
- continue;
- protect_mode = ppath->protect_mode;
- }
- }
+ int protect_mode = determine_file_protect_mode(dir, name);
+
+ dbf = apk_db_file_query(db, bdir, bent);
+ if (dbf) dbf->audited = 1;
- if (actx->mode == MODE_BACKUP) {
+ switch (actx->mode) {
+ case MODE_FULL:
+ switch (protect_mode) {
+ case APK_PROTECT_NONE:
+ break;
+ case APK_PROTECT_SYMLINKS_ONLY:
+ if (S_ISLNK(fi.mode)) goto done;
+ break;
+ case APK_PROTECT_IGNORE:
+ case APK_PROTECT_ALL:
+ case APK_PROTECT_CHANGED:
+ goto done;
+ }
+ break;
+ case MODE_BACKUP:
switch (protect_mode) {
case APK_PROTECT_NONE:
+ case APK_PROTECT_IGNORE:
goto done;
case APK_PROTECT_CHANGED:
break;
case APK_PROTECT_SYMLINKS_ONLY:
- if (!S_ISLNK(fi.mode))
- goto done;
+ if (!S_ISLNK(fi.mode)) goto done;
break;
case APK_PROTECT_ALL:
reason = 'A';
break;
}
+ if ((!dbf || reason == 'A') &&
+ apk_blob_ends_with(bent, APK_BLOB_STR(".apk-new")))
+ goto done;
+ break;
+ case MODE_SYSTEM:
+ if (!dbf || !apk_protect_mode_none(protect_mode)) goto done;
+ break;
}
- dbf = apk_db_file_query(db, bdir, bent);
- if (reason == 0)
- reason = audit_file(actx, db, dbf, dirfd, name);
- if (reason < 0)
- goto done;
- if (actx->mode == MODE_SYSTEM &&
- (reason == 'A' || protect_mode != APK_PROTECT_NONE))
- goto done;
- if (actx->mode == MODE_BACKUP &&
- reason == 'A' &&
- apk_blob_ends_with(bent, APK_BLOB_STR(".apk-new")))
- goto done;
- report_audit(actx, reason, bfull, dbf ? dbf->diri->pkg : NULL);
+ if (!dbf && actx->ignore_busybox_symlinks && S_ISLNK(fi.mode)) {
+ char target[20];
+ ssize_t n;
+ n = readlinkat(dirfd, name, target, sizeof target);
+ if (n == 12 && memcmp(target, "/bin/busybox", 12) == 0)
+ goto done;
+ if (n == 11 && memcmp(target, "/bin/bbsuid", 11) == 0)
+ goto done;
+ if (n == 19 && memcmp(target, "/bin/busybox-extras", 19) == 0)
+ goto done;
+ }
+ if (!reason) reason = audit_file(actx, db, dbf, dirfd, name, &fi);
+ report_audit(actx, reason, bfull, NULL, dbf, &fi);
+ apk_fileinfo_free(&fi);
}
done:
@@ -281,7 +402,7 @@ static int audit_directory_tree(struct audit_tree_ctx *atctx, int dirfd)
path.len--;
atctx->dir = apk_db_dir_get(atctx->db, path);
- atctx->dir->mode |= S_SEENFLAG;
+ atctx->dir->modified = 1;
r = apk_dir_foreach_file(dirfd, audit_directory_tree_item, atctx);
apk_db_dir_unref(atctx->db, atctx->dir, FALSE);
@@ -299,11 +420,11 @@ static int audit_missing_files(apk_hash_item item, void *pctx)
if (file->audited) return 0;
dir = file->diri->dir;
- if (dir->mode & S_SEENFLAG) {
- len = snprintf(path, sizeof(path), DIR_FILE_FMT, DIR_FILE_PRINTF(dir, file));
- report_audit(actx, 'X', APK_BLOB_PTR_LEN(path, len), file->diri->pkg);
- }
+ if (!dir->modified) return 0;
+ if (determine_file_protect_mode(dir, file->name) == APK_PROTECT_IGNORE) return 0;
+ len = snprintf(path, sizeof(path), DIR_FILE_FMT, DIR_FILE_PRINTF(dir, file));
+ report_audit(actx, 'X', APK_BLOB_PTR_LEN(path, len), NULL, file, NULL);
return 0;
}
@@ -316,6 +437,11 @@ static int audit_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *ar
char **parg, *arg;
int r = 0;
+ if (db->usermode) {
+ apk_err(out, "audit does not support usermode!");
+ return -ENOSYS;
+ }
+
actx->verbosity = apk_out_verbosity(&db->ctx->out);
atctx.db = db;
atctx.actx = actx;
@@ -328,7 +454,7 @@ static int audit_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *ar
foreach_array_item(parg, args) {
arg = *parg;
if (arg[0] != '/') {
- apk_warn(out, "%s: relative path skipped.\n", arg);
+ apk_warn(out, "%s: relative path skipped.", arg);
continue;
}
arg++;
@@ -340,7 +466,7 @@ static int audit_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *ar
r |= audit_directory_tree(&atctx, openat(db->root_fd, arg, O_RDONLY|O_CLOEXEC));
}
}
- if (actx->mode == MODE_SYSTEM)
+ if (actx->mode == MODE_SYSTEM || actx->mode == MODE_FULL)
apk_hash_foreach(&db->installed.files, audit_missing_files, ctx);
return r;
diff --git a/src/app_cache.c b/src/app_cache.c
index db562cb..2b9ecdc 100644
--- a/src/app_cache.c
+++ b/src/app_cache.c
@@ -26,11 +26,16 @@
struct cache_ctx {
unsigned short solver_flags;
+ unsigned short add_dependencies : 1;
};
#define CACHE_OPTIONS(OPT) \
+ OPT(OPT_CACHE_add_dependencies, "add-dependencies") \
+ OPT(OPT_CACHE_available, APK_OPT_SH("a") "available") \
+ OPT(OPT_CACHE_ignore_conflict, "ignore-conflict") \
OPT(OPT_CACHE_latest, APK_OPT_SH("l") "latest") \
- OPT(OPT_CACHE_upgrade, APK_OPT_SH("u") "upgrade")
+ OPT(OPT_CACHE_upgrade, APK_OPT_SH("u") "upgrade") \
+ OPT(OPT_CACHE_simulate, APK_OPT_SH("s") "simulate") \
APK_OPT_APPLET(option_desc, CACHE_OPTIONS);
@@ -39,12 +44,24 @@ static int option_parse_applet(void *ctx, struct apk_ctx *ac, int opt, const cha
struct cache_ctx *cctx = (struct cache_ctx *) ctx;
switch (opt) {
- case OPT_CACHE_upgrade:
- cctx->solver_flags |= APK_SOLVERF_UPGRADE;
+ case OPT_CACHE_add_dependencies:
+ cctx->add_dependencies = 1;
+ break;
+ case OPT_CACHE_available:
+ cctx->solver_flags |= APK_SOLVERF_AVAILABLE;
+ break;
+ case OPT_CACHE_ignore_conflict:
+ cctx->solver_flags |= APK_SOLVERF_IGNORE_CONFLICT;
break;
case OPT_CACHE_latest:
cctx->solver_flags |= APK_SOLVERF_LATEST;
break;
+ case OPT_CACHE_upgrade:
+ cctx->solver_flags |= APK_SOLVERF_UPGRADE;
+ break;
+ case OPT_CACHE_simulate:
+ ac->flags |= APK_SIMULATE;
+ break;
default:
return -ENOTSUP;
}
@@ -67,17 +84,32 @@ static void progress_cb(void *ctx, size_t bytes_done)
apk_print_progress(&prog->prog, prog->done + bytes_done, prog->total);
}
-static int cache_download(struct cache_ctx *cctx, struct apk_database *db)
+static int cache_download(struct cache_ctx *cctx, struct apk_database *db, struct apk_string_array *args)
{
struct apk_out *out = &db->ctx->out;
struct apk_changeset changeset = {};
struct apk_change *change;
struct apk_package *pkg;
struct apk_repository *repo;
+ struct apk_dependency_array *deps;
+ struct apk_dependency dep;
struct progress prog = { .prog = db->ctx->progress };
- int r, ret = 0;
-
- r = apk_solver_solve(db, cctx->solver_flags, db->world, &changeset);
+ int i, r, ret = 0;
+
+ apk_dependency_array_init(&deps);
+ if (args->num == 1 || cctx->add_dependencies)
+ apk_dependency_array_copy(&deps, db->world);
+ for (i = 1; i < args->num; i++) {
+ apk_blob_t b = APK_BLOB_STR(args->item[i]);
+ apk_blob_pull_dep(&b, db, &dep);
+ if (APK_BLOB_IS_NULL(b)) {
+ apk_err(out, "bad dependency: %s", args->item[i]);
+ return -EINVAL;
+ }
+ *apk_dependency_array_add(&deps) = dep;
+ }
+ r = apk_solver_solve(db, cctx->solver_flags, deps, &changeset);
+ apk_dependency_array_free(&deps);
if (r < 0) {
apk_err(out, "Unable to select packages. Run apk fix.");
return r;
@@ -85,21 +117,22 @@ static int cache_download(struct cache_ctx *cctx, struct apk_database *db)
foreach_array_item(change, changeset.changes) {
pkg = change->new_pkg;
- if ((pkg != NULL) && !(pkg->repos & db->local_repos))
- prog.total += pkg->size;
+ if (!pkg || (pkg->repos & db->local_repos) || !pkg->installed_size)
+ continue;
+ if (!apk_db_select_repo(db, pkg)) continue;
+ prog.total += pkg->size;
}
foreach_array_item(change, changeset.changes) {
pkg = change->new_pkg;
- if ((pkg == NULL) || (pkg->repos & db->local_repos))
+ if (!pkg || (pkg->repos & db->local_repos) || !pkg->installed_size)
continue;
repo = apk_db_select_repo(db, pkg);
if (repo == NULL)
continue;
- r = apk_cache_download(db, repo, pkg, APK_SIGN_VERIFY_IDENTITY, 0,
- progress_cb, &prog);
+ r = apk_cache_download(db, repo, pkg, 0, progress_cb, &prog);
if (r && r != -EALREADY) {
apk_err(out, PKG_VER_FMT ": %s", PKG_VER_PRINTF(pkg), apk_error_str(r));
ret++;
@@ -110,20 +143,23 @@ static int cache_download(struct cache_ctx *cctx, struct apk_database *db)
return ret;
}
-static void cache_clean_item(struct apk_database *db, int dirfd, const char *name, struct apk_package *pkg)
+static void cache_clean_item(struct apk_database *db, int static_cache, int dirfd, const char *name, struct apk_package *pkg)
{
struct apk_out *out = &db->ctx->out;
char tmp[PATH_MAX];
apk_blob_t b;
int i;
- if (strcmp(name, "installed") == 0) return;
-
- if (pkg) {
- if ((db->ctx->flags & APK_PURGE) && pkg->ipkg == NULL) goto delete;
- if (pkg->repos & db->local_repos & ~BIT(APK_REPOSITORY_CACHED)) goto delete;
- if (pkg->ipkg == NULL && !(pkg->repos & ~BIT(APK_REPOSITORY_CACHED))) goto delete;
- return;
+ if (!static_cache) {
+ if (strcmp(name, "installed") == 0) return;
+ if (pkg) {
+ if (db->ctx->flags & APK_PURGE) {
+ if (db->permanent || !pkg->ipkg) goto delete;
+ }
+ if (pkg->repos & db->local_repos & ~BIT(APK_REPOSITORY_CACHED)) goto delete;
+ if (pkg->ipkg == NULL && !(pkg->repos & ~BIT(APK_REPOSITORY_CACHED))) goto delete;
+ return;
+ }
}
b = APK_BLOB_STR(name);
@@ -143,41 +179,48 @@ delete:
static int cache_clean(struct apk_database *db)
{
- return apk_db_cache_foreach_item(db, cache_clean_item);
+ if (apk_db_cache_active(db)) {
+ int r = apk_db_cache_foreach_item(db, cache_clean_item, 0);
+ if (r) return r;
+ }
+ return apk_db_cache_foreach_item(db, cache_clean_item, 1);
}
static int cache_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *args)
{
- struct apk_out *out = &ac->out;
struct apk_database *db = ac->db;
struct cache_ctx *cctx = (struct cache_ctx *) ctx;
char *arg;
int r = 0, actions = 0;
- if (args->num != 1)
+ if (args->num < 1)
return -EINVAL;
arg = args->item[0];
- if (strcmp(arg, "sync") == 0)
+ if (strcmp(arg, "sync") == 0) {
actions = CACHE_CLEAN | CACHE_DOWNLOAD;
- else if (strcmp(arg, "clean") == 0)
+ } else if (strcmp(arg, "clean") == 0) {
+ actions = CACHE_CLEAN;
+ } else if (strcmp(arg, "purge") == 0) {
actions = CACHE_CLEAN;
- else if (strcmp(arg, "download") == 0)
+ db->ctx->flags |= APK_PURGE;
+ } else if (strcmp(arg, "download") == 0) {
actions = CACHE_DOWNLOAD;
- else
+ } else
return -EINVAL;
- if (!apk_db_cache_active(db)) {
- apk_err(out, "Package cache is not enabled.\n");
- r = 2;
- goto err;
+ if (!apk_db_cache_active(db))
+ actions &= CACHE_CLEAN;
+
+ if ((actions & CACHE_DOWNLOAD) && (cctx->solver_flags || cctx->add_dependencies)) {
+ if (apk_db_repository_check(db) != 0) return 3;
}
if (r == 0 && (actions & CACHE_CLEAN))
r = cache_clean(db);
if (r == 0 && (actions & CACHE_DOWNLOAD))
- r = cache_download(cctx, db);
-err:
+ r = cache_download(cctx, db, args);
+
return r;
}
diff --git a/src/app_convdb.c b/src/app_convdb.c
index f30d0f7..f27c03d 100644
--- a/src/app_convdb.c
+++ b/src/app_convdb.c
@@ -5,6 +5,7 @@
#include "apk_adb.h"
#include "apk_applet.h"
+#include "apk_tar.h"
struct conv_script {
struct list_head script_node;
@@ -189,6 +190,9 @@ static int convert_idb(struct conv_ctx *ctx, struct apk_istream *is)
break;
}
}
+ adb_wo_free(&triggers);
+ adb_wo_free(&files);
+ adb_wo_free(&paths);
return apk_istream_close(is);
}
diff --git a/src/app_convndx.c b/src/app_convndx.c
index 21015cb..f8f649e 100644
--- a/src/app_convndx.c
+++ b/src/app_convndx.c
@@ -4,17 +4,18 @@
#include "apk_adb.h"
#include "apk_applet.h"
+#include "apk_extract.h"
struct conv_ctx {
struct apk_ctx *ac;
struct adb_obj pkgs;
struct adb dbi;
- struct apk_sign_ctx sctx;
- int found;
+ struct apk_extract_ctx ectx;
};
-static void convert_index(struct conv_ctx *ctx, struct apk_istream *is)
+static int convert_v2index(struct apk_extract_ctx *ectx, apk_blob_t *desc, struct apk_istream *is)
{
+ struct conv_ctx *ctx = container_of(ectx, struct conv_ctx, ectx);
struct adb_obj pkginfo;
apk_blob_t token = APK_BLOB_STR("\n"), l;
int i;
@@ -29,41 +30,18 @@ static void convert_index(struct conv_ctx *ctx, struct apk_istream *is)
i = adb_pkg_field_index(l.ptr[0]);
if (i > 0) adb_wo_pkginfo(&pkginfo, i, APK_BLOB_PTR_LEN(l.ptr+2, l.len-2));
}
+ return apk_istream_close(is);
}
-static int load_apkindex(void *sctx, const struct apk_file_info *fi,
- struct apk_istream *is)
-{
- struct conv_ctx *ctx = sctx;
- int r;
-
- r = apk_sign_ctx_process_file(&ctx->sctx, fi, is);
- if (r <= 0) return r;
-
- if (strcmp(fi->name, "APKINDEX") == 0) {
- ctx->found = 1;
- convert_index(ctx, is);
- return apk_istream_close(is);
- }
-
- return 0;
-}
+static const struct apk_extract_ops extract_convndx = {
+ .v2index = convert_v2index,
+};
static int load_index(struct conv_ctx *ctx, struct apk_istream *is)
{
- int r = 0;
-
- if (IS_ERR_OR_NULL(is)) return is ? PTR_ERR(is) : -EINVAL;
-
- ctx->found = 0;
- apk_sign_ctx_init(&ctx->sctx, APK_SIGN_VERIFY, NULL, apk_ctx_get_trust(ctx->ac));
- r = apk_tar_parse(
- apk_istream_gunzip_mpart(is, apk_sign_ctx_mpart_cb, &ctx->sctx),
- load_apkindex, ctx, apk_ctx_get_id_cache(ctx->ac));
- apk_sign_ctx_free(&ctx->sctx);
- if (r >= 0 && ctx->found == 0) r = -APKE_V2NDX_FORMAT;
-
- return r;
+ if (IS_ERR(is)) return PTR_ERR(is);
+ apk_extract_init(&ctx->ectx, ctx->ac, &extract_convndx);
+ return apk_extract(&ctx->ectx, is);
}
static int conv_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)
diff --git a/src/app_del.c b/src/app_del.c
index f4c00fc..7acb370 100644
--- a/src/app_del.c
+++ b/src/app_del.c
@@ -14,8 +14,9 @@
#include "apk_solver.h"
struct del_ctx {
- int recursive_delete : 1;
struct apk_dependency_array *world;
+ unsigned short recursive_delete : 1;
+ unsigned int genid;
int errors;
};
@@ -68,14 +69,10 @@ static void print_not_deleted_pkg(struct apk_package *pkg0, struct apk_dependenc
apk_msg(out, "World updated, but the following packages are not removed due to:");
ctx->header = 1;
}
- if (!ctx->indent.indent) {
- ctx->indent.x = printf(" %s:", ctx->name->name);
- ctx->indent.indent = ctx->indent.x + 1;
- }
-
- if (name_in_world(pkg0->name)) {
+ if (!ctx->indent.indent)
+ apk_print_indented_group(&ctx->indent, 0, " %s:", ctx->name->name);
+ if (name_in_world(pkg0->name))
apk_print_indented(&ctx->indent, APK_BLOB_STR(pkg0->name->name));
- }
foreach_array_item(d, pkg0->provides) {
if (!name_in_world(d->name)) continue;
apk_print_indented(&ctx->indent, APK_BLOB_STR(d->name->name));
@@ -91,36 +88,44 @@ static void print_not_deleted_pkg(struct apk_package *pkg0, struct apk_dependenc
}
}
-static void print_not_deleted_name(struct apk_database *db, const char *match,
- struct apk_name *name, void *pctx)
+static int print_not_deleted_name(struct apk_database *db, const char *match,
+ struct apk_name *name, void *pctx)
{
struct apk_out *out = &db->ctx->out;
struct not_deleted_ctx *ctx = (struct not_deleted_ctx *) pctx;
struct apk_provider *p;
- ctx->indent = (struct apk_indent) { .out = out };
+ if (!name) return 0;
+
ctx->name = name;
ctx->matches = apk_foreach_genid() | APK_FOREACH_MARKED | APK_DEP_SATISFIES;
+ apk_print_indented_init(&ctx->indent, out, 0);
foreach_array_item(p, name->providers)
if (p->pkg->marked)
print_not_deleted_pkg(p->pkg, NULL, NULL, ctx);
- if (ctx->indent.indent)
- printf("\n");
+ apk_print_indented_end(&ctx->indent);
+ return 0;
}
static void delete_pkg(struct apk_package *pkg0, struct apk_dependency *dep0,
struct apk_package *pkg, void *pctx)
{
struct del_ctx *ctx = (struct del_ctx *) pctx;
+ struct apk_dependency *d;
apk_deps_del(&ctx->world, pkg0->name);
- if (ctx->recursive_delete)
+
+ if (ctx->recursive_delete) {
+ foreach_array_item(d, pkg0->provides)
+ apk_deps_del(&ctx->world, d->name);
+
apk_pkg_foreach_reverse_dependency(
- pkg0, APK_FOREACH_INSTALLED | APK_DEP_SATISFIES,
+ pkg0, ctx->genid | APK_FOREACH_INSTALLED | APK_DEP_SATISFIES,
delete_pkg, pctx);
+ }
}
-static void delete_name(struct apk_database *db, const char *match,
+static int delete_name(struct apk_database *db, const char *match,
struct apk_name *name, void *pctx)
{
struct apk_out *out = &db->ctx->out;
@@ -130,7 +135,7 @@ static void delete_name(struct apk_database *db, const char *match,
if (!name) {
apk_err(out, "No such package: %s", match);
ctx->errors++;
- return;
+ return 0;
}
pkg = apk_pkg_get_installed(name);
@@ -138,6 +143,7 @@ static void delete_name(struct apk_database *db, const char *match,
delete_pkg(pkg, NULL, NULL, pctx);
else
apk_deps_del(&ctx->world, name);
+ return 0;
}
static int del_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)
@@ -150,8 +156,9 @@ static int del_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *arg
struct apk_dependency *d;
int r = 0;
+ ctx->genid = apk_foreach_genid();
apk_dependency_array_copy(&ctx->world, db->world);
- apk_name_foreach_matching(db, args, apk_foreach_genid(), delete_name, ctx);
+ if (args->num) apk_db_foreach_matching_name(db, args, delete_name, ctx);
if (ctx->errors) return ctx->errors;
r = apk_solver_solve(db, 0, ctx->world, &changeset);
@@ -162,10 +169,8 @@ static int del_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *arg
change->new_pkg->marked = 1;
foreach_array_item(d, ctx->world)
d->name->state_int = 1;
- apk_name_foreach_matching(
- db, args,
- apk_foreach_genid() | APK_FOREACH_MARKED | APK_DEP_SATISFIES,
- print_not_deleted_name, &ndctx);
+ if (args->num)
+ apk_db_foreach_sorted_name(db, args, print_not_deleted_name, &ndctx);
if (ndctx.header)
printf("\n");
@@ -182,6 +187,7 @@ static int del_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *arg
static struct apk_applet apk_del = {
.name = "del",
.open_flags = APK_OPENF_WRITE | APK_OPENF_NO_AUTOUPDATE,
+ .remove_empty_arguments = 1,
.context_size = sizeof(struct del_ctx),
.optgroups = { &optgroup_global, &optgroup_commit, &optgroup_applet },
.main = del_main,
diff --git a/src/app_dot.c b/src/app_dot.c
index ab20700..e543dc8 100644
--- a/src/app_dot.c
+++ b/src/app_dot.c
@@ -17,9 +17,9 @@
#define S_EVALUATING -2
struct dot_ctx {
- int not_empty : 1;
- int errors_only : 1;
- int installed_only : 1;
+ unsigned short not_empty : 1;
+ unsigned short errors_only : 1;
+ unsigned short installed_only : 1;
};
#define DOT_OPTIONS(OPT) \
@@ -62,17 +62,26 @@ static void start_graph(struct dot_ctx *ctx)
" node [shape=box];\n");
}
-static void dump_name(struct dot_ctx *ctx, struct apk_name *name)
+static void dump_error_name(struct dot_ctx *ctx, struct apk_name *name)
{
if (name->state_int)
return;
name->state_int = 1;
+ start_graph(ctx);
+ printf(" \"%s\" [style=dashed, color=red, fontcolor=red, shape=octagon];\n",
+ name->name);
+}
- if (name->providers->num == 0) {
- start_graph(ctx);
- printf(" \"%s\" [style=dashed, color=red, fontcolor=red, shape=octagon];\n",
- name->name);
- }
+static void dump_broken_deps(struct dot_ctx *ctx, struct apk_package *pkg, const char *kind, struct apk_dependency *dep)
+{
+ char buf[256];
+ if (!dep->broken) return;
+
+ dump_error_name(ctx, dep->name);
+ printf(" \"" PKG_VER_FMT "\" -> \"%s\" [arrowhead=%s,style=dashed,color=red,fontcolor=red,label=\"%s\"];\n",
+ PKG_VER_PRINTF(pkg), dep->name->name,
+ kind,
+ apk_dep_snprintf(buf, sizeof buf, dep));
}
static int dump_pkg(struct dot_ctx *ctx, struct apk_package *pkg)
@@ -96,9 +105,10 @@ static int dump_pkg(struct dot_ctx *ctx, struct apk_package *pkg)
foreach_array_item(dep, pkg->depends) {
struct apk_name *name = dep->name;
- dump_name(ctx, name);
+ dump_broken_deps(ctx, pkg, "normal", dep);
if (name->providers->num == 0) {
+ dump_error_name(ctx, name);
printf(" \"" PKG_VER_FMT "\" -> \"%s\" [color=red];\n",
PKG_VER_PRINTF(pkg), name->name);
continue;
@@ -126,15 +136,24 @@ static int dump_pkg(struct dot_ctx *ctx, struct apk_package *pkg)
}
}
}
+ foreach_array_item(dep, pkg->provides) dump_broken_deps(ctx, pkg, "inv", dep);
+ foreach_array_item(dep, pkg->install_if) dump_broken_deps(ctx, pkg, "diamond", dep);
ret -= S_EVALUATING - pkg->state_int;
pkg->state_int = S_EVALUATED;
return ret;
}
-static int foreach_pkg(apk_hash_item item, void *ctx)
+static int dump(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
{
- dump_pkg((struct dot_ctx *) ctx, (struct apk_package *) item);
+ struct dot_ctx *ctx = pctx;
+ struct apk_provider *p;
+
+ if (!name) return 0;
+
+ apk_name_sorted_providers(name);
+ foreach_array_item(p, name->providers)
+ dump_pkg(ctx, p->pkg);
return 0;
}
@@ -142,20 +161,8 @@ static int dot_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *arg
{
struct apk_database *db = ac->db;
struct dot_ctx *ctx = (struct dot_ctx *) pctx;
- struct apk_provider *p;
- char **parg;
- if (args->num) {
- foreach_array_item(parg, args) {
- struct apk_name *name = apk_db_get_name(db, APK_BLOB_STR(*parg));
- if (!name)
- continue;
- foreach_array_item(p, name->providers)
- dump_pkg(ctx, p->pkg);
- }
- } else {
- apk_hash_foreach(&db->available.packages, foreach_pkg, pctx);
- }
+ apk_db_foreach_matching_name(db, args, dump, pctx);
if (!ctx->not_empty)
return 1;
@@ -166,9 +173,10 @@ static int dot_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *arg
static struct apk_applet apk_dot = {
.name = "dot",
- .open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE,
+ .open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE | APK_OPENF_ALLOW_ARCH,
+ .remove_empty_arguments = 1,
.context_size = sizeof(struct dot_ctx),
- .optgroups = { &optgroup_global, &optgroup_applet },
+ .optgroups = { &optgroup_global, &optgroup_source, &optgroup_applet },
.main = dot_main,
};
diff --git a/src/app_extract.c b/src/app_extract.c
index aa5d8aa..60a1712 100644
--- a/src/app_extract.c
+++ b/src/app_extract.c
@@ -9,28 +9,20 @@
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
-#include <spawn.h>
#include <unistd.h>
#include <sys/stat.h>
-#include <sys/wait.h>
#include "apk_applet.h"
#include "apk_print.h"
-#include "apk_adb.h"
-#include "apk_pathbuilder.h"
+#include "apk_extract.h"
+#include "apk_fs.h"
struct extract_ctx {
const char *destination;
unsigned int extract_flags;
+ struct apk_extract_ctx ectx;
struct apk_ctx *ac;
- struct adb db;
- int root_fd;
-
- struct adb_obj pkg, paths, path, files, file;
- unsigned int cur_path, cur_file;
-
- struct apk_pathbuilder pb;
};
@@ -49,7 +41,7 @@ static int option_parse_applet(void *pctx, struct apk_ctx *ac, int opt, const ch
ctx->destination = optarg;
break;
case OPT_EXTRACT_no_chown:
- ctx->extract_flags |= APK_EXTRACTF_NO_CHOWN;
+ ctx->extract_flags |= APK_FSEXTRACTF_NO_CHOWN;
break;
default:
return -ENOTSUP;
@@ -62,316 +54,68 @@ static const struct apk_option_group optgroup_applet = {
.parse = option_parse_applet,
};
-static void apk_extract_acl(struct apk_file_info *fi, struct adb_obj *o, struct apk_id_cache *idc)
-{
- fi->mode = adb_ro_int(o, ADBI_ACL_MODE);
- fi->uid = apk_id_cache_resolve_uid(idc, adb_ro_blob(o, ADBI_ACL_USER), 65534);
- fi->gid = apk_id_cache_resolve_gid(idc, adb_ro_blob(o, ADBI_ACL_GROUP), 65534);
-}
-
-static const char *uvol_detect(struct apk_ctx *ac, const char *path)
-{
- if (!apk_ctx_get_uvol(ac)) return 0;
- if (strncmp(path, "uvol", 4) != 0) return 0;
- if (path[4] == 0) return path;
- if (path[4] == '/') return &path[5];
- return 0;
-}
-
-static int uvol_run(struct apk_ctx *ac, char *action, const char *volname, char *arg1, char *arg2)
-{
- struct apk_out *out = &ac->out;
- pid_t pid;
- int r, status;
- char *argv[] = { (char*)apk_ctx_get_uvol(ac), action, (char*) volname, arg1, arg2, 0 };
- posix_spawn_file_actions_t act;
-
- posix_spawn_file_actions_init(&act);
- posix_spawn_file_actions_addclose(&act, STDIN_FILENO);
- r = posix_spawn(&pid, apk_ctx_get_uvol(ac), &act, 0, argv, environ);
- posix_spawn_file_actions_destroy(&act);
- if (r != 0) {
- apk_err(out, "%s: uvol exec error: %s", volname, apk_error_str(r));
- return r;
- }
- waitpid(pid, &status, 0);
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- apk_err(out, "%s: uvol exited with error %d", volname, WEXITSTATUS(status));
- return -APKE_UVOL;
- }
- return 0;
-}
-
-static int uvol_extract(struct apk_ctx *ac, const char *volname, char *arg1, off_t sz, struct apk_istream *is, struct apk_digest_ctx *dctx)
+static int extract_v3_meta(struct apk_extract_ctx *ectx, struct adb_obj *pkg)
{
- struct apk_out *out = &ac->out;
- struct apk_ostream *os;
- pid_t pid;
- int r, status, pipefds[2];
- char *argv[] = { (char*)apk_ctx_get_uvol(ac), "write", (char*) volname, arg1, 0 };
- posix_spawn_file_actions_t act;
-
- if (pipe2(pipefds, O_CLOEXEC) != 0) return -errno;
-
- posix_spawn_file_actions_init(&act);
- posix_spawn_file_actions_adddup2(&act, pipefds[0], STDIN_FILENO);
- r = posix_spawn(&pid, apk_ctx_get_uvol(ac), &act, 0, argv, environ);
- posix_spawn_file_actions_destroy(&act);
- if (r != 0) {
- apk_err(out, "%s: uvol exec error: %s", volname, apk_error_str(r));
- return r;
- }
- close(pipefds[0]);
- os = apk_ostream_to_fd(pipefds[1]);
- apk_stream_copy(is, os, sz, 0, 0, dctx);
- r = apk_ostream_close(os);
- if (r != 0) {
- if (r >= 0) r = -APKE_UVOL;
- apk_err(out, "%s: uvol write error: %s", volname, apk_error_str(r));
- return r;
- }
-
- waitpid(pid, &status, 0);
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- apk_err(out, "%s: uvol exited with error %d", volname, WEXITSTATUS(status));
- return -APKE_UVOL;
- }
-
return 0;
}
-static int apk_extract_volume(struct apk_ctx *ac, struct apk_file_info *fi, struct apk_istream *is, struct apk_digest_ctx *dctx)
-{
- char size[64];
- int r;
-
- snprintf(size, sizeof size, "%ju", fi->size);
- r = uvol_run(ac, "create", fi->uvol_name, size, "ro");
- if (r != 0) return r;
- return uvol_extract(ac, fi->uvol_name, size, fi->size, is, dctx);
-}
-
-static int apk_extract_file(struct extract_ctx *ctx, off_t sz, struct apk_istream *is)
+static int extract_file(struct apk_extract_ctx *ectx, const struct apk_file_info *fi, struct apk_istream *is)
{
- struct apk_ctx *ac = ctx->ac;
- struct apk_out *out = &ac->out;
- const char *path_name = apk_pathbuilder_cstr(&ctx->pb);
- struct apk_file_info fi = {
- .name = path_name,
- .uvol_name = uvol_detect(ac, path_name),
- .size = adb_ro_int(&ctx->file, ADBI_FI_SIZE),
- .mtime = adb_ro_int(&ctx->file, ADBI_FI_MTIME),
- };
- struct adb_obj acl;
- struct apk_digest_ctx dctx;
- struct apk_digest d;
- apk_blob_t target;
+ struct extract_ctx *ctx = container_of(ectx, struct extract_ctx, ectx);
+ struct apk_out *out = &ctx->ac->out;
int r;
- apk_extract_acl(&fi, adb_ro_obj(&ctx->file, ADBI_FI_ACL, &acl), apk_ctx_get_id_cache(ctx->ac));
-
- target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET);
- if (!APK_BLOB_IS_NULL(target)) {
- char *target_path;
- uint16_t mode;
-
- if (target.len < 2) return -APKE_ADB_SCHEMA;
- mode = *(uint16_t*)target.ptr;
- target.ptr += 2;
- target.len -= 2;
- switch (mode) {
- case S_IFBLK:
- case S_IFCHR:
- case S_IFIFO:
- if (target.len != sizeof(uint64_t)) return -APKE_ADB_SCHEMA;
- struct unaligned64 {
- uint64_t value;
- } __attribute__((packed));
- fi.device = ((struct unaligned64 *)target.ptr)->value;
- break;
- case S_IFLNK:
- target_path = alloca(target.len + 1);
- memcpy(target_path, target.ptr, target.len);
- target_path[target.len] = 0;
- fi.link_target = target_path;
- break;
- default:
- return -APKE_ADB_SCHEMA;
- }
- fi.mode |= mode;
- return apk_archive_entry_extract(
- ctx->root_fd, &fi, 0, 0, is, 0, 0, 0,
- ctx->extract_flags, out);
- }
-
- apk_digest_from_blob(&fi.digest, adb_ro_blob(&ctx->file, ADBI_FI_HASHES));
- if (fi.digest.alg == APK_DIGEST_NONE) return -APKE_ADB_SCHEMA;
-
- fi.mode |= S_IFREG;
- apk_digest_ctx_init(&dctx, fi.digest.alg);
- if (fi.uvol_name) {
- r = apk_extract_volume(ac, &fi, is, &dctx);
- } else {
- r = apk_archive_entry_extract(
- ctx->root_fd, &fi, 0, 0, is, 0, 0, &dctx,
- ctx->extract_flags, out);
- }
- apk_digest_ctx_final(&dctx, &d);
- apk_digest_ctx_free(&dctx);
- if (r == 0 && apk_digest_cmp(&fi.digest, &d) != 0)
- r = -APKE_FILE_INTEGRITY;
- if (fi.uvol_name) {
- if (r == 0)
- r = uvol_run(ac, "up", fi.uvol_name, 0, 0);
- else
- uvol_run(ac, "remove", fi.uvol_name, 0, 0);
- } else if (r != 0)
- unlinkat(ctx->root_fd, fi.name, 0);
+ apk_dbg2(out, "%s", fi->name);
+ r = apk_fs_extract(ctx->ac, fi, is, 0, 0, ctx->extract_flags, APK_BLOB_NULL);
+ if (r == -EEXIST && S_ISDIR(fi->mode)) r = 0;
return r;
}
-static int apk_extract_directory(struct extract_ctx *ctx)
-{
- struct apk_ctx *ac = ctx->ac;
- struct apk_out *out = &ac->out;
- struct apk_file_info fi = {
- .name = apk_pathbuilder_cstr(&ctx->pb),
- };
- struct adb_obj acl;
-
- if (uvol_detect(ac, fi.name)) return 0;
-
- apk_extract_acl(&fi, adb_ro_obj(&ctx->path, ADBI_DI_ACL, &acl), apk_ctx_get_id_cache(ctx->ac));
- fi.mode |= S_IFDIR;
-
- return apk_archive_entry_extract(
- ctx->root_fd, &fi, 0, 0, 0, 0, 0, 0,
- ctx->extract_flags, out);
-}
-
-static int apk_extract_next_file(struct extract_ctx *ctx)
-{
- apk_blob_t target;
- int r;
-
- if (!ctx->cur_path) {
- // one time init
- ctx->cur_path = ADBI_FIRST;
- ctx->cur_file = 0;
- adb_r_rootobj(&ctx->db, &ctx->pkg, &schema_package);
- adb_ro_obj(&ctx->pkg, ADBI_PKG_PATHS, &ctx->paths);
- adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path);
- adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files);
- }
-
- do {
- ctx->cur_file++;
- while (ctx->cur_file > adb_ra_num(&ctx->files)) {
- ctx->cur_path++;
- ctx->cur_file = ADBI_FIRST;
- if (ctx->cur_path > adb_ra_num(&ctx->paths)) return 1;
- adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path);
- apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME));
- adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files);
- r = apk_extract_directory(ctx);
- if (r != 0) return r;
- }
- adb_ro_obj(&ctx->files, ctx->cur_file, &ctx->file);
- apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME));
- apk_pathbuilder_pushb(&ctx->pb, adb_ro_blob(&ctx->file, ADBI_FI_NAME));
- target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET);
- if (adb_ro_int(&ctx->file, ADBI_FI_SIZE) != 0 &&
- APK_BLOB_IS_NULL(target)) {
- return 0;
- }
- r = apk_extract_file(ctx, 0, 0);
- if (r != 0) return r;
- } while (1);
-}
-
-static int apk_extract_data_block(struct adb *db, struct adb_block *b, struct apk_istream *is)
-{
- struct extract_ctx *ctx = container_of(db, struct extract_ctx, db);
- struct adb_data_package *hdr;
- size_t sz = adb_block_length(b);
- int r;
-
- if (adb_block_type(b) != ADB_BLOCK_DATA) return 0;
-
- r = apk_extract_next_file(ctx);
- if (r != 0) {
- if (r > 0) r = -APKE_ADB_BLOCK;
- return r;
- }
-
- hdr = apk_istream_get(is, sizeof *hdr);
- sz -= sizeof *hdr;
- if (IS_ERR(hdr)) return PTR_ERR(hdr);
-
- if (hdr->path_idx != ctx->cur_path ||
- hdr->file_idx != ctx->cur_file ||
- sz != adb_ro_int(&ctx->file, ADBI_FI_SIZE)) {
- // got data for some unexpected file
- return -APKE_ADB_BLOCK;
- }
-
- return apk_extract_file(ctx, sz, is);
-}
-
-static int apk_extract_pkg(struct extract_ctx *ctx, const char *fn)
-{
- struct apk_ctx *ac = ctx->ac;
- struct apk_trust *trust = apk_ctx_get_trust(ac);
- int r;
-
- r = adb_m_process(&ctx->db,
- adb_decompress(apk_istream_from_fd_url(AT_FDCWD, fn, apk_ctx_since(ac, 0)), 0),
- ADB_SCHEMA_PACKAGE, trust, apk_extract_data_block);
- if (r == 0) {
- r = apk_extract_next_file(ctx);
- if (r == 0) r = -APKE_ADB_BLOCK;
- if (r == 1) r = 0;
- }
- adb_free(&ctx->db);
- return r;
-}
+static const struct apk_extract_ops extract_ops = {
+ .v2meta = apk_extract_v2_meta,
+ .v3meta = extract_v3_meta,
+ .file = extract_file,
+};
static int extract_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)
{
struct extract_ctx *ctx = pctx;
struct apk_out *out = &ac->out;
char **parg;
- int r;
+ int r = 0;
ctx->ac = ac;
- ctx->extract_flags |= APK_EXTRACTF_NO_OVERWRITE;
+ if (getuid() != 0) ctx->extract_flags |= APK_FSEXTRACTF_NO_CHOWN|APK_FSEXTRACTF_NO_SYS_XATTRS;
+ if (!(ac->force & APK_FORCE_OVERWRITE)) ctx->extract_flags |= APK_FSEXTRACTF_NO_OVERWRITE;
if (!ctx->destination) ctx->destination = ".";
- ctx->root_fd = openat(AT_FDCWD, ctx->destination, O_RDONLY);
- if (ctx->root_fd < 0) {
+
+ ac->dest_fd = openat(AT_FDCWD, ctx->destination, O_RDONLY);
+ if (ac->dest_fd < 0) {
r = -errno;
apk_err(out, "Error opening destination '%s': %s",
ctx->destination, apk_error_str(r));
return r;
}
+ apk_extract_init(&ctx->ectx, ac, &extract_ops);
foreach_array_item(parg, args) {
apk_out(out, "Extracting %s...", *parg);
- r = apk_extract_pkg(ctx, *parg);
+ r = apk_extract(&ctx->ectx, apk_istream_from_fd_url(AT_FDCWD, *parg, apk_ctx_since(ac, 0)));
if (r != 0) {
apk_err(out, "%s: %s", *parg, apk_error_str(r));
break;
}
}
- close(ctx->root_fd);
+ close(ac->dest_fd);
return r;
}
-static struct apk_applet apk_extract = {
+static struct apk_applet app_extract = {
.name = "extract",
.context_size = sizeof(struct extract_ctx),
.optgroups = { &optgroup_global, &optgroup_applet },
.main = extract_main,
};
-APK_DEFINE_APPLET(apk_extract);
+APK_DEFINE_APPLET(app_extract);
diff --git a/src/app_fetch.c b/src/app_fetch.c
index c54a8e0..d133f37 100644
--- a/src/app_fetch.c
+++ b/src/app_fetch.c
@@ -20,13 +20,16 @@
#include "apk_print.h"
#include "apk_solver.h"
-#define FETCH_RECURSIVE 1
-#define FETCH_STDOUT 2
-#define FETCH_LINK 4
+#define FETCH_RECURSIVE 0x01
+#define FETCH_STDOUT 0x02
+#define FETCH_LINK 0x04
+#define FETCH_URL 0x08
+#define FETCH_WORLD 0x10
struct fetch_ctx {
unsigned int flags;
int outdir_fd, errors;
+ time_t built_after;
struct apk_database *db;
struct apk_progress prog;
size_t done, total;
@@ -68,19 +71,41 @@ static int cup(void)
}
#define FETCH_OPTIONS(OPT) \
+ OPT(OPT_FETCH_built_after, APK_OPT_ARG "built-after") \
OPT(OPT_FETCH_link, APK_OPT_SH("l") "link") \
OPT(OPT_FETCH_recursive, APK_OPT_SH("R") "recursive") \
OPT(OPT_FETCH_output, APK_OPT_ARG APK_OPT_SH("o") "output") \
OPT(OPT_FETCH_simulate, "simulate") \
- OPT(OPT_FETCH_stdout, APK_OPT_SH("s") "stdout")
+ OPT(OPT_FETCH_stdout, APK_OPT_SH("s") "stdout") \
+ OPT(OPT_FETCH_url, "url") \
+ OPT(OPT_FETCH_world, APK_OPT_SH("w") "world") \
APK_OPT_APPLET(option_desc, FETCH_OPTIONS);
+static time_t parse_time(const char *timestr)
+{
+ struct tm tm;
+ char *p;
+ time_t t;
+
+ p = strptime(optarg, "%Y-%m-%d %H:%M:%S", &tm);
+ if (p && *p == 0) return mktime(&tm);
+
+ t = strtoul(optarg, &p, 10);
+ if (p && *p == 0) return t;
+
+ return 0;
+}
+
static int option_parse_applet(void *ctx, struct apk_ctx *ac, int opt, const char *optarg)
{
struct fetch_ctx *fctx = (struct fetch_ctx *) ctx;
switch (opt) {
+ case OPT_FETCH_built_after:
+ fctx->built_after = parse_time(optarg);
+ if (!fctx->built_after) return -EINVAL;
+ break;
case OPT_FETCH_simulate:
ac->flags |= APK_SIMULATE;
break;
@@ -96,6 +121,13 @@ static int option_parse_applet(void *ctx, struct apk_ctx *ac, int opt, const cha
case OPT_FETCH_output:
fctx->outdir_fd = openat(AT_FDCWD, optarg, O_RDONLY | O_CLOEXEC);
break;
+ case OPT_FETCH_url:
+ fctx->flags |= FETCH_URL;
+ break;
+ case OPT_FETCH_world:
+ fctx->flags |= FETCH_WORLD | FETCH_RECURSIVE;
+ ac->open_flags &= ~APK_OPENF_NO_WORLD;
+ break;
default:
return -ENOTSUP;
}
@@ -113,12 +145,10 @@ static void progress_cb(void *pctx, size_t bytes_done)
apk_print_progress(&ctx->prog, ctx->done + bytes_done, ctx->total);
}
-static int fetch_package(apk_hash_item item, void *pctx)
+static int fetch_package(struct apk_database *db, const char *match, struct apk_package *pkg, void *pctx)
{
- struct fetch_ctx *ctx = (struct fetch_ctx *) pctx;
- struct apk_database *db = ctx->db;
+ struct fetch_ctx *ctx = pctx;
struct apk_out *out = &db->ctx->out;
- struct apk_package *pkg = (struct apk_package *) item;
struct apk_istream *is;
struct apk_ostream *os;
struct apk_repository *repo;
@@ -146,22 +176,25 @@ static int fetch_package(apk_hash_item item, void *pctx)
return 0;
}
- apk_msg(out, "Downloading " PKG_VER_FMT, PKG_VER_PRINTF(pkg));
+ r = apk_repo_format_item(db, repo, pkg, &urlfd, url, sizeof(url));
+ if (r < 0) goto err;
+
+ if (ctx->flags & FETCH_URL)
+ apk_msg(out, "%s", url);
+ else
+ apk_msg(out, "Downloading " PKG_VER_FMT, PKG_VER_PRINTF(pkg));
+
if (db->ctx->flags & APK_SIMULATE)
return 0;
- r = apk_repo_format_item(db, repo, pkg, &urlfd, url, sizeof(url));
- if (r < 0)
- goto err;
-
if (ctx->flags & FETCH_STDOUT) {
os = apk_ostream_to_fd(STDOUT_FILENO);
} else {
if ((ctx->flags & FETCH_LINK) && urlfd >= 0) {
- if (linkat(urlfd, url,
- ctx->outdir_fd, filename,
- AT_SYMLINK_FOLLOW) == 0)
- return 0;
+ const char *urlfile = apk_url_local_file(url);
+ if (urlfile &&
+ linkat(urlfd, urlfile, ctx->outdir_fd, filename, AT_SYMLINK_FOLLOW) == 0)
+ goto done;
}
os = apk_ostream_to_file(ctx->outdir_fd, filename, 0644);
if (IS_ERR(os)) {
@@ -171,8 +204,8 @@ static int fetch_package(apk_hash_item item, void *pctx)
}
is = apk_istream_from_fd_url(urlfd, url, apk_db_url_since(db, 0));
- if (IS_ERR_OR_NULL(is)) {
- r = PTR_ERR(is) ?: -EIO;
+ if (IS_ERR(is)) {
+ r = PTR_ERR(is);
goto err;
}
@@ -181,13 +214,13 @@ static int fetch_package(apk_hash_item item, void *pctx)
apk_istream_close(is);
r = apk_ostream_close(os);
if (r) goto err;
-
- ctx->done += pkg->size;
- return 0;
+ goto done;
err:
apk_err(out, PKG_VER_FMT ": %s", PKG_VER_PRINTF(pkg), apk_error_str(r));
ctx->errors++;
+done:
+ ctx->done += pkg->size;
return 0;
}
@@ -195,6 +228,8 @@ static void mark_package(struct fetch_ctx *ctx, struct apk_package *pkg)
{
if (pkg == NULL || pkg->marked)
return;
+ if (ctx->built_after && pkg->build_time && ctx->built_after >= pkg->build_time)
+ return;
ctx->total += pkg->size;
pkg->marked = 1;
}
@@ -210,22 +245,28 @@ static void mark_error(struct fetch_ctx *ctx, const char *match, struct apk_name
ctx->errors++;
}
-static void mark_name_flags(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
+static void mark_dep_flags(struct fetch_ctx *ctx, struct apk_dependency *dep)
+{
+ dep->name->auto_select_virtual = 1;
+ apk_deps_add(&ctx->world, dep);
+}
+
+static int mark_name_flags(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
{
struct fetch_ctx *ctx = (struct fetch_ctx *) pctx;
struct apk_dependency dep = (struct apk_dependency) {
.name = name,
.version = &apk_atom_null,
- .result_mask = APK_DEPMASK_ANY,
+ .op = APK_DEPMASK_ANY,
};
- if (!IS_ERR_OR_NULL(name)) {
- name->auto_select_virtual = 1;
- apk_deps_add(&ctx->world, &dep);
- } else {
+ if (!name) {
ctx->errors++;
mark_error(ctx, match, name);
+ return 0;
}
+ mark_dep_flags(ctx, &dep);
+ return 0;
}
static void mark_names_recursive(struct apk_database *db, struct apk_string_array *args, void *pctx)
@@ -246,23 +287,27 @@ static void mark_names_recursive(struct apk_database *db, struct apk_string_arra
apk_change_array_free(&changeset.changes);
}
-static void mark_name(struct apk_database *db, const char *match, struct apk_name *name, void *ctx)
+static int mark_name(struct apk_database *db, const char *match, struct apk_name *name, void *ctx)
{
struct apk_package *pkg = NULL;
struct apk_provider *p;
if (!name) goto err;
- foreach_array_item(p, name->providers)
- if (pkg == NULL || apk_pkg_version_compare(p->pkg, pkg) == APK_VERSION_GREATER)
+ foreach_array_item(p, name->providers) {
+ if (pkg == NULL ||
+ (p->pkg->name == name && pkg->name != name) ||
+ apk_pkg_version_compare(p->pkg, pkg) == APK_VERSION_GREATER)
pkg = p->pkg;
+ }
if (!pkg) goto err;
mark_package(ctx, pkg);
- return;
+ return 0;
err:
mark_error(ctx, match, name);
+ return 0;
}
static int purge_package(void *pctx, int dirfd, const char *filename)
@@ -302,6 +347,7 @@ static int fetch_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
struct apk_out *out = &ac->out;
struct apk_database *db = ac->db;
struct fetch_ctx *ctx = (struct fetch_ctx *) pctx;
+ struct apk_dependency *dep;
ctx->db = db;
ctx->prog = db->ctx->progress;
@@ -321,15 +367,19 @@ static int fetch_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
if (ctx->flags & FETCH_RECURSIVE) {
apk_dependency_array_init(&ctx->world);
- apk_name_foreach_matching(db, args, apk_foreach_genid(), mark_name_flags, ctx);
+ foreach_array_item(dep, db->world)
+ mark_dep_flags(ctx, dep);
+ if (args->num)
+ apk_db_foreach_matching_name(db, args, mark_name_flags, ctx);
if (ctx->errors == 0)
mark_names_recursive(db, args, ctx);
apk_dependency_array_free(&ctx->world);
} else {
- apk_name_foreach_matching(db, args, apk_foreach_genid(), mark_name, ctx);
+ if (args->num)
+ apk_db_foreach_matching_name(db, args, mark_name, ctx);
}
if (!ctx->errors)
- apk_hash_foreach(&db->available.packages, fetch_package, ctx);
+ apk_db_foreach_sorted_package(db, NULL, fetch_package, ctx);
/* Remove packages not matching download spec from the output directory */
if (!ctx->errors && (db->ctx->flags & APK_PURGE) &&
@@ -341,9 +391,9 @@ static int fetch_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
static struct apk_applet apk_fetch = {
.name = "fetch",
- .open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE,
+ .open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE | APK_OPENF_ALLOW_ARCH,
.context_size = sizeof(struct fetch_ctx),
- .optgroups = { &optgroup_global, &optgroup_applet },
+ .optgroups = { &optgroup_global, &optgroup_source, &optgroup_applet },
.main = fetch_main,
};
diff --git a/src/app_fix.c b/src/app_fix.c
index 170f2c2..9f6610c 100644
--- a/src/app_fix.c
+++ b/src/app_fix.c
@@ -13,12 +13,14 @@
#include "apk_database.h"
#include "apk_print.h"
#include "apk_solver.h"
+#include "apk_fs.h"
struct fix_ctx {
+ struct apk_database *db;
unsigned short solver_flags;
- int fix_depends : 1;
- int fix_xattrs : 1;
- int fix_directory_permissions : 1;
+ unsigned short fix_depends : 1;
+ unsigned short fix_xattrs : 1;
+ unsigned short fix_directory_permissions : 1;
int errors;
};
@@ -61,11 +63,21 @@ static const struct apk_option_group optgroup_applet = {
.parse = option_parse_applet,
};
-static int mark_recalculate(apk_hash_item item, void *ctx)
+static int fix_directory_permissions(apk_hash_item item, void *pctx)
{
+ struct fix_ctx *ctx = (struct fix_ctx *) pctx;
+ struct apk_database *db = ctx->db;
+ struct apk_out *out = &db->ctx->out;
struct apk_db_dir *dir = (struct apk_db_dir *) item;
- if (dir->refs == 0) return 0;
- dir->update_permissions = 1;
+
+ if (dir->namelen == 0 || !dir->refs) return 0;
+
+ apk_db_dir_prepare(db, dir, dir->owner->acl, dir->owner->acl);
+ if (dir->permissions_ok) return 0;
+
+ apk_dbg(out, "fixing directory %s", dir->name);
+ dir->permissions_ok = 1;
+ apk_db_dir_update_permissions(db, dir->owner);
return 0;
}
@@ -74,7 +86,7 @@ static void mark_fix(struct fix_ctx *ctx, struct apk_name *name)
apk_solver_set_name_flags(name, ctx->solver_flags, ctx->fix_depends ? ctx->solver_flags : 0);
}
-static void set_solver_flags(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
+static int set_solver_flags(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
{
struct apk_out *out = &db->ctx->out;
struct fix_ctx *ctx = pctx;
@@ -82,8 +94,11 @@ static void set_solver_flags(struct apk_database *db, const char *match, struct
if (!name) {
apk_err(out, "Package '%s' not found", match);
ctx->errors++;
- } else
- mark_fix(ctx, name);
+ return 0;
+ }
+
+ mark_fix(ctx, name);
+ return 0;
}
static int fix_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)
@@ -92,12 +107,10 @@ static int fix_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *arg
struct fix_ctx *ctx = (struct fix_ctx *) pctx;
struct apk_installed_package *ipkg;
+ ctx->db = db;
if (!ctx->solver_flags)
ctx->solver_flags = APK_SOLVERF_REINSTALL;
- if (ctx->fix_directory_permissions)
- apk_hash_foreach(&db->installed.dirs, mark_recalculate, db);
-
if (args->num == 0) {
list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
if (ipkg->broken_files || ipkg->broken_script ||
@@ -105,16 +118,25 @@ static int fix_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *arg
mark_fix(ctx, ipkg->pkg->name);
}
} else
- apk_name_foreach_matching(db, args, apk_foreach_genid(), set_solver_flags, ctx);
+ apk_db_foreach_matching_name(db, args, set_solver_flags, ctx);
if (ctx->errors) return ctx->errors;
+ if (ctx->fix_directory_permissions) {
+ apk_hash_foreach(&db->installed.dirs, fix_directory_permissions, ctx);
+ if (db->num_dir_update_errors) {
+ apk_err(&ac->out, "Failed to fix directory permissions");
+ return -1;
+ }
+ }
+
return apk_solver_commit(db, 0, db->world);
}
static struct apk_applet apk_fix = {
.name = "fix",
.open_flags = APK_OPENF_WRITE,
+ .remove_empty_arguments = 1,
.context_size = sizeof(struct fix_ctx),
.optgroups = { &optgroup_global, &optgroup_commit, &optgroup_applet },
.main = fix_main,
diff --git a/src/app_index.c b/src/app_index.c
index cc37fa4..c13ba2b 100644
--- a/src/app_index.c
+++ b/src/app_index.c
@@ -15,13 +15,18 @@
#include "apk_applet.h"
#include "apk_database.h"
+#include "apk_defines.h"
#include "apk_print.h"
+#include "apk_tar.h"
-#define APK_INDEXF_NO_WARNINGS 0x0001
+#define APK_INDEXF_NO_WARNINGS BIT(0)
+#define APK_INDEXF_MERGE BIT(1)
+#define APK_INDEXF_PRUNE_ORIGIN BIT(2)
struct counts {
- struct apk_out *out;
+ struct apk_indent indent;
int unsatisfied;
+ unsigned short header : 1;
};
struct index_ctx {
@@ -30,15 +35,16 @@ struct index_ctx {
const char *description;
const char *rewrite_arch;
time_t index_mtime;
- int method;
unsigned short index_flags;
};
#define INDEX_OPTIONS(OPT) \
OPT(OPT_INDEX_description, APK_OPT_ARG APK_OPT_SH("d") "description") \
OPT(OPT_INDEX_index, APK_OPT_ARG APK_OPT_SH("x") "index") \
+ OPT(OPT_INDEX_merge, "merge") \
OPT(OPT_INDEX_no_warnings, "no-warnings") \
OPT(OPT_INDEX_output, APK_OPT_ARG APK_OPT_SH("o") "output") \
+ OPT(OPT_INDEX_prune_origin, "prune-origin") \
OPT(OPT_INDEX_rewrite_arch, APK_OPT_ARG "rewrite-arch")
APK_OPT_APPLET(option_desc, INDEX_OPTIONS);
@@ -54,9 +60,15 @@ static int option_parse_applet(void *ctx, struct apk_ctx *ac, int opt, const cha
case OPT_INDEX_index:
ictx->index = optarg;
break;
+ case OPT_INDEX_merge:
+ ictx->index_flags |= APK_INDEXF_MERGE;
+ break;
case OPT_INDEX_output:
ictx->output = optarg;
break;
+ case OPT_INDEX_prune_origin:
+ ictx->index_flags |= APK_INDEXF_PRUNE_ORIGIN;
+ break;
case OPT_INDEX_rewrite_arch:
ictx->rewrite_arch = optarg;
break;
@@ -74,6 +86,46 @@ static const struct apk_option_group optgroup_applet = {
.parse = option_parse_applet,
};
+struct index_writer {
+ struct apk_ostream *os;
+ int count;
+ unsigned short index_flags;
+};
+
+static int index_write_entry(struct apk_database *db, const char *match, struct apk_package *pkg, void *ctx)
+{
+ struct index_writer *iw = ctx;
+
+ switch (iw->index_flags & (APK_INDEXF_MERGE|APK_INDEXF_PRUNE_ORIGIN)) {
+ case APK_INDEXF_MERGE:
+ break;
+ case APK_INDEXF_MERGE|APK_INDEXF_PRUNE_ORIGIN:
+ if (!pkg->marked && pkg->origin) {
+ struct apk_name *n = apk_db_query_name(db, *pkg->origin);
+ if (n && n->state_int) return 0;
+ }
+ break;
+ default:
+ if (!pkg->marked) return 0;
+ break;
+ }
+
+ iw->count++;
+ return apk_pkg_write_index_entry(pkg, iw->os);
+}
+
+static int index_write(struct index_ctx *ictx, struct apk_database *db, struct apk_ostream *os)
+{
+ struct index_writer iw = {
+ .index_flags = ictx->index_flags,
+ .os = os,
+ };
+
+ apk_db_foreach_sorted_package(db, NULL, index_write_entry, &iw);
+
+ return iw.count;
+}
+
static int index_read_file(struct apk_database *db, struct index_ctx *ictx)
{
struct apk_file_info fi;
@@ -87,30 +139,35 @@ static int index_read_file(struct apk_database *db, struct index_ctx *ictx)
return apk_db_index_read_file(db, ictx->index, 0);
}
-static int warn_if_no_providers(apk_hash_item item, void *ctx)
+static int warn_if_no_providers(struct apk_database *db, const char *match, struct apk_name *name, void *ctx)
{
struct counts *counts = (struct counts *) ctx;
- struct apk_name *name = (struct apk_name *) item;
- struct apk_out *out = counts->out;
if (!name->is_dependency) return 0;
if (name->providers->num) return 0;
- if (++counts->unsatisfied < 10) {
- apk_warn(out, "No provider for dependency '%s'", name->name);
- } else if (counts->unsatisfied == 10) {
- apk_warn(out, "Too many unsatisfiable dependencies, not reporting the rest.");
+ if (!counts->header) {
+ apk_print_indented_group(&counts->indent, 2, "WARNING: No provider for the dependencies:\n");
+ counts->header = 1;
}
-
+ apk_print_indented(&counts->indent, APK_BLOB_STR(name->name));
+ counts->unsatisfied++;
return 0;
}
+static void index_mark_package(struct apk_database *db, struct apk_package *pkg, apk_blob_t *rewrite_arch)
+{
+ if (rewrite_arch) pkg->arch = rewrite_arch;
+ pkg->marked = 1;
+ if (pkg->origin) apk_db_get_name(db, *pkg->origin)->state_int = 1;
+}
+
static int index_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *args)
{
struct apk_out *out = &ac->out;
struct apk_database *db = ac->db;
- struct counts counts = { .out = out };
- struct apk_ostream *os;
+ struct counts counts = { .unsatisfied=0 };
+ struct apk_ostream *os, *counter;
struct apk_file_info fi;
int total, r, found, newpkgs = 0, errors = 0;
struct index_ctx *ictx = (struct index_ctx *) ctx;
@@ -126,9 +183,6 @@ static int index_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *ar
return -1;
}
- if (ictx->method == 0)
- ictx->method = APK_SIGN_GENERATE;
-
if ((r = index_read_file(db, ictx)) < 0) {
apk_err(out, "%s: %s", ictx->index, apk_error_str(r));
return r;
@@ -177,25 +231,21 @@ static int index_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *ar
if (pkg->name != name) continue;
if (apk_blob_compare(bver, *pkg->version) != 0) continue;
if (pkg->size != fi.size) continue;
- pkg->filename = strdup(*parg);
- if (rewrite_arch) pkg->arch = rewrite_arch;
+ index_mark_package(db, pkg, rewrite_arch);
found = TRUE;
break;
}
} while (0);
if (!found) {
- struct apk_sign_ctx sctx;
- apk_sign_ctx_init(&sctx, ictx->method, NULL, apk_ctx_get_trust(ac));
- r = apk_pkg_read(db, *parg, &sctx, &pkg);
+ r = apk_pkg_read(db, *parg, &pkg, FALSE);
if (r < 0) {
apk_err(out, "%s: %s", *parg, apk_error_str(r));
errors++;
} else {
+ index_mark_package(db, pkg, rewrite_arch);
newpkgs++;
- if (rewrite_arch) pkg->arch = rewrite_arch;
}
- apk_sign_ctx_free(&sctx);
}
}
if (errors)
@@ -205,40 +255,33 @@ static int index_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *ar
os = apk_ostream_to_file(AT_FDCWD, ictx->output, 0644);
else
os = apk_ostream_to_fd(STDOUT_FILENO);
- if (IS_ERR_OR_NULL(os)) return -1;
-
- if (ictx->method == APK_SIGN_GENERATE) {
- struct apk_ostream *counter;
-
- memset(&fi, 0, sizeof(fi));
- fi.mode = 0644 | S_IFREG;
- fi.name = "APKINDEX";
- counter = apk_ostream_counter(&fi.size);
- r = apk_db_index_write(db, counter);
- apk_ostream_close(counter);
-
- if (r >= 0) {
- os = apk_ostream_gzip(os);
- if (ictx->description != NULL) {
- struct apk_file_info fi_desc;
- memset(&fi_desc, 0, sizeof(fi));
- fi_desc.mode = 0644 | S_IFREG;
- fi_desc.name = "DESCRIPTION";
- fi_desc.size = strlen(ictx->description);
- apk_tar_write_entry(os, &fi_desc, ictx->description);
- }
-
- apk_tar_write_entry(os, &fi, NULL);
- r = apk_db_index_write(db, os);
- apk_tar_write_padding(os, &fi);
-
- apk_tar_write_entry(os, NULL, NULL);
- }
- } else {
- r = apk_db_index_write(db, os);
+ if (IS_ERR(os)) return PTR_ERR(os);
+
+ memset(&fi, 0, sizeof(fi));
+ fi.mode = 0644 | S_IFREG;
+ fi.name = "APKINDEX";
+ fi.mtime = apk_get_build_time();
+ counter = apk_ostream_counter(&fi.size);
+ index_write(ictx, db, counter);
+ apk_ostream_close(counter);
+
+ os = apk_ostream_gzip(os);
+ if (ictx->description) {
+ struct apk_file_info fi_desc;
+ memset(&fi_desc, 0, sizeof(fi));
+ fi_desc.mode = 0644 | S_IFREG;
+ fi_desc.name = "DESCRIPTION";
+ fi_desc.size = strlen(ictx->description);
+ fi_desc.mtime = apk_get_build_time();
+ apk_tar_write_entry(os, &fi_desc, ictx->description);
}
- apk_ostream_close(os);
+ apk_tar_write_entry(os, &fi, NULL);
+ index_write(ictx, db, os);
+ apk_tar_write_padding(os, &fi);
+ apk_tar_write_entry(os, NULL, NULL);
+
+ r = apk_ostream_close(os);
if (r < 0) {
apk_err(out, "Index generation failed: %s", apk_error_str(r));
return r;
@@ -246,16 +289,18 @@ static int index_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *ar
total = r;
if (!(ictx->index_flags & APK_INDEXF_NO_WARNINGS)) {
- apk_hash_foreach(&db->available.names, warn_if_no_providers, &counts);
+ apk_print_indented_init(&counts.indent, out, 1);
+ apk_db_foreach_sorted_name(db, NULL, warn_if_no_providers, &counts);
+ apk_print_indented_end(&counts.indent);
}
if (counts.unsatisfied != 0)
apk_warn(out,
"Total of %d unsatisfiable package names. Your repository may be broken.",
counts.unsatisfied);
- apk_msg(out, "Index has %d packages (of which %d are new)",
- total, newpkgs);
-
+ if (ictx->output != NULL)
+ apk_msg(out, "Index has %d packages (of which %d are new)",
+ total, newpkgs);
return 0;
}
diff --git a/src/app_info.c b/src/app_info.c
index beb32d9..4a21ccf 100644
--- a/src/app_info.c
+++ b/src/app_info.c
@@ -81,6 +81,18 @@ static void info_exists(struct info_ctx *ctx, struct apk_database *db,
}
}
+static struct apk_package *get_owner(struct apk_database *db, apk_blob_t fn)
+{
+ struct apk_db_dir *dir;
+
+ apk_blob_pull_blob_match(&fn, APK_BLOB_STRLIT("/"));
+ if (fn.len && fn.ptr[fn.len-1] == '/') fn.len--;
+
+ dir = apk_db_dir_query(db, fn);
+ if (dir) return dir->owner->pkg;
+ return apk_db_get_file_owner(db, fn);
+}
+
static void info_who_owns(struct info_ctx *ctx, struct apk_database *db,
struct apk_string_array *args)
{
@@ -102,11 +114,12 @@ static void info_who_owns(struct info_ctx *ctx, struct apk_database *db,
fn = APK_BLOB_STR(*parg);
via = "";
- pkg = apk_db_get_file_owner(db, fn);
+
+ pkg = get_owner(db, fn);
if (pkg == NULL) {
r = readlinkat(db->root_fd, *parg, buf, sizeof(buf));
if (r > 0 && r < PATH_MAX && buf[0] == '/') {
- pkg = apk_db_get_file_owner(db, APK_BLOB_STR(buf));
+ pkg = get_owner(db, APK_BLOB_STR(buf));
via = "symlink target ";
}
}
@@ -122,7 +135,7 @@ static void info_who_owns(struct info_ctx *ctx, struct apk_database *db,
dep = (struct apk_dependency) {
.name = pkg->name,
.version = &apk_atom_null,
- .result_mask = APK_DEPMASK_ANY,
+ .op = APK_DEPMASK_ANY,
};
apk_deps_add(&deps, &dep);
} else {
@@ -132,7 +145,7 @@ static void info_who_owns(struct info_ctx *ctx, struct apk_database *db,
}
if (verbosity < 1 && deps->num != 0) {
os = apk_ostream_to_fd(STDOUT_FILENO);
- if (!IS_ERR_OR_NULL(os)) {
+ if (!IS_ERR(os)) {
apk_deps_write(db, deps, os, APK_BLOB_PTR_LEN(" ", 1));
apk_ostream_write(os, "\n", 1);
apk_ostream_close(os);
@@ -346,18 +359,17 @@ static void info_subaction(struct info_ctx *ctx, struct apk_package *pkg)
}
}
-static void print_name_info(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
+static int print_name_info(struct apk_database *db, const char *match, struct apk_package *pkg, void *pctx)
{
struct info_ctx *ctx = (struct info_ctx *) pctx;
- struct apk_provider *p;
- if (name == NULL) {
+ if (!pkg) {
ctx->errors++;
- return;
+ return 0;
}
- foreach_array_item(p, name->providers)
- info_subaction(ctx, p->pkg);
+ info_subaction(ctx, pkg);
+ return 0;
}
#define INFO_OPTIONS(OPT) \
@@ -443,7 +455,6 @@ static int info_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *arg
struct apk_out *out = &ac->out;
struct apk_database *db = ac->db;
struct info_ctx *ictx = (struct info_ctx *) ctx;
- struct apk_installed_package *ipkg;
verbosity = apk_out_verbosity(out);
ictx->db = db;
@@ -452,14 +463,14 @@ static int info_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *arg
if (ictx->action != NULL) {
ictx->action(ictx, db, args);
} else if (args->num > 0) {
- /* Print info on given names */
- apk_name_foreach_matching(
- db, args, APK_FOREACH_NULL_MATCHES_ALL | apk_foreach_genid(),
- print_name_info, ctx);
+ /* Print info on given packages */
+ apk_db_foreach_sorted_providers(db, args, print_name_info, ctx);
} else {
/* Print all installed packages */
- list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list)
- verbose_print_pkg(ipkg->pkg, 1);
+ struct apk_package_array *pkgs = apk_db_sorted_installed_packages(db);
+ struct apk_package **ppkg;
+ foreach_array_item(ppkg, pkgs)
+ verbose_print_pkg(*ppkg, 1);
}
return ictx->errors;
@@ -472,9 +483,9 @@ static const struct apk_option_group optgroup_applet = {
static struct apk_applet apk_info = {
.name = "info",
- .open_flags = APK_OPENF_READ,
+ .open_flags = APK_OPENF_READ | APK_OPENF_ALLOW_ARCH,
.context_size = sizeof(struct info_ctx),
- .optgroups = { &optgroup_global, &optgroup_applet },
+ .optgroups = { &optgroup_global, &optgroup_source, &optgroup_applet },
.main = info_main,
};
diff --git a/src/app_list.c b/src/app_list.c
index 620c3ad..efdf11d 100644
--- a/src/app_list.c
+++ b/src/app_list.c
@@ -26,6 +26,7 @@ struct list_ctx {
unsigned int match_origin : 1;
unsigned int match_depends : 1;
unsigned int match_providers : 1;
+ unsigned int manifest : 1;
struct apk_string_array *filters;
};
@@ -62,35 +63,29 @@ static int is_orphaned(const struct apk_name *name)
return (repos & ~BIT(APK_REPOSITORY_CACHED)) == 0;
}
-/* returns the currently installed package if there is a newer package that satisfies `name` */
-static const struct apk_package *is_upgradable(struct apk_name *name, const struct apk_package *pkg0)
+/* returns the currently installed package if 'pkg' is a newer and installable version */
+static const struct apk_package *is_upgradable(const struct apk_database *db, const struct apk_package *pkg)
{
- struct apk_provider *p;
+ struct apk_name *name = pkg->name;
struct apk_package *ipkg;
- apk_blob_t no_version = APK_BLOB_STR("");
- apk_blob_t *latest = &no_version;
- int r;
-
- if (!name) return NULL;
+ unsigned short allowed_repos;
ipkg = apk_pkg_get_installed(name);
if (!ipkg) return NULL;
- if (!pkg0) {
- foreach_array_item(p, name->providers) {
- pkg0 = p->pkg;
- if (pkg0 == ipkg) continue;
- r = apk_version_compare_blob(*pkg0->version, *latest);
- if (r == APK_VERSION_GREATER) latest = pkg0->version;
- }
- } else {
- latest = pkg0->version;
- }
- return apk_version_compare_blob(*ipkg->version, *latest) == APK_VERSION_LESS ? ipkg : NULL;
+ allowed_repos = db->repo_tags[ipkg->ipkg->repository_tag].allowed_repos;
+ if (!(pkg->repos & allowed_repos)) return NULL;
+
+ return apk_version_match(*ipkg->version, APK_VERSION_LESS, *pkg->version) ? ipkg : NULL;
}
-static void print_package(const struct apk_package *pkg, const struct list_ctx *ctx)
+static void print_package(const struct apk_database *db, const struct apk_package *pkg, const struct list_ctx *ctx)
{
+ if (ctx->verbosity <= 0) {
+ printf("%s\n", pkg->name->name);
+ return;
+ }
+
printf(PKG_VER_FMT " " BLOB_FMT " ",
PKG_VER_PRINTF(pkg), BLOB_PRINTF(*pkg->arch));
@@ -104,11 +99,8 @@ static void print_package(const struct apk_package *pkg, const struct list_ctx *
if (pkg->ipkg)
printf(" [installed]");
else {
- const struct apk_package *u;
-
- u = is_upgradable(pkg->name, pkg);
- if (u != NULL)
- printf(" [upgradable from: " PKG_VER_FMT "]", PKG_VER_PRINTF(u));
+ const struct apk_package *u = is_upgradable(db, pkg);
+ if (u != NULL) printf(" [upgradable from: " PKG_VER_FMT "]", PKG_VER_PRINTF(u));
}
@@ -121,7 +113,12 @@ static void print_package(const struct apk_package *pkg, const struct list_ctx *
printf("\n");
}
-static void filter_package(const struct apk_package *pkg, const struct list_ctx *ctx)
+static void print_manifest(const struct apk_package *pkg, const struct list_ctx *ctx)
+{
+ printf("%s " BLOB_FMT "\n", pkg->name->name, BLOB_PRINTF(*pkg->version));
+}
+
+static void filter_package(const struct apk_database *db, const struct apk_package *pkg, const struct list_ctx *ctx, const struct apk_name *name)
{
if (ctx->match_origin && !origin_matches(ctx, pkg))
return;
@@ -135,13 +132,19 @@ static void filter_package(const struct apk_package *pkg, const struct list_ctx
if (ctx->available && pkg->repos == BIT(APK_REPOSITORY_CACHED))
return;
- if (ctx->upgradable && !is_upgradable(pkg->name, pkg))
+ if (ctx->upgradable && !is_upgradable(db, pkg))
return;
- print_package(pkg, ctx);
+ if (ctx->match_providers)
+ printf("<%s> ", name->name);
+
+ if (ctx->manifest)
+ print_manifest(pkg, ctx);
+ else
+ print_package(db, pkg, ctx);
}
-static void iterate_providers(const struct apk_name *name, const struct list_ctx *ctx)
+static void iterate_providers(const struct apk_database *db, const struct apk_name *name, const struct list_ctx *ctx)
{
struct apk_provider *p;
@@ -149,37 +152,37 @@ static void iterate_providers(const struct apk_name *name, const struct list_ctx
if (!ctx->match_providers && p->pkg->name != name)
continue;
- if (ctx->match_providers)
- printf("<%s> ", name->name);
-
- filter_package(p->pkg, ctx);
+ filter_package(db, p->pkg, ctx, name);
}
}
-static void print_result(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
+static int print_result(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
{
struct list_ctx *ctx = pctx;
struct apk_name **pname;
- if (name == NULL)
- return;
+ if (!name) return 0;
+ apk_name_sorted_providers(name);
if (ctx->match_depends) {
foreach_array_item(pname, name->rdepends)
- iterate_providers(*pname, ctx);
+ iterate_providers(db, *pname, ctx);
} else {
- iterate_providers(name, ctx);
+ iterate_providers(db, name, ctx);
}
+ return 0;
}
#define LIST_OPTIONS(OPT) \
OPT(OPT_LIST_available, APK_OPT_SH("a") "available") \
- OPT(OPT_LIST_installed, APK_OPT_SH("I") "installed") \
OPT(OPT_LIST_depends, APK_OPT_SH("d") "depends") \
+ OPT(OPT_LIST_installed, APK_OPT_SH("I") "installed") \
+ OPT(OPT_LIST_manifest, "manifest") \
OPT(OPT_LIST_origin, APK_OPT_SH("o") "origin") \
OPT(OPT_LIST_orphaned, APK_OPT_SH("O") "orphaned") \
OPT(OPT_LIST_providers, APK_OPT_SH("P") "providers") \
- OPT(OPT_LIST_upgradeable, APK_OPT_SH("u") "upgradeable")
+ OPT(OPT_LIST_upgradable, APK_OPT_SH("u") "upgradable") \
+ OPT(OPT_LIST_upgradeable, "upgradeable")
APK_OPT_APPLET(option_desc, LIST_OPTIONS);
@@ -192,11 +195,15 @@ static int option_parse_applet(void *pctx, struct apk_ctx *ac, int opt, const ch
ctx->available = 1;
ctx->orphaned = 0;
break;
+ case OPT_LIST_depends:
+ ctx->match_depends = 1;
+ break;
case OPT_LIST_installed:
ctx->installed = 1;
break;
- case OPT_LIST_depends:
- ctx->match_depends = 1;
+ case OPT_LIST_manifest:
+ ctx->manifest = 1;
+ ctx->installed = 1;
break;
case OPT_LIST_origin:
ctx->match_origin = 1;
@@ -208,6 +215,7 @@ static int option_parse_applet(void *pctx, struct apk_ctx *ac, int opt, const ch
case OPT_LIST_providers:
ctx->match_providers = 1;
break;
+ case OPT_LIST_upgradable:
case OPT_LIST_upgradeable:
ctx->available = 1;
ctx->orphaned = 0;
@@ -238,18 +246,15 @@ static int list_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *ar
if (ctx->match_origin)
args = NULL;
- apk_name_foreach_matching(
- db, args, APK_FOREACH_NULL_MATCHES_ALL | apk_foreach_genid(),
- print_result, ctx);
-
+ apk_db_foreach_sorted_name(db, args, print_result, ctx);
return 0;
}
static struct apk_applet apk_list = {
.name = "list",
- .open_flags = APK_OPENF_READ,
+ .open_flags = APK_OPENF_READ | APK_OPENF_ALLOW_ARCH,
.context_size = sizeof(struct list_ctx),
- .optgroups = { &optgroup_global, &optgroup_applet },
+ .optgroups = { &optgroup_global, &optgroup_source, &optgroup_applet },
.main = list_main,
};
diff --git a/src/app_manifest.c b/src/app_manifest.c
index 86164f6..64c0127 100644
--- a/src/app_manifest.c
+++ b/src/app_manifest.c
@@ -14,8 +14,11 @@
#include "apk_defines.h"
#include "apk_applet.h"
#include "apk_database.h"
+#include "apk_extract.h"
#include "apk_version.h"
#include "apk_print.h"
+#include "apk_adb.h"
+#include "apk_pathbuilder.h"
/* TODO: support package files as well as generating manifest from the installed DB. */
static char *csum_types[APK_CHECKSUM_SHA1 + 1] = {
@@ -60,81 +63,115 @@ static void process_package(struct apk_database *db, struct apk_package *pkg)
struct manifest_file_ctx {
struct apk_out *out;
- struct apk_sign_ctx *sctx;
+ struct apk_extract_ctx ectx;
const char *prefix1, *prefix2;
};
-static int read_file_entry(void *ctx, const struct apk_file_info *ae, struct apk_istream *is)
+static int process_pkg_file(struct apk_extract_ctx *ectx, const struct apk_file_info *fi, struct apk_istream *is)
{
- struct manifest_file_ctx *mctx = ctx;
+ struct manifest_file_ctx *mctx = container_of(ectx, struct manifest_file_ctx, ectx);
struct apk_out *out = mctx->out;
char csum_buf[APK_BLOB_CHECKSUM_BUF];
apk_blob_t csum_blob = APK_BLOB_BUF(csum_buf);
- int r;
-
- r = apk_sign_ctx_verify_tar(mctx->sctx, ae, is);
- if (r != 0)
- return r;
- if (!mctx->sctx->data_started)
- return 0;
-
- if ((ae->mode & S_IFMT) != S_IFREG)
- return 0;
+ if ((fi->mode & S_IFMT) != S_IFREG) return 0;
memset(csum_buf, '\0', sizeof(csum_buf));
- apk_blob_push_hexdump(&csum_blob, APK_DIGEST_BLOB(ae->digest));
+ apk_blob_push_hexdump(&csum_blob, APK_DIGEST_BLOB(fi->digest));
apk_out(out, "%s%s%s:%s %s",
mctx->prefix1, mctx->prefix2,
- apk_digest_alg_str(ae->digest.alg), csum_buf,
- ae->name);
+ apk_digest_alg_str(fi->digest.alg), csum_buf,
+ fi->name);
return 0;
}
+static int process_v3_meta(struct apk_extract_ctx *ectx, struct adb_obj *pkg)
+{
+ struct manifest_file_ctx *mctx = container_of(ectx, struct manifest_file_ctx, ectx);
+ struct apk_out *out = mctx->out;
+ struct adb_obj paths, path, files, file;
+ struct apk_digest digest;
+ struct apk_pathbuilder pb;
+ char buf[APK_DIGEST_MAX_LENGTH*2+1];
+ apk_blob_t hex;
+ int i, j, n;
+
+ adb_ro_obj(pkg, ADBI_PKG_PATHS, &paths);
+
+ for (i = ADBI_FIRST; i <= adb_ra_num(&paths); i++) {
+ adb_ro_obj(&paths, i, &path);
+ adb_ro_obj(&path, ADBI_DI_FILES, &files);
+ apk_pathbuilder_setb(&pb, adb_ro_blob(&path, ADBI_DI_NAME));
+
+ for (j = ADBI_FIRST; j <= adb_ra_num(&files); j++) {
+ adb_ro_obj(&files, j, &file);
+ n = apk_pathbuilder_pushb(&pb, adb_ro_blob(&file, ADBI_FI_NAME));
+ apk_digest_from_blob(&digest, adb_ro_blob(&file, ADBI_FI_HASHES));
+
+ hex = APK_BLOB_BUF(buf);
+ apk_blob_push_hexdump(&hex, APK_DIGEST_BLOB(digest));
+ apk_blob_push_blob(&hex, APK_BLOB_STRLIT("\0"));
+
+ apk_out(out, "%s%s%s:%s %s",
+ mctx->prefix1, mctx->prefix2,
+ apk_digest_alg_str(digest.alg), buf,
+ apk_pathbuilder_cstr(&pb));
+ apk_pathbuilder_pop(&pb, n);
+ }
+ }
+
+ return -ECANCELED;
+}
+
+static const struct apk_extract_ops extract_manifest_ops = {
+ .v2meta = apk_extract_v2_meta,
+ .v3meta = process_v3_meta,
+ .file = process_pkg_file,
+};
+
static void process_file(struct apk_database *db, const char *match)
{
- struct apk_id_cache *idc = apk_ctx_get_id_cache(db->ctx);
struct apk_out *out = &db->ctx->out;
- struct apk_sign_ctx sctx;
struct manifest_file_ctx ctx = {
.out = out,
- .sctx = &sctx,
.prefix1 = "",
.prefix2 = "",
};
int r;
+ apk_extract_init(&ctx.ectx, db->ctx, &extract_manifest_ops);
if (apk_out_verbosity(out) > 1) {
ctx.prefix1 = match;
ctx.prefix2 = ": ";
}
- apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY, NULL, apk_ctx_get_trust(db->ctx));
- r = apk_tar_parse(
- apk_istream_gunzip_mpart(apk_istream_from_file(AT_FDCWD, match), apk_sign_ctx_mpart_cb, &sctx),
- read_file_entry, &ctx, idc);
- apk_sign_ctx_free(&sctx);
- if (r < 0) apk_err(out, "%s: %s", match, apk_error_str(r));
+ r = apk_extract(&ctx.ectx, apk_istream_from_file(AT_FDCWD, match));
+ if (r < 0 && r != -ECANCELED) apk_err(out, "%s: %s", match, apk_error_str(r));
}
-static void process_match(struct apk_database *db, const char *match, struct apk_name *name, void *ctx)
+static int process_match(struct apk_database *db, const char *match, struct apk_name *name, void *ctx)
{
struct apk_provider *p;
- if (name == NULL) {
+ if (!name) {
process_file(db, match);
- return;
+ return 0;
}
- foreach_array_item(p, name->providers)
+ apk_name_sorted_providers(name);
+ foreach_array_item(p, name->providers) {
+ if (p->pkg->name != name) continue;
process_package(db, p->pkg);
+ }
+ return 0;
}
static int manifest_main(void *applet_ctx, struct apk_ctx *ac, struct apk_string_array *args)
{
- apk_name_foreach_matching(ac->db, args, apk_foreach_genid(), process_match, NULL);
+ if (!args->num) return 0;
+ apk_db_foreach_sorted_name(ac->db, args, process_match, NULL);
return 0;
}
diff --git a/src/app_mkndx.c b/src/app_mkndx.c
index 3316de5..b1e809b 100644
--- a/src/app_mkndx.c
+++ b/src/app_mkndx.c
@@ -18,6 +18,7 @@
#include "apk_adb.h"
#include "apk_applet.h"
#include "apk_database.h"
+#include "apk_extract.h"
#include "apk_print.h"
struct mkndx_ctx {
@@ -29,9 +30,10 @@ struct mkndx_ctx {
apk_blob_t r;
struct adb db;
struct adb_obj pkgs;
+ struct adb_obj pkginfo;
time_t index_mtime;
- struct apk_sign_ctx sctx;
+ struct apk_extract_ctx ectx;
size_t file_size;
};
@@ -83,7 +85,7 @@ static int cmpfield(const void *pa, const void *pb)
return apk_blob_sort(a->str, b->str);
}
-static adb_val_t mkndx_read_v2_pkginfo(struct adb *db, struct apk_istream *is, size_t file_size, struct apk_sign_ctx *sctx, apk_blob_t rewrite_arch)
+static int mkndx_parse_v2meta(struct apk_extract_ctx *ectx, struct apk_istream *is)
{
static struct field fields[] = {
FIELD("arch", ADBI_PI_ARCH),
@@ -99,34 +101,35 @@ static adb_val_t mkndx_read_v2_pkginfo(struct adb *db, struct apk_istream *is, s
FIELD("pkgdesc", ADBI_PI_DESCRIPTION),
FIELD("pkgname", ADBI_PI_NAME),
FIELD("pkgver", ADBI_PI_VERSION),
- FIELD("provider_priority", 0),
+ FIELD("provider_priority", ADBI_PI_PROVIDER_PRIORITY),
FIELD("provides", ADBI_PI_PROVIDES),
FIELD("replaces", ADBI_PI_REPLACES),
- FIELD("replaces_priority", ADBI_PI_PRIORITY),
+ FIELD("replaces_priority", 0),
FIELD("size", ADBI_PI_INSTALLED_SIZE),
FIELD("triggers", 0),
FIELD("url", ADBI_PI_URL),
};
+ struct mkndx_ctx *ctx = container_of(ectx, struct mkndx_ctx, ectx);
struct field *f, key;
- struct adb_obj pkginfo, deps[3];
+ struct adb *db = &ctx->db;
+ struct adb_obj deps[3];
apk_blob_t line, k, v, token = APK_BLOB_STR("\n"), bdep;
int r, e = 0, i = 0;
- adb_wo_alloca(&pkginfo, &schema_pkginfo, db);
adb_wo_alloca(&deps[0], &schema_dependency_array, db);
adb_wo_alloca(&deps[1], &schema_dependency_array, db);
adb_wo_alloca(&deps[2], &schema_dependency_array, db);
while ((r = apk_istream_get_delim(is, token, &line)) == 0) {
- if (sctx) apk_sign_ctx_parse_pkginfo_line(sctx, line);
if (line.len < 1 || line.ptr[0] == '#') continue;
if (!apk_blob_split(line, APK_BLOB_STR(" = "), &k, &v)) continue;
+ apk_extract_v2_control(ectx, k, v);
key.str = k;
f = bsearch(&key, fields, ARRAY_SIZE(fields), sizeof(fields[0]), cmpfield);
if (!f || f->ndx == 0) continue;
- if (adb_ro_val(&pkginfo, f->ndx) != ADB_NULL) {
+ if (adb_ro_val(&ctx->pkginfo, f->ndx) != ADB_NULL) {
/* Workaround abuild bug that emitted multiple license lines */
if (f->ndx == ADBI_PI_LICENSE) continue;
return ADB_ERROR(APKE_ADB_PACKAGE_FORMAT);
@@ -134,7 +137,7 @@ static adb_val_t mkndx_read_v2_pkginfo(struct adb *db, struct apk_istream *is, s
switch (f->ndx) {
case ADBI_PI_ARCH:
- if (!APK_BLOB_IS_NULL(rewrite_arch)) v = rewrite_arch;
+ if (!APK_BLOB_IS_NULL(ctx->rewrite_arch)) v = ctx->rewrite_arch;
break;
case ADBI_PI_DEPENDS:
i = 0;
@@ -151,50 +154,42 @@ static adb_val_t mkndx_read_v2_pkginfo(struct adb *db, struct apk_istream *is, s
}
continue;
}
- adb_wo_pkginfo(&pkginfo, f->ndx, v);
+ adb_wo_pkginfo(&ctx->pkginfo, f->ndx, v);
}
if (r != -APKE_EOF) return ADB_ERROR(-r);
- adb_wo_arr(&pkginfo, ADBI_PI_DEPENDS, &deps[0]);
- adb_wo_arr(&pkginfo, ADBI_PI_PROVIDES, &deps[1]);
- adb_wo_arr(&pkginfo, ADBI_PI_REPLACES, &deps[2]);
- adb_wo_int(&pkginfo, ADBI_PI_FILE_SIZE, file_size);
+ adb_wo_arr(&ctx->pkginfo, ADBI_PI_DEPENDS, &deps[0]);
+ adb_wo_arr(&ctx->pkginfo, ADBI_PI_PROVIDES, &deps[1]);
+ adb_wo_arr(&ctx->pkginfo, ADBI_PI_REPLACES, &deps[2]);
- return adb_w_obj(&pkginfo);
+ return 0;
}
-static int mkndx_parse_v2_tar(void *pctx, const struct apk_file_info *ae, struct apk_istream *is)
+static int mkndx_parse_v3meta(struct apk_extract_ctx *ectx, struct adb_obj *pkg)
{
- struct mkndx_ctx *ctx = pctx;
- adb_val_t o;
- int r;
-
- r = apk_sign_ctx_process_file(&ctx->sctx, ae, is);
- if (r <= 0) return r;
- if (ctx->sctx.control_verified) return -ECANCELED;
- if (!ctx->sctx.control_started || ctx->sctx.data_started) return 0;
-
- if (strcmp(ae->name, ".PKGINFO") == 0) {
- o = adb_wa_append(
- &ctx->pkgs,
- mkndx_read_v2_pkginfo(
- &ctx->db, is, ctx->file_size, &ctx->sctx,
- ctx->rewrite_arch));
- if (ADB_IS_ERROR(o)) return -ADB_VAL_VALUE(o);
- }
+ struct mkndx_ctx *ctx = container_of(ectx, struct mkndx_ctx, ectx);
+ struct adb_obj pkginfo;
+
+ adb_ro_obj(pkg, ADBI_PKG_PKGINFO, &pkginfo);
+ adb_wo_copyobj(&ctx->pkginfo, &pkginfo);
return 0;
}
+static const struct apk_extract_ops extract_ndxinfo_ops = {
+ .v2meta = mkndx_parse_v2meta,
+ .v3meta = mkndx_parse_v3meta,
+};
+
static int mkndx_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)
{
struct apk_out *out = &ac->out;
- struct apk_id_cache *idc = apk_ctx_get_id_cache(ac);
struct apk_trust *trust = apk_ctx_get_trust(ac);
struct adb odb, tmpdb;
struct adb_obj oroot, opkgs, ndx, tmpl;
struct apk_file_info fi;
- adb_val_t match;
+ struct apk_checksum csum;
+ adb_val_t val;
int r, found, errors = 0, newpkgs = 0, numpkgs;
struct mkndx_ctx *ctx = pctx;
char **parg;
@@ -205,13 +200,16 @@ static int mkndx_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
return -1;
}
+ apk_extract_init(&ctx->ectx, ac, &extract_ndxinfo_ops);
+
adb_init(&odb);
adb_w_init_tmp(&tmpdb, 200);
adb_wo_alloca(&tmpl, &schema_pkginfo, &tmpdb);
- adb_w_init_alloca(&ctx->db, ADB_SCHEMA_INDEX, 1000);
+ adb_w_init_alloca(&ctx->db, ADB_SCHEMA_INDEX, 8000);
adb_wo_alloca(&ndx, &schema_index, &ctx->db);
adb_wo_alloca(&ctx->pkgs, &schema_pkginfo_array, &ctx->db);
+ adb_wo_alloca(&ctx->pkginfo, &schema_pkginfo, &ctx->db);
if (ctx->index) {
apk_fileinfo_get(AT_FDCWD, ctx->index, 0, &fi, 0);
@@ -221,7 +219,7 @@ static int mkndx_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
ADB_SCHEMA_INDEX, trust);
if (r) {
apk_err(out, "%s: %s", ctx->index, apk_error_str(r));
- return r;
+ goto done;
}
adb_ro_obj(adb_r_rootobj(&odb, &oroot, &schema_index), ADBI_NDX_PACKAGES, &opkgs);
}
@@ -240,6 +238,7 @@ static int mkndx_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
if (index_mtime >= fi.mtime) {
char *fname, *fend;
apk_blob_t bname, bver;
+ int i;
/* Check that it looks like a package name */
fname = strrchr(*parg, '/');
@@ -257,39 +256,38 @@ static int mkndx_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
adb_wo_resetdb(&tmpl);
adb_wo_blob(&tmpl, ADBI_PI_NAME, bname);
adb_wo_blob(&tmpl, ADBI_PI_VERSION, bver);
- match = adb_w_obj(&tmpl);
+ adb_wo_int(&tmpl, ADBI_PI_FILE_SIZE, fi.size);
- for (int i = 0; (i = adb_ra_find(&opkgs, i, &tmpdb, match)) > 0; ) {
+ if ((i = adb_ra_find(&opkgs, 0, &tmpl)) > 0) {
struct adb_obj pkg;
- adb_val_t val;
-
adb_ro_obj(&opkgs, i, &pkg);
- if (apk_blob_compare(bname, adb_ro_blob(&pkg, ADBI_PI_NAME))) continue;
- if (apk_blob_compare(bver, adb_ro_blob(&pkg, ADBI_PI_VERSION))) continue;
- if (fi.size != adb_ro_int(&pkg, ADBI_PI_FILE_SIZE)) continue;
val = adb_wa_append(&ctx->pkgs, adb_w_copy(&ctx->db, &odb, adb_ro_val(&opkgs, i)));
- if (ADB_IS_ERROR(val))
- errors++;
-
found = TRUE;
- break;
}
}
if (!found) {
do_file:
- apk_sign_ctx_init(&ctx->sctx, APK_SIGN_VERIFY, NULL, trust);
- r = apk_tar_parse(
- apk_istream_gunzip_mpart(apk_istream_from_file(AT_FDCWD, *parg), apk_sign_ctx_mpart_cb, &ctx->sctx),
- mkndx_parse_v2_tar, ctx, idc);
- apk_sign_ctx_free(&ctx->sctx);
+ apk_extract_reset(&ctx->ectx);
+ apk_extract_generate_identity(&ctx->ectx, &csum);
+ csum.type = APK_CHECKSUM_NONE;
+ r = apk_extract(&ctx->ectx, apk_istream_from_file(AT_FDCWD, *parg));
if (r < 0 && r != -ECANCELED) goto err_pkg;
+
+ adb_wo_int(&ctx->pkginfo, ADBI_PI_FILE_SIZE, ctx->file_size);
+ if (csum.type != APK_CHECKSUM_NONE)
+ adb_wo_blob(&ctx->pkginfo, ADBI_PI_UNIQUE_ID,
+ APK_BLOB_CSUM(csum));
+
+ val = adb_wa_append_obj(&ctx->pkgs, &ctx->pkginfo);
newpkgs++;
}
+ if (ADB_IS_ERROR(val)) errors++;
}
if (errors) {
apk_err(out, "%d errors, not creating index", errors);
- return -1;
+ r = -1;
+ goto done;
}
numpkgs = adb_ra_num(&ctx->pkgs);
@@ -301,14 +299,15 @@ static int mkndx_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
apk_ostream_to_file(AT_FDCWD, ctx->output, 0644),
&ctx->db, trust);
- adb_free(&ctx->db);
- adb_free(&odb);
-
if (r == 0)
apk_msg(out, "Index has %d packages (of which %d are new)", numpkgs, newpkgs);
else
apk_err(out, "Index creation failed: %s", apk_error_str(r));
+done:
+ adb_wo_free(&ctx->pkgs);
+ adb_free(&ctx->db);
+ adb_free(&odb);
#if 0
apk_hash_foreach(&db->available.names, warn_if_no_providers, &counts);
diff --git a/src/app_mkpkg.c b/src/app_mkpkg.c
index 00f467c..2d9b9d7 100644
--- a/src/app_mkpkg.c
+++ b/src/app_mkpkg.c
@@ -11,7 +11,6 @@
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
-#include <assert.h>
#include <unistd.h>
#include <sys/stat.h>
@@ -20,7 +19,9 @@
#include "apk_applet.h"
#include "apk_database.h"
#include "apk_pathbuilder.h"
+#include "apk_extract.h"
#include "apk_print.h"
+#include "apk_xattr.h"
#define BLOCK_SIZE 4096
@@ -29,42 +30,101 @@ struct mkpkg_ctx {
const char *files_dir, *output;
struct adb db;
struct adb_obj paths, *files;
- struct apk_sign_ctx sctx;
+ struct apk_extract_ctx ectx;
+ apk_blob_t package[ADBI_PKG_MAX];
apk_blob_t info[ADBI_PI_MAX];
+ apk_blob_t script[ADBI_SCRPT_MAX];
+ struct apk_string_array *triggers;
uint64_t installed_size;
struct apk_pathbuilder pb;
+ struct adb_compression_spec spec;
+ unsigned has_scripts : 1;
};
#define MKPKG_OPTIONS(OPT) \
- OPT(OPT_MKPKG_files, APK_OPT_ARG APK_OPT_SH("f") "files") \
- OPT(OPT_MKPKG_info, APK_OPT_ARG APK_OPT_SH("i") "info") \
- OPT(OPT_MKPKG_output, APK_OPT_ARG APK_OPT_SH("o") "output") \
+ OPT(OPT_MKPKG_compression, APK_OPT_ARG APK_OPT_SH("c") "compression") \
+ OPT(OPT_MKPKG_files, APK_OPT_ARG APK_OPT_SH("F") "files") \
+ OPT(OPT_MKPKG_info, APK_OPT_ARG APK_OPT_SH("I") "info") \
+ OPT(OPT_MKPKG_output, APK_OPT_ARG APK_OPT_SH("o") "output") \
+ OPT(OPT_MKPKG_script, APK_OPT_ARG APK_OPT_SH("s") "script") \
+ OPT(OPT_MKPKG_trigger, APK_OPT_ARG APK_OPT_SH("t") "trigger") \
APK_OPT_APPLET(option_desc, MKPKG_OPTIONS);
+static int parse_info(struct mkpkg_ctx *ictx, struct apk_out *out, const char *optarg)
+{
+ apk_blob_t l, r;
+ int i;
+
+ if (!apk_blob_split(APK_BLOB_STR(optarg), APK_BLOB_STRLIT(":"), &l, &r))
+ goto inval;
+
+ i = adb_s_field_by_name_blob(&schema_pkginfo, l);
+ switch (i) {
+ case 0:
+ break;
+ case ADBI_PI_FILE_SIZE:
+ case ADBI_PI_INSTALLED_SIZE:
+ return -EINVAL;
+ default:
+ ictx->info[i] = r;
+ return 0;
+ }
+
+ i = adb_s_field_by_name_blob(&schema_package, l);
+ switch (i) {
+ case ADBI_PKG_REPLACES_PRIORITY:
+ ictx->package[i] = r;
+ return 0;
+ default:
+ break;
+ }
+
+inval:
+ apk_err(out, "invalid info field: " BLOB_FMT, BLOB_PRINTF(l));
+ return -EINVAL;
+}
+
static int option_parse_applet(void *ctx, struct apk_ctx *ac, int optch, const char *optarg)
{
struct apk_out *out = &ac->out;
struct mkpkg_ctx *ictx = ctx;
apk_blob_t l, r;
- int i;
+ int i, ret;
switch (optch) {
- case OPT_MKPKG_info:
- apk_blob_split(APK_BLOB_STR(optarg), APK_BLOB_STRLIT(":"), &l, &r);
- i = adb_s_field_by_name_blob(&schema_pkginfo, l);
- if (!i || i == ADBI_PI_FILE_SIZE || i == ADBI_PI_INSTALLED_SIZE) {
- apk_err(out, "invalid pkginfo field: " BLOB_FMT, BLOB_PRINTF(l));
+ case OPT_MKPKG_compression:
+ if (adb_parse_compression(optarg, &ictx->spec) != 0) {
+ apk_err(out, "invalid compression type: %s", optarg);
return -EINVAL;
}
- ictx->info[i] = r;
break;
+ case OPT_MKPKG_info:
+ return parse_info(ictx, out, optarg);
case OPT_MKPKG_files:
ictx->files_dir = optarg;
break;
case OPT_MKPKG_output:
ictx->output = optarg;
break;
+ case OPT_MKPKG_script:
+ apk_blob_split(APK_BLOB_STR(optarg), APK_BLOB_STRLIT(":"), &l, &r);
+ i = adb_s_field_by_name_blob(&schema_scripts, l);
+ if (!i) {
+ apk_err(out, "invalid script type: " BLOB_FMT, BLOB_PRINTF(l));
+ return -EINVAL;
+ }
+ ret = apk_blob_from_file(AT_FDCWD, r.ptr, &ictx->script[i]);
+ if (ret) {
+ apk_err(out, "failed to load script: " BLOB_FMT ": %s",
+ BLOB_PRINTF(r), apk_error_str(ret));
+ return ret;
+ }
+ ictx->has_scripts = 1;
+ break;
+ case OPT_MKPKG_trigger:
+ *apk_string_array_add(&ictx->triggers) = (char*) optarg;
+ break;
default:
return -ENOTSUP;
}
@@ -76,6 +136,42 @@ static const struct apk_option_group optgroup_applet = {
.parse = option_parse_applet,
};
+static adb_val_t create_xattrs(struct adb *db, int fd)
+{
+ struct adb_obj xa;
+ char names[1024], buf[1024];
+ ssize_t len, vlen;
+ adb_val_t val;
+ int i;
+
+ len = apk_flistxattr(fd, names, sizeof names);
+ if (len <= 0) return ADB_NULL;
+
+ adb_wo_alloca(&xa, &schema_xattr_array, db);
+ for (i = 0; i < len; i += strlen(&names[i]) + 1) {
+ vlen = apk_fgetxattr(fd, &names[i], buf, sizeof buf);
+ if (vlen < 0) continue;
+
+ apk_blob_t vec[] = {
+ APK_BLOB_PTR_LEN(&names[i], strlen(&names[i])+1),
+ APK_BLOB_PTR_LEN(buf, vlen),
+ };
+ adb_wa_append(&xa, adb_w_blob_vec(db, ARRAY_SIZE(vec), vec));
+ }
+ close(fd);
+ val = adb_w_arr(&xa);
+ adb_wo_free(&xa);
+
+ return val;
+}
+
+static adb_val_t create_xattrs_closefd(struct adb *db, int fd)
+{
+ adb_val_t val = create_xattrs(db, fd);
+ close(fd);
+ return val;
+}
+
static int mkpkg_process_dirent(void *pctx, int dirfd, const char *entry);
static int mkpkg_process_directory(struct mkpkg_ctx *ctx, int dirfd, struct apk_file_info *fi)
@@ -93,6 +189,7 @@ static int mkpkg_process_directory(struct mkpkg_ctx *ctx, int dirfd, struct apk_
adb_wo_int(&acl, ADBI_ACL_MODE, fi->mode & ~S_IFMT);
adb_wo_blob(&acl, ADBI_ACL_USER, apk_id_cache_resolve_user(idc, fi->uid));
adb_wo_blob(&acl, ADBI_ACL_GROUP, apk_id_cache_resolve_group(idc, fi->gid));
+ adb_wo_val(&acl, ADBI_ACL_XATTRS, create_xattrs(&ctx->db, dirfd));
adb_wo_obj(&fio, ADBI_DI_ACL, &acl);
adb_wo_alloca(&files, &schema_file_array, &ctx->db);
@@ -103,12 +200,14 @@ static int mkpkg_process_directory(struct mkpkg_ctx *ctx, int dirfd, struct apk_
if (r) {
apk_err(out, "failed to process directory '%s': %d",
apk_pathbuilder_cstr(&ctx->pb), r);
- return r;
+ goto done;
}
adb_wo_obj(&fio, ADBI_DI_FILES, &files);
adb_wa_append_obj(&ctx->paths, &fio);
- return 0;
+done:
+ adb_wo_free(&files);
+ return r;
}
static int mkpkg_process_dirent(void *pctx, int dirfd, const char *entry)
@@ -131,7 +230,7 @@ static int mkpkg_process_dirent(void *pctx, int dirfd, const char *entry)
char target[1022];
} symlink;
} ft;
- int r;
+ int r, n;
r = apk_fileinfo_get(dirfd, entry, APK_FI_NOFOLLOW | APK_FI_DIGEST(APK_DIGEST_SHA256), &fi, NULL);
if (r) return r;
@@ -143,27 +242,26 @@ static int mkpkg_process_dirent(void *pctx, int dirfd, const char *entry)
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
- ft.dev.mode = fi.mode & S_IFMT;
- ft.dev.dev = fi.device;
+ ft.dev.mode = htole16(fi.mode & S_IFMT);
+ ft.dev.dev = htole64(fi.device);
target = APK_BLOB_STRUCT(ft.dev);
break;
case S_IFLNK:
- ft.symlink.mode = fi.mode & S_IFMT;
+ ft.symlink.mode = htole16(fi.mode & S_IFMT);
r = readlinkat(dirfd, entry, ft.symlink.target, sizeof ft.symlink.target);
if (r < 0) return r;
target = APK_BLOB_PTR_LEN((void*)&ft.symlink, sizeof(ft.symlink.mode) + r);
r = 0;
break;
case S_IFDIR:
- apk_pathbuilder_push(&ctx->pb, entry);
+ n = apk_pathbuilder_push(&ctx->pb, entry);
r = mkpkg_process_directory(ctx, openat(dirfd, entry, O_RDONLY), &fi);
- apk_pathbuilder_pop(&ctx->pb);
+ apk_pathbuilder_pop(&ctx->pb, n);
return r;
default:
- apk_pathbuilder_push(&ctx->pb, entry);
- apk_out(out, "%s: special file ignored",
- apk_pathbuilder_cstr(&ctx->pb), entry);
- apk_pathbuilder_pop(&ctx->pb);
+ n = apk_pathbuilder_push(&ctx->pb, entry);
+ apk_out(out, "%s: special file ignored", apk_pathbuilder_cstr(&ctx->pb));
+ apk_pathbuilder_pop(&ctx->pb, n);
return 0;
}
@@ -180,6 +278,7 @@ static int mkpkg_process_dirent(void *pctx, int dirfd, const char *entry)
adb_wo_int(&acl, ADBI_ACL_MODE, fi.mode & 07777);
adb_wo_blob(&acl, ADBI_ACL_USER, apk_id_cache_resolve_user(idc, fi.uid));
adb_wo_blob(&acl, ADBI_ACL_GROUP, apk_id_cache_resolve_group(idc, fi.gid));
+ adb_wo_val(&acl, ADBI_ACL_XATTRS, create_xattrs_closefd(&ctx->db, openat(dirfd, entry, O_RDONLY)));
adb_wo_obj(&fio, ADBI_FI_ACL, &acl);
adb_wa_append_obj(ctx->files, &fio);
@@ -199,6 +298,33 @@ static char *pkgi_filename(struct adb_obj *pkgi, char *buf, size_t n)
return buf;
}
+static int check_required(struct apk_out *out, apk_blob_t *vals, int index, const struct adb_object_schema *schema)
+{
+ if (!APK_BLOB_IS_NULL(vals[index])) return 0;
+ apk_err(out, "required info field '%s' not provided",
+ schema->fields[index-1].name);
+ return -EINVAL;
+}
+
+static int assign_fields(struct apk_out *out, apk_blob_t *vals, int num_vals, struct adb_obj *obj)
+{
+ int i, r;
+
+ for (i = 0; i < num_vals; i++) {
+ apk_blob_t b = vals[i];
+ if (APK_BLOB_IS_NULL(b)) continue;
+
+ adb_val_t val = adb_wo_val_fromstring(obj, i, b);
+ if (ADB_IS_ERROR(val)) {
+ r = ADB_VAL_VALUE(val);
+ apk_err(out, "info field '%s' has invalid value: %s",
+ obj->schema->fields[i-1].name, apk_error_str(r));
+ return r;
+ }
+ }
+ return 0;
+}
+
static int mkpkg_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)
{
struct apk_out *out = &ac->out;
@@ -207,7 +333,10 @@ static int mkpkg_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
int i, j, r;
struct mkpkg_ctx *ctx = pctx;
struct apk_ostream *os;
+ struct apk_digest d = {};
char outbuf[PATH_MAX];
+ const int uid_len = apk_digest_alg_len(APK_DIGEST_SHA1);
+ apk_blob_t uid = APK_BLOB_PTR_LEN((char*)d.data, uid_len);
ctx->ac = ac;
adb_w_init_alloca(&ctx->db, ADB_SCHEMA_PACKAGE, 40);
@@ -216,23 +345,19 @@ static int mkpkg_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
adb_wo_alloca(&ctx->paths, &schema_dir_array, &ctx->db);
// prepare package info
- for (i = 0; i < ARRAY_SIZE(ctx->info); i++) {
- apk_blob_t val = ctx->info[i];
- if (APK_BLOB_IS_NULL(val)) {
- switch (i) {
- case ADBI_PI_NAME:
- case ADBI_PI_VERSION:
- r = -EINVAL;
- apk_err(out, "required pkginfo field '%s' not provided",
- schema_pkginfo.fields[i-1].name);
- goto err;
- }
- continue;
- }
- adb_wo_val_fromstring(&pkgi, i, val);
- }
- if (adb_ro_val(&pkgi, ADBI_PI_ARCH) == ADB_VAL_NULL)
- adb_wo_blob(&pkgi, ADBI_PI_ARCH, APK_BLOB_STRLIT(APK_DEFAULT_ARCH));
+ r = -EINVAL;
+ if (check_required(out, ctx->info, ADBI_PI_NAME, &schema_pkginfo) ||
+ check_required(out, ctx->info, ADBI_PI_VERSION, &schema_pkginfo))
+ goto err;
+
+ if (APK_BLOB_IS_NULL(ctx->info[ADBI_PI_ARCH]))
+ ctx->info[ADBI_PI_ARCH] = APK_BLOB_STRLIT(APK_DEFAULT_ARCH);
+
+ r = assign_fields(out, ctx->info, ARRAY_SIZE(ctx->info), &pkgi);
+ if (r) goto err;
+
+ r = assign_fields(out, ctx->package, ARRAY_SIZE(ctx->package), &pkg);
+ if (r) goto err;
// scan and add all files
if (ctx->files_dir) {
@@ -249,8 +374,25 @@ static int mkpkg_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
}
adb_wo_int(&pkgi, ADBI_PI_INSTALLED_SIZE, ctx->installed_size);
+ adb_wo_blob(&pkgi, ADBI_PI_UNIQUE_ID, uid);
+
adb_wo_obj(&pkg, ADBI_PKG_PKGINFO, &pkgi);
adb_wo_obj(&pkg, ADBI_PKG_PATHS, &ctx->paths);
+ if (ctx->has_scripts) {
+ struct adb_obj scripts;
+ adb_wo_alloca(&scripts, &schema_scripts, &ctx->db);
+ for (i = ADBI_FIRST; i < ADBI_SCRPT_MAX; i++)
+ adb_wo_blob(&scripts, i, ctx->script[i]);
+ adb_wo_obj(&pkg, ADBI_PKG_SCRIPTS, &scripts);
+ }
+ if (ctx->triggers) {
+ struct adb_obj triggers;
+ adb_wo_alloca(&triggers, &schema_string_array, &ctx->db);
+ for (i = 0; i < ctx->triggers->num; i++)
+ adb_wa_append_fromstring(&triggers, APK_BLOB_STR(ctx->triggers->item[i]));
+ adb_wo_obj(&pkg, ADBI_PKG_TRIGGERS, &triggers);
+ adb_wo_free(&triggers);
+ }
adb_w_rootobj(&pkg);
// re-read since object resets
@@ -258,13 +400,23 @@ static int mkpkg_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
adb_ro_obj(&pkg, ADBI_PKG_PKGINFO, &pkgi);
adb_ro_obj(&pkg, ADBI_PKG_PATHS, &ctx->paths);
+ // fill in unique id
+ apk_digest_calc(&d, APK_DIGEST_SHA256, ctx->db.adb.ptr, ctx->db.adb.len);
+ uid = adb_ro_blob(&pkgi, ADBI_PI_UNIQUE_ID);
+ memcpy(uid.ptr, d.data, uid.len);
+
if (!ctx->output) {
ctx->output = pkgi_filename(&pkgi, outbuf, sizeof outbuf);
}
// construct package with ADB as header, and the file data in
// concatenated data blocks
- os = adb_compress(apk_ostream_to_file(AT_FDCWD, ctx->output, 0644), ADB_COMP_DEFLATE);
+ os = adb_compress(apk_ostream_to_file(AT_FDCWD, ctx->output, 0644), &ctx->spec);
+ if (IS_ERR(os)) {
+ r = PTR_ERR(os);
+ goto err;
+ }
+
adb_c_adb(os, &ctx->db, trust);
int files_fd = openat(AT_FDCWD, ctx->files_dir, O_RDONLY);
for (i = ADBI_FIRST; i <= adb_ra_num(&ctx->paths); i++) {
@@ -278,28 +430,29 @@ static int mkpkg_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *a
adb_ro_obj(&files, j, &file);
apk_blob_t filename = adb_ro_blob(&file, ADBI_FI_NAME);
apk_blob_t target = adb_ro_blob(&file, ADBI_FI_TARGET);
- size_t sz = adb_ro_int(&file, ADBI_FI_SIZE);
+ uint64_t sz = adb_ro_int(&file, ADBI_FI_SIZE);
if (!APK_BLOB_IS_NULL(target)) continue;
if (!sz) continue;
struct adb_data_package hdr = {
- .path_idx = i,
- .file_idx = j,
+ .path_idx = htole32(i),
+ .file_idx = htole32(j),
};
- apk_pathbuilder_pushb(&ctx->pb, filename);
+ int n = apk_pathbuilder_pushb(&ctx->pb, filename);
adb_c_block_data(
os, APK_BLOB_STRUCT(hdr), sz,
apk_istream_from_fd(openat(files_fd,
apk_pathbuilder_cstr(&ctx->pb),
O_RDONLY)));
- apk_pathbuilder_pop(&ctx->pb);
+ apk_pathbuilder_pop(&ctx->pb, n);
}
}
close(files_fd);
r = apk_ostream_close(os);
err:
+ adb_wo_free(&ctx->paths);
adb_free(&ctx->db);
- if (r) apk_err(out, "failed to create package: %s", apk_error_str(r));
+ if (r) apk_err(out, "failed to create package: %s: %s", ctx->output, apk_error_str(r));
return r;
}
diff --git a/src/app_policy.c b/src/app_policy.c
index c026337..cf4e2c3 100644
--- a/src/app_policy.c
+++ b/src/app_policy.c
@@ -13,16 +13,14 @@
#include "apk_version.h"
#include "apk_print.h"
-extern const char * const apk_installed_file;
-
-static void print_policy(struct apk_database *db, const char *match, struct apk_name *name, void *ctx)
+static int print_policy(struct apk_database *db, const char *match, struct apk_name *name, void *ctx)
{
struct apk_out *out = &db->ctx->out;
struct apk_provider *p;
struct apk_repository *repo;
int i, j, num = 0;
- if (!name) return;
+ if (!name) return 0;
/*
zlib1g policy:
@@ -38,14 +36,13 @@ zlib1g policy:
1.1:
http://nl.alpinelinux.org/alpine/v2.4/main
*/
+ apk_name_sorted_providers(name);
foreach_array_item(p, name->providers) {
- if (p->pkg->name != name)
- continue;
- if (num++ == 0)
- apk_out(out, "%s policy:", name->name);
+ if (p->pkg->name != name) continue;
+ if (num++ == 0) apk_out(out, "%s policy:", name->name);
apk_out(out, " " BLOB_FMT ":", BLOB_PRINTF(*p->version));
- if (p->pkg->ipkg != NULL)
- apk_out(out, " %s", apk_installed_file);
+ if (p->pkg->ipkg)
+ apk_out(out, " %s/installed", apk_db_layer_name(p->pkg->layer));
for (i = 0; i < db->num_repos; i++) {
repo = &db->repos[i];
if (!(BIT(i) & p->pkg->repos))
@@ -59,11 +56,13 @@ zlib1g policy:
}
}
}
+ return 0;
}
static int policy_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *args)
{
- apk_name_foreach_matching(ac->db, args, apk_foreach_genid(), print_policy, NULL);
+ if (!args->num) return 0;
+ apk_db_foreach_sorted_name(ac->db, args, print_policy, NULL);
return 0;
}
diff --git a/src/app_search.c b/src/app_search.c
index 4ac4243..aebbfeb 100644
--- a/src/app_search.c
+++ b/src/app_search.c
@@ -19,25 +19,18 @@ struct search_ctx {
void (*print_package)(struct search_ctx *ctx, struct apk_package *pkg);
int verbosity;
- int show_all : 1;
- int search_exact : 1;
- int search_description : 1;
- int search_origin : 1;
+ unsigned int show_all : 1;
+ unsigned int search_exact : 1;
+ unsigned int search_description : 1;
+ unsigned int search_origin : 1;
unsigned int matches;
struct apk_string_array *filter;
+ struct apk_package *prev_match;
};
-static int unique_match(struct apk_package *pkg)
-{
- if (pkg->state_int) return 0;
- pkg->state_int = 1;
- return 1;
-}
-
static void print_package_name(struct search_ctx *ctx, struct apk_package *pkg)
{
- if (!unique_match(pkg)) return;
printf("%s", pkg->name->name);
if (ctx->verbosity > 0)
printf("-" BLOB_FMT, BLOB_PRINTF(*pkg->version));
@@ -48,7 +41,6 @@ static void print_package_name(struct search_ctx *ctx, struct apk_package *pkg)
static void print_origin_name(struct search_ctx *ctx, struct apk_package *pkg)
{
- if (!unique_match(pkg)) return;
if (pkg->origin != NULL)
printf(BLOB_FMT, BLOB_PRINTF(*pkg->origin));
else
@@ -93,7 +85,6 @@ static int option_parse_applet(void *ctx, struct apk_ctx *ac, int opt, const cha
break;
case OPT_SEARCH_description:
ictx->search_description = 1;
- ictx->search_exact = 1;
ictx->show_all = 1;
break;
case OPT_SEARCH_exact:
@@ -127,15 +118,16 @@ static void print_result_pkg(struct search_ctx *ctx, struct apk_package *pkg)
if (ctx->search_description) {
foreach_array_item(pmatch, ctx->filter) {
- if (strstr(pkg->description, *pmatch) != NULL ||
- strstr(pkg->name->name, *pmatch) != NULL)
+ if (fnmatch(*pmatch, pkg->description, FNM_CASEFOLD) == 0 ||
+ fnmatch(*pmatch, pkg->name->name, FNM_CASEFOLD) == 0)
goto match;
}
return;
}
if (ctx->search_origin) {
foreach_array_item(pmatch, ctx->filter) {
- if (pkg->origin && apk_blob_compare(APK_BLOB_STR(*pmatch), *pkg->origin) == 0)
+ if (!pkg->origin) continue;
+ if (apk_blob_compare(APK_BLOB_STR(*pmatch), *pkg->origin) == 0)
goto match;
}
return;
@@ -144,31 +136,28 @@ match:
ctx->print_result(ctx, pkg);
}
-static void print_result(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
+static int print_result(struct apk_database *db, const char *match, struct apk_package *pkg, void *pctx)
{
struct search_ctx *ctx = pctx;
- struct apk_provider *p;
- struct apk_package *pkg = NULL;
- if (!name) return;
+ if (!pkg) return 0;
if (ctx->show_all) {
- foreach_array_item(p, name->providers)
- print_result_pkg(ctx, p->pkg);
- } else {
- foreach_array_item(p, name->providers) {
- if (pkg == NULL ||
- apk_version_compare_blob(*p->version, *pkg->version) == APK_VERSION_GREATER)
- pkg = p->pkg;
- }
- if (pkg)
- print_result_pkg(ctx, pkg);
+ print_result_pkg(ctx, pkg);
+ return 0;
}
-}
-static int print_pkg(apk_hash_item item, void *pctx)
-{
- print_result_pkg((struct search_ctx *) pctx, (struct apk_package *) item);
+ if (!ctx->prev_match) {
+ ctx->prev_match = pkg;
+ return 0;
+ }
+ if (ctx->prev_match->name != pkg->name) {
+ print_result_pkg(ctx, ctx->prev_match);
+ ctx->prev_match = pkg;
+ return 0;
+ }
+ if (apk_pkg_version_compare(pkg, ctx->prev_match) == APK_VERSION_GREATER)
+ ctx->prev_match = pkg;
return 0;
}
@@ -186,8 +175,11 @@ static int search_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *
if (ctx->print_result == NULL)
ctx->print_result = ctx->print_package;
- if (ctx->search_description || ctx->search_origin)
- return apk_hash_foreach(&db->available.packages, print_pkg, ctx);
+ if (ctx->search_description || ctx->search_origin) {
+ // Just enumerate all names in sorted order, and do the
+ // filtering in the callback.
+ args = NULL;
+ }
if (!ctx->search_exact) {
foreach_array_item(pmatch, ctx->filter) {
@@ -196,17 +188,17 @@ static int search_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *
*pmatch = tmp;
}
}
- apk_name_foreach_matching(
- db, args, APK_FOREACH_NULL_MATCHES_ALL | apk_foreach_genid(),
- print_result, ctx);
+ apk_db_foreach_sorted_providers(db, args, print_result, ctx);
+ if (ctx->prev_match) print_result_pkg(ctx, ctx->prev_match);
+
return 0;
}
static struct apk_applet apk_search = {
.name = "search",
- .open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE,
+ .open_flags = APK_OPENF_READ | APK_OPENF_NO_STATE | APK_OPENF_ALLOW_ARCH,
.context_size = sizeof(struct search_ctx),
- .optgroups = { &optgroup_global, &optgroup_applet },
+ .optgroups = { &optgroup_global, &optgroup_source, &optgroup_applet },
.main = search_main,
};
diff --git a/src/app_update.c b/src/app_update.c
index 533cd1c..2432a5e 100644
--- a/src/app_update.c
+++ b/src/app_update.c
@@ -24,7 +24,7 @@ static int update_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *a
char buf[32] = "OK:";
if (apk_out_verbosity(out) < 1)
- return db->repo_update_errors;
+ return db->repositories.unavailable + db->repositories.stale;
for (i = 0; i < db->num_repos; i++) {
repo = &db->repos[i];
@@ -38,18 +38,20 @@ static int update_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *a
URL_PRINTF(urlp));
}
- if (db->repo_update_errors != 0)
- snprintf(buf, sizeof(buf), "%d errors;",
- db->repo_update_errors);
+ if (db->repositories.unavailable || db->repositories.stale)
+ snprintf(buf, sizeof(buf), "%d unavailable, %d stale;",
+ db->repositories.unavailable,
+ db->repositories.stale);
+
apk_msg(out, "%s %d distinct packages available", buf,
db->available.packages.num_items);
- return db->repo_update_errors;
+ return db->repositories.unavailable + db->repositories.stale;
}
static struct apk_applet apk_update = {
.name = "update",
- .open_flags = APK_OPENF_WRITE,
+ .open_flags = APK_OPENF_WRITE | APK_OPENF_ALLOW_ARCH,
.forced_force = APK_FORCE_REFRESH,
.main = update_main,
};
diff --git a/src/app_upgrade.c b/src/app_upgrade.c
index 3edd0b1..fd74a68 100644
--- a/src/app_upgrade.c
+++ b/src/app_upgrade.c
@@ -20,10 +20,10 @@ extern char **apk_argv;
struct upgrade_ctx {
unsigned short solver_flags;
- int no_self_upgrade : 1;
- int self_upgrade_only : 1;
- int ignore : 1;
- int prune : 1;
+ unsigned short no_self_upgrade : 1;
+ unsigned short self_upgrade_only : 1;
+ unsigned short ignore : 1;
+ unsigned short prune : 1;
int errors;
};
@@ -91,7 +91,7 @@ int apk_do_self_upgrade(struct apk_database *db, unsigned short solver_flags, un
struct apk_package *pkg0 = p0->pkg;
if (pkg0->name != name || pkg0->repos == 0)
continue;
- if (apk_version_compare_blob(*pkg0->version, *pkg->version) == APK_VERSION_GREATER) {
+ if (apk_version_match(*pkg0->version, APK_VERSION_GREATER, *pkg->version)) {
r = 1;
break;
}
@@ -139,7 +139,7 @@ ret:
return r;
}
-static void set_upgrade_for_name(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
+static int set_upgrade_for_name(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
{
struct apk_out *out = &db->ctx->out;
struct upgrade_ctx *uctx = (struct upgrade_ctx *) pctx;
@@ -147,10 +147,11 @@ static void set_upgrade_for_name(struct apk_database *db, const char *match, str
if (!name) {
apk_err(out, "Package '%s' not found", match);
uctx->errors++;
- return;
+ return 0;
}
apk_solver_set_name_flags(name, uctx->ignore ? APK_SOLVERF_INSTALLED : APK_SOLVERF_UPGRADE, 0);
+ return 0;
}
static int upgrade_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *args)
@@ -170,6 +171,7 @@ static int upgrade_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *
"Use --force-broken-world to override.");
return -1;
}
+ if (apk_db_repository_check(db) != 0) return -1;
solver_flags = APK_SOLVERF_UPGRADE | uctx->solver_flags;
if (!uctx->no_self_upgrade && !args->num) {
@@ -184,8 +186,8 @@ static int upgrade_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *
apk_dependency_array_copy(&world, db->world);
if (solver_flags & APK_SOLVERF_AVAILABLE) {
foreach_array_item(dep, world) {
- if (dep->result_mask == APK_DEPMASK_CHECKSUM) {
- dep->result_mask = APK_DEPMASK_ANY;
+ if (dep->op == APK_DEPMASK_CHECKSUM) {
+ dep->op = APK_DEPMASK_ANY;
dep->version = &apk_atom_null;
}
}
@@ -209,7 +211,7 @@ static int upgrade_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *
if (args->num > 0) {
/* if specific packages are listed, we don't want to upgrade world. */
if (!uctx->ignore) solver_flags &= ~APK_SOLVERF_UPGRADE;
- apk_name_foreach_matching(db, args, apk_foreach_genid(), set_upgrade_for_name, uctx);
+ apk_db_foreach_matching_name(db, args, set_upgrade_for_name, uctx);
if (uctx->errors) return uctx->errors;
}
diff --git a/src/app_verify.c b/src/app_verify.c
index adfe0ec..b711800 100644
--- a/src/app_verify.c
+++ b/src/app_verify.c
@@ -12,38 +12,25 @@
#include <unistd.h>
#include "apk_applet.h"
-#include "apk_database.h"
#include "apk_print.h"
+#include "apk_extract.h"
static int verify_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *args)
{
struct apk_out *out = &ac->out;
- struct apk_sign_ctx sctx;
- struct apk_id_cache *idc = apk_ctx_get_id_cache(ac);
- struct apk_trust *trust = apk_ctx_get_trust(ac);
+ struct apk_extract_ctx ectx;
char **parg;
- int r, ok, rc = 0;
-
- trust->allow_untrusted = 1;
+ int r, rc = 0;
foreach_array_item(parg, args) {
- apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY, NULL, trust);
- r = apk_tar_parse(
- apk_istream_gunzip_mpart(apk_istream_from_file(AT_FDCWD, *parg),
- apk_sign_ctx_mpart_cb, &sctx),
- apk_sign_ctx_verify_tar, &sctx, idc);
- ok = sctx.control_verified && sctx.data_verified;
+ apk_extract_init(&ectx, ac, 0);
+ r = apk_extract(&ectx, apk_istream_from_file(AT_FDCWD, *parg));
if (apk_out_verbosity(out) >= 1)
- apk_msg(out, "%s: %d - %s", *parg, r,
- r < 0 ? apk_error_str(r) :
- ok ? "OK" :
- !sctx.control_verified ? "UNTRUSTED" : "FAILED");
- else if (!ok)
+ apk_msg(out, "%s: %s", *parg,
+ r < 0 ? apk_error_str(r) : "OK");
+ else if (r < 0)
apk_out(out, "%s", *parg);
- if (!ok)
- rc++;
-
- apk_sign_ctx_free(&sctx);
+ if (r < 0) rc++;
}
return rc;
diff --git a/src/app_version.c b/src/app_version.c
index a42ab63..bc893d8 100644
--- a/src/app_version.c
+++ b/src/app_version.c
@@ -17,7 +17,8 @@
struct ver_ctx {
int (*action)(struct apk_database *db, struct apk_string_array *args);
const char *limchars;
- int all_tags : 1;
+ unsigned int max_pkg_len;
+ unsigned short all_tags : 1;
};
static int ver_indexes(struct apk_database *db, struct apk_string_array *args)
@@ -45,10 +46,8 @@ static int ver_test(struct apk_database *db, struct apk_string_array *args)
struct apk_out *out = &db->ctx->out;
int r;
- if (args->num != 2)
- return 1;
-
- r = apk_version_compare(args->item[0], args->item[1]);
+ if (args->num != 2) return 1;
+ r = apk_version_compare(APK_BLOB_STR(args->item[0]), APK_BLOB_STR(args->item[1]));
apk_out(out, "%s", apk_version_op_string(r));
return 0;
}
@@ -109,37 +108,44 @@ static const struct apk_option_group optgroup_applet = {
.parse = option_parse_applet,
};
-static void ver_print_package_status(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
+struct ver_name_state {
+ struct apk_package *installed, *latest;
+ unsigned short tag, ver_result;
+};
+
+static struct ver_name_state *state_from_name(struct apk_name *name)
+{
+ static_assert(sizeof name->state_buf >= sizeof(struct ver_name_state));
+ return (struct ver_name_state *) &name->state_buf[0];
+}
+
+static int ver_calculate_length(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
{
- struct apk_out *out = &db->ctx->out;
struct ver_ctx *ctx = (struct ver_ctx *) pctx;
- struct apk_package *pkg;
+ struct apk_package *installed, *latest;
struct apk_provider *p0;
- char pkgname[41];
- const char *opstr;
- apk_blob_t *latest = apk_atomize(&db->atoms, APK_BLOB_STR(""));
+ struct ver_name_state *ns;
unsigned int latest_repos = 0;
- int i, r = -1;
unsigned short tag, allowed_repos;
+ const char *opstr;
+ int i, r, len;
- if (!name) return;
-
- pkg = apk_pkg_get_installed(name);
- if (!pkg) return;
+ if (!name) return 0;
- tag = pkg->ipkg->repository_tag;
- allowed_repos = db->repo_tags[tag].allowed_repos;
+ installed = latest = apk_pkg_get_installed(name);
+ if (!installed) return 0;
+ allowed_repos = db->repo_tags[installed->ipkg->repository_tag].allowed_repos;
foreach_array_item(p0, name->providers) {
struct apk_package *pkg0 = p0->pkg;
if (pkg0->name != name || pkg0->repos == 0)
continue;
if (!(ctx->all_tags || (pkg0->repos & allowed_repos)))
continue;
- r = apk_version_compare_blob(*pkg0->version, *latest);
+ r = apk_version_compare(*pkg0->version, *latest->version);
switch (r) {
case APK_VERSION_GREATER:
- latest = pkg0->version;
+ latest = pkg0;
latest_repos = pkg0->repos;
break;
case APK_VERSION_EQUAL:
@@ -147,15 +153,12 @@ static void ver_print_package_status(struct apk_database *db, const char *match,
break;
}
}
- r = latest->len ? apk_version_compare_blob(*pkg->version, *latest)
- : APK_VERSION_UNKNOWN;
+
+ ns = state_from_name(name);
+ r = apk_version_compare(*installed->version, *latest->version);
opstr = apk_version_op_string(r);
if ((ctx->limchars != NULL) && (strchr(ctx->limchars, *opstr) == NULL))
- return;
- if (apk_out_verbosity(out) <= 0) {
- apk_out(out, "%s", pkg->name->name);
- return;
- }
+ return 0;
tag = APK_DEFAULT_REPOSITORY_TAG;
for (i = 1; i < db->num_repo_tags; i++) {
@@ -165,11 +168,41 @@ static void ver_print_package_status(struct apk_database *db, const char *match,
}
}
- snprintf(pkgname, sizeof(pkgname), PKG_VER_FMT, PKG_VER_PRINTF(pkg));
- apk_out(out, "%-40s%s " BLOB_FMT " " BLOB_FMT,
- pkgname, opstr,
- BLOB_PRINTF(*latest),
- BLOB_PRINTF(db->repo_tags[tag].tag));
+ *ns = (struct ver_name_state) {
+ .installed = installed,
+ .latest = latest,
+ .tag = tag,
+ .ver_result = r,
+ };
+
+ len = PKG_VER_STRLEN(installed);
+ if (len > ctx->max_pkg_len) ctx->max_pkg_len = len;
+ return 0;
+}
+
+static int ver_print_package_status(struct apk_database *db, const char *match, struct apk_name *name, void *pctx)
+{
+ struct apk_out *out = &db->ctx->out;
+ struct ver_ctx *ctx = (struct ver_ctx *) pctx;
+ struct ver_name_state *ns;
+
+ if (!name) return 0;
+
+ ns = state_from_name(name);
+ if (!ns->installed) return 0;
+
+ if (apk_out_verbosity(out) <= 0) {
+ apk_out(out, "%s", name->name);
+ return 0;
+ }
+
+ apk_out(out, PKG_VER_FMT "%*s %s " BLOB_FMT " " BLOB_FMT,
+ PKG_VER_PRINTF(ns->installed),
+ (int)(ctx->max_pkg_len - PKG_VER_STRLEN(ns->installed)), "",
+ apk_version_op_string(ns->ver_result),
+ BLOB_PRINTF(*ns->latest->version),
+ BLOB_PRINTF(db->repo_tags[ns->tag].tag));
+ return 0;
}
static int ver_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)
@@ -178,6 +211,7 @@ static int ver_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *arg
struct apk_database *db = ac->db;
struct ver_ctx *ctx = (struct ver_ctx *) pctx;
+ ctx->max_pkg_len = 39;
if (ctx->limchars) {
if (strlen(ctx->limchars) == 0)
ctx->limchars = NULL;
@@ -188,12 +222,10 @@ static int ver_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *arg
if (ctx->action != NULL)
return ctx->action(db, args);
- apk_msg(out, "%-42s%s", "Installed:", "Available:");
-
- apk_name_foreach_matching(
- db, args, APK_FOREACH_NULL_MATCHES_ALL | apk_foreach_genid(),
- ver_print_package_status, ctx);
+ apk_db_foreach_matching_name(db, args, ver_calculate_length, ctx);
+ apk_msg(out, "%*s %s", -ctx->max_pkg_len, "Installed:", "Available:");
+ apk_db_foreach_sorted_name(db, args, ver_print_package_status, ctx);
return 0;
}
diff --git a/src/app_vertest.c b/src/app_vertest.c
index b933dd1..fdb1d68 100644
--- a/src/app_vertest.c
+++ b/src/app_vertest.c
@@ -8,38 +8,72 @@
*/
#include <stdio.h>
+#include <unistd.h>
#include "apk_defines.h"
#include "apk_applet.h"
#include "apk_database.h"
#include "apk_version.h"
#include "apk_print.h"
-static int vertest_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)
+static int vertest_one(struct apk_ctx *ac, apk_blob_t arg)
{
struct apk_out *out = &ac->out;
- apk_blob_t arg, ver, op, space = APK_BLOB_STRLIT(" ");
+ apk_blob_t ver1, ver2, op, space = APK_BLOB_STRLIT(" "), binvert = APK_BLOB_STRLIT("!");
+ int ok = 0, invert = 0;
+
+ // trim comments and trailing whitespace
+ apk_blob_split(arg, APK_BLOB_STRLIT("#"), &arg, &op);
+ arg = apk_blob_trim(arg);
+ if (arg.len == 0) return 0;
+
+ // arguments are either:
+ // "version" -> check validity
+ // "!version" -> check invalid
+ // "ver1 op ver2" -> check if that the comparison is true
+ // "ver1 !op ver2" -> check if that the comparison is false
+ if (apk_blob_split(arg, space, &ver1, &op) &&
+ apk_blob_split(op, space, &op, &ver2)) {
+ invert = apk_blob_pull_blob_match(&op, binvert);
+ ok = apk_version_match(ver1, apk_version_result_mask_blob(op), ver2);
+ } else {
+ ver1 = arg;
+ invert = apk_blob_pull_blob_match(&ver1, binvert);
+ ok = apk_version_validate(ver1);
+ }
+ if (invert) ok = !ok;
+ if (!ok) {
+ apk_msg(out, "FAIL: " BLOB_FMT, BLOB_PRINTF(arg));
+ return 1;
+ }
+
+ apk_dbg(out, "OK: " BLOB_FMT, BLOB_PRINTF(arg));
+ return 0;
+}
+
+static int vertest_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)
+{
+ struct apk_istream *is;
char **parg;
- int errors = 0;
-
- foreach_array_item(parg, args) {
- int ok = 0;
-
- // arguments are either:
- // "version" -> check validty
- // "ver1 op ver2" -> check if that the comparison is true
- arg = APK_BLOB_STR(*parg);
- if (apk_blob_split(arg, space, &ver, &arg) &&
- apk_blob_split(arg, space, &op, &arg)) {
- if (apk_version_compare_blob(ver, arg) & apk_version_result_mask_blob(op))
- ok = 1;
- } else {
- ok = apk_version_validate(arg);
+ apk_blob_t l;
+ int errors = 0, count = 0;
+
+ if (args->num != 0) {
+ foreach_array_item(parg, args)
+ errors += vertest_one(ac, APK_BLOB_STR(*parg));
+ count = args->num;
+ } else {
+ is = apk_istream_from_fd(STDIN_FILENO);
+ if (IS_ERR(is)) return 1;
+
+ while (apk_istream_get_delim(is, APK_BLOB_STR("\n"), &l) == 0) {
+ errors += vertest_one(ac, l);
+ count++;
}
- if (!ok) {
- apk_msg(out, "%s", *parg);
+
+ if (apk_istream_close(is) != 0)
errors++;
- }
}
+ if (errors) apk_dbg(&ac->out, "Result: %d/%d", count-errors, count);
return errors ? 1 : 0;
}
diff --git a/src/applet.c b/src/applet.c
index 1d47662..70dbf47 100644
--- a/src/applet.c
+++ b/src/applet.c
@@ -9,8 +9,9 @@
#include <zlib.h>
#include "apk_applet.h"
#include "apk_print.h"
+#include "help.h"
-static struct list_head apk_applet_list;
+static LIST_HEAD(apk_applet_list);
#define apk_applet_foreach(iter) list_for_each_entry(iter, &apk_applet_list, node)
@@ -20,16 +21,6 @@ void apk_applet_register(struct apk_applet *applet)
list_add_tail(&applet->node, &apk_applet_list);
}
-void apk_applet_register_builtin(void)
-{
- extern apk_init_func_t __start_initapplets[], __stop_initapplets[];
- apk_init_func_t *p;
-
- list_init(&apk_applet_list);
- for (p = __start_initapplets; p < __stop_initapplets; p++)
- (*p)();
-}
-
struct apk_applet *apk_applet_find(const char *name)
{
struct apk_applet *a;
@@ -41,6 +32,7 @@ struct apk_applet *apk_applet_find(const char *name)
return NULL;
}
+#ifndef NO_HELP
static inline int is_group(struct apk_applet *applet, const char *topic)
{
if (!applet) return strcasecmp(topic, "apk") == 0;
@@ -49,18 +41,24 @@ static inline int is_group(struct apk_applet *applet, const char *topic)
if (strcasecmp(applet->optgroups[i]->desc, topic) == 0) return 1;
return 0;
}
+#endif
void apk_applet_help(struct apk_applet *applet, struct apk_out *out)
{
-#include "help.h"
-
#ifndef NO_HELP
- char buf[uncompressed_help_size], *ptr, *msg;
- unsigned long len = sizeof buf;
+#ifdef COMPRESSED_HELP
+ unsigned char buf[payload_help_size];
+#endif
+ const char *ptr = (const char *) payload_help, *base = ptr, *msg;
+ unsigned long len = payload_help_size;
int num = 0;
- uncompress((unsigned char*) buf, &len, compressed_help, sizeof compressed_help);
- for (ptr = buf; *ptr && ptr < &buf[len]; ptr = msg + strlen(msg) + 1) {
+#ifdef COMPRESSED_HELP
+ uncompress(buf, &len, payload_help, sizeof payload_help);
+ ptr = base = (const char *) buf;
+ len = sizeof buf;
+#endif
+ for (; *ptr && ptr < &base[len]; ptr = msg + strlen(msg) + 1) {
msg = ptr + strlen(ptr) + 1;
if (is_group(applet, ptr)) {
fputc('\n', stdout);
diff --git a/src/blob.c b/src/blob.c
index 32cd92e..0ff7ac7 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -7,10 +7,11 @@
* SPDX-License-Identifier: GPL-2.0-only
*/
-#include <malloc.h>
+#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
+#include <stdarg.h>
#include "apk_blob.h"
#include "apk_hash.h"
@@ -32,121 +33,14 @@ char *apk_blob_cstr(apk_blob_t blob)
return cstr;
}
-#if defined(__i386__)
-static unsigned long inline memspn(
- const unsigned char *ptr,
- unsigned long len,
- const unsigned char *array)
+apk_blob_t apk_blob_dup(apk_blob_t blob)
{
- unsigned long p = len;
-
- __asm__("cld ; xorl %%eax, %%eax\n"
- "1:\n"
- "lodsb\n"
- "btl %%eax, %2\n"
- "jnc 2f\n"
- "decl %1\n"
- "jnz 1b\n"
- "2:\n"
- : "+&S" (ptr), "+&r" (p)
- : "m" (*array)
- : "cc", "%eax");
-
- return p;
+ char *ptr = malloc(blob.len);
+ if (!ptr) return APK_BLOB_NULL;
+ memcpy(ptr, blob.ptr, blob.len);
+ return APK_BLOB_PTR_LEN(ptr, blob.len);
}
-static unsigned long inline memcspn(
- const unsigned char *ptr,
- unsigned long len,
- const unsigned char *array)
-{
- unsigned long p = len;
-
- __asm__("cld ; xorl %%eax, %%eax\n"
- "1:\n"
- "lodsb\n"
- "btl %%eax, %2\n"
- "jc 2f\n"
- "decl %1\n"
- "jnz 1b\n"
- "2:\n"
- : "+&S" (ptr), "+&r" (p)
- : "m" (*array)
- : "cc", "%eax");
-
- return p;
-}
-
-int apk_blob_spn(apk_blob_t blob, const apk_spn_match accept, apk_blob_t *l, apk_blob_t *r)
-{
- unsigned int i;
-
- if (blob.len == 0)
- return 0;
- i = blob.len - memspn((unsigned char*) blob.ptr, blob.len, accept);
- if (i == blob.len)
- return 0;
- if (l != NULL)
- *l = APK_BLOB_PTR_LEN(blob.ptr, i);
- if (r != NULL)
- *r = APK_BLOB_PTR_LEN(blob.ptr+i, blob.len-i);
- return 1;
-}
-
-int apk_blob_cspn(apk_blob_t blob, const apk_spn_match reject, apk_blob_t *l, apk_blob_t *r)
-{
- unsigned int i;
-
- if (blob.len == 0)
- return 0;
- i = blob.len - memcspn((unsigned char*) blob.ptr, blob.len, reject);
- if (i == blob.len)
- return 0;
- if (l != NULL)
- *l = APK_BLOB_PTR_LEN(blob.ptr, i);
- if (r != NULL)
- *r = APK_BLOB_PTR_LEN(blob.ptr+i, blob.len-i);
- return 1;
-}
-#else
-static int inline test_bit(const unsigned char *array, unsigned char bit)
-{
- return array[bit >> 3] & (1 << (bit & 7));
-}
-
-int apk_blob_spn(apk_blob_t blob, const apk_spn_match accept, apk_blob_t *l, apk_blob_t *r)
-{
- int i;
-
- for (i = 0; i < blob.len; i++) {
- if (!test_bit(accept, blob.ptr[i])) {
- if (l != NULL)
- *l = APK_BLOB_PTR_LEN(blob.ptr, i);
- if (r != NULL)
- *r = APK_BLOB_PTR_LEN(blob.ptr+i, blob.len-i);
- return 1;
- }
- }
- return 0;
-}
-
-int apk_blob_cspn(apk_blob_t blob, const apk_spn_match reject, apk_blob_t *l, apk_blob_t *r)
-{
- int i;
-
- for (i = 0; i < blob.len; i++) {
- if (test_bit(reject, blob.ptr[i])) {
- if (l != NULL)
- *l = APK_BLOB_PTR_LEN(blob.ptr, i);
- if (r != NULL)
- *r = APK_BLOB_PTR_LEN(blob.ptr+i, blob.len-i);
- return 1;
- }
- }
- return 0;
-}
-#endif
-
int apk_blob_rsplit(apk_blob_t blob, char split, apk_blob_t *l, apk_blob_t *r)
{
char *sep;
@@ -383,27 +277,6 @@ void apk_blob_push_csum(apk_blob_t *to, struct apk_checksum *csum)
}
}
-void apk_blob_push_hexdump(apk_blob_t *to, apk_blob_t binary)
-{
- char *d;
- int i;
-
- if (unlikely(APK_BLOB_IS_NULL(*to)))
- return;
-
- if (unlikely(to->len < binary.len * 2)) {
- *to = APK_BLOB_NULL;
- return;
- }
-
- for (i = 0, d = to->ptr; i < binary.len; i++) {
- *(d++) = xd[(binary.ptr[i] >> 4) & 0xf];
- *(d++) = xd[binary.ptr[i] & 0xf];
- }
- to->ptr = d;
- to->len -= binary.len * 2;
-}
-
static const char b64encode[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -440,6 +313,47 @@ void apk_blob_push_base64(apk_blob_t *to, apk_blob_t binary)
to->len -= needed;
}
+void apk_blob_push_hexdump(apk_blob_t *to, apk_blob_t binary)
+{
+ char *d;
+ int i;
+
+ if (unlikely(APK_BLOB_IS_NULL(*to)))
+ return;
+
+ if (unlikely(to->len < binary.len * 2)) {
+ *to = APK_BLOB_NULL;
+ return;
+ }
+
+ for (i = 0, d = to->ptr; i < binary.len; i++) {
+ *(d++) = xd[(binary.ptr[i] >> 4) & 0xf];
+ *(d++) = xd[binary.ptr[i] & 0xf];
+ }
+ to->ptr = d;
+ to->len -= binary.len * 2;
+}
+
+void apk_blob_push_fmt(apk_blob_t *to, const char *fmt, ...)
+{
+ va_list va;
+ int n;
+
+ if (unlikely(APK_BLOB_IS_NULL(*to)))
+ return;
+
+ va_start(va, fmt);
+ n = vsnprintf(to->ptr, to->len, fmt, va);
+ va_end(va);
+
+ if (n >= 0 && n <= to->len) {
+ to->ptr += n;
+ to->len -= n;
+ } else {
+ *to = APK_BLOB_NULL;
+ }
+}
+
void apk_blob_pull_char(apk_blob_t *b, int expected)
{
if (unlikely(APK_BLOB_IS_NULL(*b)))
@@ -452,7 +366,7 @@ void apk_blob_pull_char(apk_blob_t *b, int expected)
b->len --;
}
-unsigned int apk_blob_pull_uint(apk_blob_t *b, int radix)
+uint64_t apk_blob_pull_uint(apk_blob_t *b, int radix)
{
unsigned int val;
int ch;
@@ -647,16 +561,3 @@ void apk_blob_pull_base64(apk_blob_t *b, apk_blob_t to)
err:
*b = APK_BLOB_NULL;
}
-
-#if defined(__GLIBC__) && !defined(__UCLIBC__)
-size_t strlcpy(char *dst, const char *src, size_t size)
-{
- size_t ret = strlen(src), len;
- if (!size) return ret;
- len = ret;
- if (len >= size) len = size - 1;
- memcpy(dst, src, len);
- dst[len] = 0;
- return ret;
-}
-#endif
diff --git a/src/commit.c b/src/commit.c
index 640c135..bf9db10 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -7,7 +7,6 @@
* SPDX-License-Identifier: GPL-2.0-only
*/
-#include <assert.h>
#include <limits.h>
#include <stdint.h>
#include <unistd.h>
@@ -18,6 +17,20 @@
#include "apk_print.h"
+struct apk_stats {
+ size_t bytes;
+ unsigned int changes;
+ unsigned int packages;
+};
+
+struct progress {
+ struct apk_progress prog;
+ struct apk_stats done;
+ struct apk_stats total;
+ struct apk_package *pkg;
+ int total_changes_digits;
+};
+
static inline int pkg_available(struct apk_database *db, struct apk_package *pkg)
{
if (pkg->repos & db->available_repos)
@@ -26,7 +39,7 @@ static inline int pkg_available(struct apk_database *db, struct apk_package *pkg
}
static int print_change(struct apk_database *db, struct apk_change *change,
- int cur, int total)
+ struct progress *prog)
{
struct apk_out *out = &db->ctx->out;
struct apk_name *name;
@@ -37,7 +50,9 @@ static int print_change(struct apk_database *db, struct apk_change *change,
apk_blob_t *oneversion = NULL;
int r;
- snprintf(status, sizeof(status), "(%i/%i)", cur+1, total);
+ snprintf(status, sizeof status, "(%*i/%i)",
+ prog->total_changes_digits, prog->done.changes+1,
+ prog->total.changes);
name = newpkg ? newpkg->name : oldpkg->name;
if (oldpkg == NULL) {
@@ -90,12 +105,6 @@ static int print_change(struct apk_database *db, struct apk_change *change,
return TRUE;
}
-struct apk_stats {
- unsigned int changes;
- size_t bytes;
- unsigned int packages;
-};
-
static void count_change(struct apk_change *change, struct apk_stats *stats)
{
if (change->new_pkg != change->old_pkg || change->reinstall) {
@@ -112,13 +121,6 @@ static void count_change(struct apk_change *change, struct apk_stats *stats)
}
}
-struct progress {
- struct apk_progress prog;
- struct apk_stats done;
- struct apk_stats total;
- struct apk_package *pkg;
-};
-
static void progress_cb(void *ctx, size_t installed_bytes)
{
struct progress *prog = (struct progress *) ctx;
@@ -127,20 +129,19 @@ static void progress_cb(void *ctx, size_t installed_bytes)
prog->total.bytes + prog->total.packages);
}
-static int dump_packages(struct apk_out *out, struct apk_changeset *changeset,
+static int dump_packages(struct apk_out *out, struct apk_change_array *changes,
int (*cmp)(struct apk_change *change),
const char *msg)
{
struct apk_change *change;
struct apk_name *name;
- struct apk_indent indent = { .out = out, .indent = 2 };
+ struct apk_indent indent;
int match = 0;
- foreach_array_item(change, changeset->changes) {
- if (!cmp(change))
- continue;
- if (match == 0)
- printf("%s:\n", msg);
+ apk_print_indented_init(&indent, out, 0);
+ foreach_array_item(change, changes) {
+ if (!cmp(change)) continue;
+ if (!match) apk_print_indented_group(&indent, 2, "%s:\n", msg);
if (change->new_pkg != NULL)
name = change->new_pkg->name;
else
@@ -149,11 +150,19 @@ static int dump_packages(struct apk_out *out, struct apk_changeset *changeset,
apk_print_indented(&indent, APK_BLOB_STR(name->name));
match++;
}
- if (match)
- printf("\n");
+ apk_print_indented_end(&indent);
return match;
}
+static int sort_change(const void *a, const void *b)
+{
+ const struct apk_change *ca = a;
+ const struct apk_change *cb = b;
+ const struct apk_name *na = ca->old_pkg ? ca->old_pkg->name : ca->new_pkg->name;
+ const struct apk_name *nb = cb->old_pkg ? cb->old_pkg->name : cb->new_pkg->name;
+ return apk_name_cmp_display(na, nb);
+}
+
static int cmp_remove(struct apk_change *change)
{
return change->new_pkg == NULL;
@@ -196,13 +205,14 @@ static int cmp_upgrade(struct apk_change *change)
return 0;
}
-static void run_triggers(struct apk_database *db, struct apk_changeset *changeset)
+static int run_triggers(struct apk_database *db, struct apk_changeset *changeset)
{
struct apk_change *change;
struct apk_installed_package *ipkg;
+ int errors = 0;
if (apk_db_fire_triggers(db) == 0)
- return;
+ return 0;
foreach_array_item(change, changeset->changes) {
struct apk_package *pkg = change->new_pkg;
@@ -213,10 +223,11 @@ static void run_triggers(struct apk_database *db, struct apk_changeset *changese
continue;
*apk_string_array_add(&ipkg->pending_triggers) = NULL;
- apk_ipkg_run_script(ipkg, db, APK_SCRIPT_TRIGGER,
- ipkg->pending_triggers->item);
+ errors += apk_ipkg_run_script(ipkg, db, APK_SCRIPT_TRIGGER,
+ ipkg->pending_triggers->item) != 0;
apk_string_array_free(&ipkg->pending_triggers);
}
+ return errors;
}
#define PRE_COMMIT_HOOK 0
@@ -258,6 +269,16 @@ static int run_commit_hooks(struct apk_database *db, int type)
run_commit_hook, &hook);
}
+static int calc_precision(unsigned int num)
+{
+ int precision = 1;
+ while (num >= 10) {
+ precision++;
+ num /= 10;
+ }
+ return precision;
+}
+
int apk_solver_commit_changeset(struct apk_database *db,
struct apk_changeset *changeset,
struct apk_dependency_array *world)
@@ -267,7 +288,7 @@ int apk_solver_commit_changeset(struct apk_database *db,
struct apk_change *change;
char buf[32];
const char *size_unit;
- off_t humanized, size_diff = 0;
+ off_t humanized, size_diff = 0, download_size = 0;
int r, errors = 0;
assert(world);
@@ -283,38 +304,56 @@ int apk_solver_commit_changeset(struct apk_database *db,
/* Count what needs to be done */
foreach_array_item(change, changeset->changes) {
count_change(change, &prog.total);
- if (change->new_pkg)
+ if (change->new_pkg) {
size_diff += change->new_pkg->installed_size;
+ if (change->new_pkg != change->old_pkg &&
+ !(change->new_pkg->repos & db->local_repos))
+ download_size += change->new_pkg->size;
+ }
if (change->old_pkg)
size_diff -= change->old_pkg->installed_size;
}
- size_unit = apk_get_human_size(llabs(size_diff), &humanized);
+ prog.total_changes_digits = calc_precision(prog.total.changes);
if ((apk_out_verbosity(out) > 1 || (db->ctx->flags & APK_INTERACTIVE)) &&
!(db->ctx->flags & APK_SIMULATE)) {
- r = dump_packages(out, changeset, cmp_remove,
+ struct apk_change_array *sorted;
+
+ apk_change_array_init(&sorted);
+ apk_change_array_copy(&sorted, changeset->changes);
+ qsort(sorted->item, sorted->num, sizeof(struct apk_change), sort_change);
+
+ r = dump_packages(out, sorted, cmp_remove,
"The following packages will be REMOVED");
- r += dump_packages(out, changeset, cmp_downgrade,
+ r += dump_packages(out, sorted, cmp_downgrade,
"The following packages will be DOWNGRADED");
if (r || (db->ctx->flags & APK_INTERACTIVE) || apk_out_verbosity(out) > 2) {
- r += dump_packages(out, changeset, cmp_new,
+ r += dump_packages(out, sorted, cmp_new,
"The following NEW packages will be installed");
- r += dump_packages(out, changeset, cmp_upgrade,
+ r += dump_packages(out, sorted, cmp_upgrade,
"The following packages will be upgraded");
- r += dump_packages(out, changeset, cmp_reinstall,
+ r += dump_packages(out, sorted, cmp_reinstall,
"The following packages will be reinstalled");
- printf("After this operation, %lld %s of %s.\n",
+ if (download_size) {
+ size_unit = apk_get_human_size(download_size, &humanized);
+ apk_msg(out, "Need to download %lld %s of packages.",
+ (long long)humanized, size_unit);
+ }
+ size_unit = apk_get_human_size(llabs(size_diff), &humanized);
+ apk_msg(out, "After this operation, %lld %s of %s.",
(long long)humanized,
size_unit,
(size_diff < 0) ?
"disk space will be freed" :
"additional disk space will be used");
}
+ apk_change_array_free(&sorted);
+
if (r > 0 && (db->ctx->flags & APK_INTERACTIVE)) {
printf("Do you want to continue [Y/n]? ");
fflush(stdout);
r = fgetc(stdin);
- if (r != 'y' && r != 'Y' && r != '\n')
+ if (r != 'y' && r != 'Y' && r != '\n' && r != EOF)
return -1;
}
}
@@ -327,7 +366,7 @@ int apk_solver_commit_changeset(struct apk_database *db,
r = change->old_pkg &&
(change->old_pkg->ipkg->broken_files ||
change->old_pkg->ipkg->broken_script);
- if (print_change(db, change, prog.done.changes, prog.total.changes)) {
+ if (print_change(db, change, &prog)) {
prog.pkg = change->new_pkg;
progress_cb(&prog, 0);
@@ -346,12 +385,12 @@ int apk_solver_commit_changeset(struct apk_database *db,
apk_print_progress(&prog.prog, prog.total.bytes + prog.total.packages,
prog.total.bytes + prog.total.packages);
- apk_db_update_directory_permissions(db);
- run_triggers(db, changeset);
+ errors += db->num_dir_update_errors;
+ errors += run_triggers(db, changeset);
all_done:
apk_dependency_array_copy(&db->world, world);
- apk_db_write_config(db);
+ if (apk_db_write_config(db) != 0) errors++;
run_commit_hooks(db, POST_COMMIT_HOOK);
if (!db->performing_self_upgrade) {
@@ -380,6 +419,8 @@ all_done:
enum {
STATE_PRESENT = 0x80000000,
STATE_MISSING = 0x40000000,
+ STATE_VIRTUAL_ONLY = 0x20000000,
+ STATE_INSTALLIF = 0x10000000,
STATE_COUNT_MASK = 0x0000ffff,
};
@@ -396,22 +437,15 @@ struct print_state {
static void label_start(struct print_state *ps, const char *text)
{
if (ps->label) {
- printf(" %s:\n", ps->label);
+ apk_print_indented_line(&ps->i, " %s:\n", ps->label);
ps->label = NULL;
- ps->i.x = ps->i.indent = 0;
ps->num_labels++;
}
- if (ps->i.x == 0) {
- ps->i.x = printf(" %s", text);
- ps->i.indent = ps->i.x + 1;
- }
+ if (!ps->i.x) apk_print_indented_group(&ps->i, 0, " %s", text);
}
static void label_end(struct print_state *ps)
{
- if (ps->i.x != 0) {
- printf("\n");
- ps->i.x = ps->i.indent = 0;
- }
+ apk_print_indented_end(&ps->i);
}
static void print_pinning_errors(struct print_state *ps, struct apk_package *pkg, unsigned int tag)
@@ -425,7 +459,10 @@ static void print_pinning_errors(struct print_state *ps, struct apk_package *pkg
if (!(pkg->repos & db->available_repos)) {
label_start(ps, "masked in:");
apk_print_indented_fmt(&ps->i, "--no-network");
- } else if (pkg->repos == BIT(APK_REPOSITORY_CACHED) && !(pkg->filename != NULL || pkg->installed_size == 0)) {
+ } else if (!(BIT(pkg->layer) & db->active_layers)) {
+ label_start(ps, "masked in:");
+ apk_print_indented_fmt(&ps->i, "layer");
+ } else if (pkg->repos == BIT(APK_REPOSITORY_CACHED) && !pkg->filename_ndx) {
label_start(ps, "masked in:");
apk_print_indented_fmt(&ps->i, "cache");
} else {
@@ -501,6 +538,19 @@ static void print_deps(struct print_state *ps, struct apk_package *pkg, int matc
label_end(ps);
}
+static void print_broken_deps(struct print_state *ps, struct apk_dependency_array *deps, const char *label)
+{
+ struct apk_dependency *dep;
+ char tmp[256];
+
+ foreach_array_item(dep, deps) {
+ if (!dep->broken) continue;
+ label_start(ps, label);
+ apk_print_indented_fmt(&ps->i, "%s", apk_dep_snprintf(tmp, sizeof(tmp), dep));
+ }
+ label_end(ps);
+}
+
static void analyze_package(struct print_state *ps, struct apk_package *pkg, unsigned int tag)
{
char pkgtext[256];
@@ -508,6 +558,15 @@ static void analyze_package(struct print_state *ps, struct apk_package *pkg, uns
snprintf(pkgtext, sizeof(pkgtext), PKG_VER_FMT, PKG_VER_PRINTF(pkg));
ps->label = pkgtext;
+ if (pkg->uninstallable) {
+ label_start(ps, "error:");
+ apk_print_indented_fmt(&ps->i, "uninstallable");
+ label_end(ps);
+ print_broken_deps(ps, pkg->depends, "depends:");
+ print_broken_deps(ps, pkg->provides, "provides:");
+ print_broken_deps(ps, pkg->install_if, "install_if:");
+ }
+
print_pinning_errors(ps, pkg, tag);
print_conflicts(ps, pkg);
print_deps(ps, pkg, APK_DEP_CONFLICTS);
@@ -515,7 +574,7 @@ static void analyze_package(struct print_state *ps, struct apk_package *pkg, uns
print_deps(ps, pkg, APK_DEP_SATISFIES);
}
-static void analyze_name(struct print_state *ps, struct apk_name *name)
+static void analyze_missing_name(struct print_state *ps, struct apk_name *name)
{
struct apk_name **pname0, *name0;
struct apk_provider *p0;
@@ -528,6 +587,10 @@ static void analyze_name(struct print_state *ps, struct apk_name *name)
snprintf(tmp, sizeof(tmp), "%s (virtual)", name->name);
ps->label = tmp;
+ label_start(ps, "note:");
+ apk_print_indented_words(&ps->i, "please select one of the 'provided by' packages explicitly");
+ label_end(ps);
+
label_start(ps, "provided by:");
foreach_array_item(p0, name->providers)
p0->pkg->name->state_int++;
@@ -552,7 +615,7 @@ static void analyze_name(struct print_state *ps, struct apk_name *name)
label_start(ps, "required by:");
foreach_array_item(d0, ps->world) {
- if (d0->name != name || d0->conflict)
+ if (d0->name != name || apk_dep_conflict(d0))
continue;
apk_print_indented_fmt(&ps->i, "world[%s]",
apk_dep_snprintf(tmp, sizeof(tmp), d0));
@@ -567,7 +630,7 @@ static void analyze_name(struct print_state *ps, struct apk_name *name)
continue;
p0->pkg->foreach_genid = genid;
foreach_array_item(d0, p0->pkg->depends) {
- if (d0->name != name || d0->conflict)
+ if (d0->name != name || apk_dep_conflict(d0))
continue;
apk_print_indented_fmt(&ps->i,
PKG_VER_FMT "[%s]",
@@ -589,12 +652,95 @@ static void analyze_deps(struct print_state *ps, struct apk_dependency_array *de
foreach_array_item(d0, deps) {
name0 = d0->name;
- if (d0->conflict)
- continue;
- if ((name0->state_int & (STATE_PRESENT | STATE_MISSING)) != 0)
+ if (apk_dep_conflict(d0)) continue;
+ if ((name0->state_int & (STATE_INSTALLIF | STATE_PRESENT | STATE_MISSING)) != 0)
continue;
name0->state_int |= STATE_MISSING;
- analyze_name(ps, name0);
+ analyze_missing_name(ps, name0);
+ }
+}
+
+static void discover_deps(struct apk_dependency_array *deps);
+static void discover_name(struct apk_name *name, int pkg_state);
+
+static void discover_reverse_iif(struct apk_name *name)
+{
+ struct apk_name **pname0, *name0;
+ struct apk_dependency *d;
+ struct apk_provider *p;
+
+ foreach_array_item(pname0, name->rinstall_if) {
+ name0 = *pname0;
+
+ foreach_array_item(p, name0->providers) {
+ int ok = 1;
+ if (!p->pkg->marked) continue;
+ if (p->pkg->install_if->num == 0) continue;
+ foreach_array_item(d, p->pkg->install_if) {
+ if (apk_dep_conflict(d) == !!(d->name->state_int & (STATE_PRESENT|STATE_INSTALLIF))) {
+ ok = 0;
+ break;
+ }
+ }
+ if (ok) {
+ discover_name(p->pkg->name, STATE_INSTALLIF);
+ foreach_array_item(d, p->pkg->provides)
+ discover_name(d->name, STATE_INSTALLIF);
+ }
+ }
+ }
+}
+
+static int is_name_concrete(struct apk_package *pkg, struct apk_name *name)
+{
+ struct apk_dependency *d;
+ if (pkg->name == name) return 1;
+ foreach_array_item(d, pkg->provides) {
+ if (d->name != name) continue;
+ if (d->version == &apk_atom_null) continue;
+ return 1;
+ }
+ return 0;
+}
+
+static void discover_name(struct apk_name *name, int pkg_state)
+{
+ struct apk_provider *p;
+ struct apk_dependency *d;
+
+ foreach_array_item(p, name->providers) {
+ int state = pkg_state;
+ if (!p->pkg->marked) continue;
+ if ((state == STATE_PRESENT || state == STATE_INSTALLIF) &&
+ !p->pkg->provider_priority && !is_name_concrete(p->pkg, name))
+ state = STATE_VIRTUAL_ONLY;
+ if (p->pkg->state_int & state) continue;
+ p->pkg->state_int |= state;
+
+ p->pkg->name->state_int |= state;
+ foreach_array_item(d, p->pkg->provides) {
+ int dep_state = state;
+ if (dep_state == STATE_INSTALLIF && d->version == &apk_atom_null)
+ dep_state = STATE_VIRTUAL_ONLY;
+ d->name->state_int |= dep_state;
+ }
+
+ discover_deps(p->pkg->depends);
+ if (state == STATE_PRESENT || state == STATE_INSTALLIF) {
+ discover_reverse_iif(p->pkg->name);
+ foreach_array_item(d, p->pkg->provides)
+ discover_reverse_iif(d->name);
+ }
+ }
+}
+
+static void discover_deps(struct apk_dependency_array *deps)
+{
+ struct apk_dependency *d;
+
+ foreach_array_item(d, deps) {
+ if (apk_dep_conflict(d)) continue;
+ discover_name(d->name, STATE_PRESENT);
}
}
@@ -605,7 +751,6 @@ void apk_solver_print_errors(struct apk_database *db,
struct apk_out *out = &db->ctx->out;
struct print_state ps;
struct apk_change *change;
- struct apk_dependency *p;
/* ERROR: unsatisfiable dependencies:
* name:
@@ -644,36 +789,30 @@ void apk_solver_print_errors(struct apk_database *db,
* any other selected version. or all of them with -v.
*/
- apk_err(out, "unable to select packages:");
-
/* Construct information about names */
foreach_array_item(change, changeset->changes) {
struct apk_package *pkg = change->new_pkg;
- if (pkg == NULL)
- continue;
- pkg->marked = 1;
- pkg->name->state_int |= STATE_PRESENT;
- foreach_array_item(p, pkg->provides)
- p->name->state_int |= STATE_PRESENT;
+ if (pkg) pkg->marked = 1;
}
+ discover_deps(world);
/* Analyze is package, and missing names referred to */
ps = (struct print_state) {
- .i.out = out,
.db = db,
.world = world,
};
+ apk_err(out, "unable to select packages:");
+ apk_print_indented_init(&ps.i, out, 1);
analyze_deps(&ps, world);
foreach_array_item(change, changeset->changes) {
struct apk_package *pkg = change->new_pkg;
- if (pkg == NULL)
- continue;
+ if (!pkg) continue;
analyze_package(&ps, pkg, change->new_repository_tag);
analyze_deps(&ps, pkg->depends);
}
- if (ps.num_labels == 0)
- printf(" Huh? Error reporter did not find the broken constraints.\n");
+ if (!ps.num_labels)
+ apk_print_indented_line(&ps.i, "Huh? Error reporter did not find the broken constraints.\n");
}
int apk_solver_commit(struct apk_database *db,
diff --git a/src/common.c b/src/common.c
index 14a56a3..2b0fe7e 100644
--- a/src/common.c
+++ b/src/common.c
@@ -6,7 +6,6 @@
* SPDX-License-Identifier: GPL-2.0-only
*/
-#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
@@ -40,3 +39,18 @@ void *apk_array_resize(void *array, size_t new_size, size_t elem_size)
return tmp;
}
+
+time_t apk_get_build_time(void)
+{
+ static int initialized = 0;
+ static time_t timestamp = 0;
+ char *source_date_epoch;
+
+ if (initialized) return timestamp;
+ source_date_epoch = getenv("SOURCE_DATE_EPOCH");
+ if (source_date_epoch && *source_date_epoch)
+ timestamp = strtoull(source_date_epoch, NULL, 10);
+ else timestamp = time(NULL);
+ initialized = 1;
+ return timestamp;
+}
diff --git a/src/context.c b/src/context.c
index 7020b25..0000f70 100644
--- a/src/context.c
+++ b/src/context.c
@@ -11,24 +11,27 @@
#include <unistd.h>
#include <sys/stat.h>
#include "apk_context.h"
+#include "apk_fs.h"
void apk_ctx_init(struct apk_ctx *ac)
{
memset(ac, 0, sizeof *ac);
apk_string_array_init(&ac->repository_list);
- apk_string_array_init(&ac->private_keys);
+ apk_trust_init(&ac->trust);
apk_out_reset(&ac->out);
ac->out.out = stdout;
ac->out.err = stderr;
ac->out.verbosity = 1;
+ apk_digest_ctx_init(&ac->dctx, APK_DIGEST_SHA256);
}
void apk_ctx_free(struct apk_ctx *ac)
{
+ if (ac->protected_paths) apk_istream_close(ac->protected_paths);
+ apk_digest_ctx_free(&ac->dctx);
apk_id_cache_free(&ac->id_cache);
apk_trust_free(&ac->trust);
apk_string_array_free(&ac->repository_list);
- apk_string_array_free(&ac->private_keys);
if (ac->out.log) fclose(ac->out.log);
}
@@ -39,12 +42,24 @@ int apk_ctx_prepare(struct apk_ctx *ac)
ac->open_flags &= ~(APK_OPENF_CREATE | APK_OPENF_WRITE);
ac->open_flags |= APK_OPENF_READ;
}
+ if (ac->flags & APK_ALLOW_UNTRUSTED) ac->trust.allow_untrusted = 1;
if (!ac->cache_dir) ac->cache_dir = "etc/apk/cache";
if (!ac->keys_dir) ac->keys_dir = "etc/apk/keys";
if (!ac->root) ac->root = "/";
if (!ac->cache_max_age) ac->cache_max_age = 4*60*60; /* 4 hours default */
- if (!strcmp(ac->root, "/")) ac->flags |= APK_NO_CHROOT; /* skip chroot if root is default */
- ac->uvol = getenv("APK_UVOL");
+
+ if (!strcmp(ac->root, "/")) {
+ // No chroot needed if using system root
+ ac->flags |= APK_NO_CHROOT;
+
+ // Check uvol availability
+ ac->uvol = "/usr/sbin/uvol";
+ if (access(ac->uvol, X_OK) != 0)
+ ac->uvol = ERR_PTR(-APKE_UVOL_NOT_AVAILABLE);
+ } else {
+ ac->root_set = 1;
+ ac->uvol = ERR_PTR(-APKE_UVOL_ROOT);
+ }
ac->root_fd = openat(AT_FDCWD, ac->root, O_RDONLY | O_CLOEXEC);
if (ac->root_fd < 0 && (ac->open_flags & APK_OPENF_CREATE)) {
@@ -55,13 +70,29 @@ int apk_ctx_prepare(struct apk_ctx *ac)
apk_err(&ac->out, "Unable to open root: %s", apk_error_str(errno));
return -errno;
}
+ ac->dest_fd = ac->root_fd;
+
+ if (ac->open_flags & APK_OPENF_CREATE) {
+ uid_t uid = getuid();
+ if (ac->open_flags & APK_OPENF_USERMODE) {
+ if (uid == 0) {
+ apk_err(&ac->out, "--usermode not allowed as root");
+ return -EINVAL;
+ }
+ } else {
+ if (uid != 0) {
+ apk_err(&ac->out, "Use --usermode to allow creating database as non-root");
+ return -EINVAL;
+ }
+ }
+ }
- if (ac->open_flags & APK_OPENF_WRITE) {
- const char *log_path = "var/log/apk.log", *log_dir = "var/log";
+ if ((ac->open_flags & APK_OPENF_WRITE) && !(ac->flags & APK_NO_LOGFILE)) {
+ const char *log_path = "var/log/apk.log";
const int lflags = O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC;
int fd = openat(ac->root_fd, log_path, lflags, 0644);
if (fd < 0 && (ac->open_flags & APK_OPENF_CREATE)) {
- mkdirat(ac->root_fd, log_dir, 0755);
+ apk_make_dirs(ac->root_fd, "var/log", 0755, 0755);
fd = openat(ac->root_fd, log_path, lflags, 0644);
}
if (fd < 0) {
@@ -75,12 +106,10 @@ int apk_ctx_prepare(struct apk_ctx *ac)
struct apk_trust *apk_ctx_get_trust(struct apk_ctx *ac)
{
- if (!ac->trust.initialized) {
- int r = apk_trust_init(&ac->trust,
- openat(ac->root_fd, ac->keys_dir, O_RDONLY | O_CLOEXEC),
- ac->private_keys);
- if (r) return ERR_PTR(r);
- ac->trust.allow_untrusted = !!(ac->flags & APK_ALLOW_UNTRUSTED);
+ if (!ac->trust.keys_loaded) {
+ int r = apk_trust_load_keys(&ac->trust,
+ openat(ac->root_fd, ac->keys_dir, O_RDONLY | O_CLOEXEC));
+ if (r != 0) apk_err(&ac->out, "Unable to load trust keys: %s", apk_error_str(r));
}
return &ac->trust;
}
diff --git a/src/crypto.c b/src/crypto.c
new file mode 100644
index 0000000..615a022
--- /dev/null
+++ b/src/crypto.c
@@ -0,0 +1,80 @@
+#include "apk_crypto.h"
+
+static const char *apk_digest_str[] = {
+ [APK_DIGEST_NONE] = "none",
+ [APK_DIGEST_MD5] = "md5",
+ [APK_DIGEST_SHA1] = "sha1",
+ [APK_DIGEST_SHA256_160] = "sha256-160",
+ [APK_DIGEST_SHA256] = "sha256",
+ [APK_DIGEST_SHA512] = "sha512",
+};
+
+const char *apk_digest_alg_str(uint8_t alg)
+{
+ const char *alg_str = "unknown";
+ if (alg < ARRAY_SIZE(apk_digest_str))
+ alg_str = apk_digest_str[alg];
+ return alg_str;
+}
+
+int apk_digest_alg_len(uint8_t alg)
+{
+ switch (alg) {
+ case APK_DIGEST_MD5: return 16;
+ case APK_DIGEST_SHA1: return 20;
+ case APK_DIGEST_SHA256_160: return 20;
+ case APK_DIGEST_SHA256: return 32;
+ case APK_DIGEST_SHA512: return 64;
+ default: return 0;
+ }
+}
+
+uint8_t apk_digest_alg_by_len(int len)
+{
+ switch (len) {
+ case 0: return APK_DIGEST_NONE;
+ case 16: return APK_DIGEST_MD5;
+ case 20: return APK_DIGEST_SHA1;
+ case 32: return APK_DIGEST_SHA256;
+ case 64: return APK_DIGEST_SHA512;
+ default: return APK_DIGEST_NONE;
+ }
+}
+
+uint8_t apk_digest_alg_from_csum(int csum)
+{
+ switch (csum) {
+ case APK_CHECKSUM_NONE: return APK_DIGEST_NONE;
+ case APK_CHECKSUM_MD5: return APK_DIGEST_MD5;
+ case APK_CHECKSUM_SHA1: return APK_DIGEST_SHA1;
+ default: return APK_DIGEST_NONE;
+ }
+}
+
+uint8_t apk_digest_from_blob(struct apk_digest *d, apk_blob_t b)
+{
+ d->alg = apk_digest_alg_by_len(b.len);
+ d->len = 0;
+ if (d->alg != APK_DIGEST_NONE) {
+ d->len = b.len;
+ memcpy(d->data, b.ptr, d->len);
+ }
+ return d->alg;
+}
+
+void apk_digest_from_checksum(struct apk_digest *d, const struct apk_checksum *c)
+{
+ apk_digest_set(d, apk_digest_alg_from_csum(c->type));
+ memcpy(d->data, c->data, d->len);
+}
+
+
+void apk_checksum_from_digest(struct apk_checksum *csum, const struct apk_digest *d)
+{
+ if (d->len > sizeof csum->data) {
+ csum->type = APK_CHECKSUM_NONE;
+ } else {
+ csum->type = d->len;
+ memcpy(csum->data, d->data, d->len);
+ }
+}
diff --git a/src/crypto_openssl.c b/src/crypto_openssl.c
index 73f1879..a335fb1 100644
--- a/src/crypto_openssl.c
+++ b/src/crypto_openssl.c
@@ -4,82 +4,123 @@
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/err.h>
+#include <openssl/opensslv.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#ifndef OPENSSL_NO_ENGINE
+#include <openssl/engine.h>
+#endif
#include "apk_crypto.h"
-static const char *apk_digest_str[] = {
- [APK_DIGEST_NONE] = "none",
- [APK_DIGEST_MD5] = "md5",
- [APK_DIGEST_SHA1] = "sha1",
- [APK_DIGEST_SHA256] = "sha256",
- [APK_DIGEST_SHA512] = "sha512",
-};
+// Copmatibility with older openssl
-const char *apk_digest_alg_str(uint8_t alg)
+#if OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
+
+static inline EVP_MD_CTX *EVP_MD_CTX_new(void)
{
- const char *alg_str = "unknown";
- if (alg < ARRAY_SIZE(apk_digest_str))
- alg_str = apk_digest_str[alg];
- return alg_str;
+ return EVP_MD_CTX_create();
}
-int apk_digest_alg_len(uint8_t alg)
+static inline void EVP_MD_CTX_free(EVP_MD_CTX *mdctx)
{
+ return EVP_MD_CTX_destroy(mdctx);
+}
+
+#endif
+
+static inline const EVP_MD *apk_digest_alg_to_evp(uint8_t alg) {
switch (alg) {
- case APK_DIGEST_MD5: return 16;
- case APK_DIGEST_SHA1: return 20;
- case APK_DIGEST_SHA256: return 32;
- case APK_DIGEST_SHA512: return 64;
- default: return 0;
+ case APK_DIGEST_NONE: return NULL;
+ case APK_DIGEST_MD5: return EVP_md5();
+ case APK_DIGEST_SHA1: return EVP_sha1();
+ case APK_DIGEST_SHA256_160:
+ case APK_DIGEST_SHA256: return EVP_sha256();
+ case APK_DIGEST_SHA512: return EVP_sha512();
+ default:
+ assert(alg);
+ return NULL;
}
}
-uint8_t apk_digest_alg_by_len(int len)
+int apk_digest_calc(struct apk_digest *d, uint8_t alg, const void *ptr, size_t sz)
{
- switch (len) {
- case 0: return APK_DIGEST_NONE;
- case 16: return APK_DIGEST_MD5;
- case 20: return APK_DIGEST_SHA1;
- case 32: return APK_DIGEST_SHA256;
- case 64: return APK_DIGEST_SHA512;
- default: return APK_DIGEST_NONE;
- }
+ unsigned int md_sz = sizeof d->data;
+ if (EVP_Digest(ptr, sz, d->data, &md_sz, apk_digest_alg_to_evp(alg), 0) != 1)
+ return -APKE_CRYPTO_ERROR;
+ apk_digest_set(d, alg);
+ return 0;
}
-uint8_t apk_digest_alg_from_csum(int csum)
+int apk_digest_ctx_init(struct apk_digest_ctx *dctx, uint8_t alg)
{
- switch (csum) {
- case APK_CHECKSUM_NONE: return APK_DIGEST_NONE;
- case APK_CHECKSUM_MD5: return APK_DIGEST_MD5;
- case APK_CHECKSUM_SHA1: return APK_DIGEST_SHA1;
- default: return APK_DIGEST_NONE;
- }
+ dctx->alg = alg;
+ dctx->mdctx = EVP_MD_CTX_new();
+ if (!dctx->mdctx) return -ENOMEM;
+#ifdef EVP_MD_CTX_FLAG_FINALISE
+ EVP_MD_CTX_set_flags(dctx->mdctx, EVP_MD_CTX_FLAG_FINALISE);
+#endif
+ if (EVP_DigestInit_ex(dctx->mdctx, apk_digest_alg_to_evp(alg), 0) != 1)
+ return -APKE_CRYPTO_ERROR;
+ return 0;
+}
+
+int apk_digest_ctx_reset(struct apk_digest_ctx *dctx)
+{
+ if (dctx->alg == APK_DIGEST_NONE) return 0;
+ if (EVP_DigestInit_ex(dctx->mdctx, NULL, 0) != 1) return -APKE_CRYPTO_ERROR;
+ return 0;
+}
+
+int apk_digest_ctx_reset_alg(struct apk_digest_ctx *dctx, uint8_t alg)
+{
+ if (EVP_MD_CTX_reset(dctx->mdctx) != 1 ||
+ EVP_DigestInit_ex(dctx->mdctx, apk_digest_alg_to_evp(alg), 0) != 1)
+ return -APKE_CRYPTO_ERROR;
+ dctx->alg = alg;
+ return 0;
+}
+
+void apk_digest_ctx_free(struct apk_digest_ctx *dctx)
+{
+ EVP_MD_CTX_free(dctx->mdctx);
+ dctx->mdctx = 0;
}
-uint8_t apk_digest_from_blob(struct apk_digest *d, apk_blob_t b)
+int apk_digest_ctx_update(struct apk_digest_ctx *dctx, const void *ptr, size_t sz)
{
- d->alg = apk_digest_alg_by_len(b.len);
- d->len = 0;
- if (d->alg != APK_DIGEST_NONE) {
- d->len = b.len;
- memcpy(d->data, b.ptr, d->len);
+ if (dctx->alg == APK_DIGEST_NONE) return 0;
+ return EVP_DigestUpdate(dctx->mdctx, ptr, sz) == 1 ? 0 : -APKE_CRYPTO_ERROR;
+}
+
+int apk_digest_ctx_final(struct apk_digest_ctx *dctx, struct apk_digest *d)
+{
+ unsigned int mdlen = sizeof d->data;
+ if (dctx->alg != APK_DIGEST_NONE &&
+ EVP_DigestFinal_ex(dctx->mdctx, d->data, &mdlen) != 1) {
+ apk_digest_reset(d);
+ return -APKE_CRYPTO_ERROR;
}
- return d->alg;
+ d->alg = dctx->alg;
+ d->len = apk_digest_alg_len(d->alg);
+ return 0;
}
-int apk_pkey_init(struct apk_pkey *pkey, EVP_PKEY *key)
+static int apk_pkey_init(struct apk_pkey *pkey, EVP_PKEY *key)
{
unsigned char dig[EVP_MAX_MD_SIZE], *pub = NULL;
unsigned int dlen = sizeof dig;
- int len;
+ int len, r = -APKE_CRYPTO_ERROR;
if ((len = i2d_PublicKey(key, &pub)) < 0) return -APKE_CRYPTO_ERROR;
- EVP_Digest(pub, len, dig, &dlen, EVP_sha512(), NULL);
- memcpy(pkey->id, dig, sizeof pkey->id);
+ if (EVP_Digest(pub, len, dig, &dlen, EVP_sha512(), NULL) == 1) {
+ memcpy(pkey->id, dig, sizeof pkey->id);
+ r = 0;
+ }
OPENSSL_free(pub);
-
pkey->key = key;
- return 0;
+
+ return r;
}
void apk_pkey_free(struct apk_pkey *pkey)
@@ -109,40 +150,66 @@ int apk_pkey_load(struct apk_pkey *pkey, int dirfd, const char *fn)
BIO_free(bio);
if (!key) return -APKE_CRYPTO_KEY_FORMAT;
- apk_pkey_init(pkey, key);
- return 0;
+ return apk_pkey_init(pkey, key);
}
-int apk_sign_start(struct apk_digest_ctx *dctx, struct apk_pkey *pkey)
+int apk_sign_start(struct apk_digest_ctx *dctx, uint8_t alg, struct apk_pkey *pkey)
{
if (EVP_MD_CTX_reset(dctx->mdctx) != 1 ||
- EVP_DigestSignInit(dctx->mdctx, NULL, EVP_sha512(), NULL, pkey->key) != 1)
+ EVP_DigestSignInit(dctx->mdctx, NULL, apk_digest_alg_to_evp(alg), NULL, pkey->key) != 1)
return -APKE_CRYPTO_ERROR;
+ dctx->alg = alg;
return 0;
}
int apk_sign(struct apk_digest_ctx *dctx, void *sig, size_t *len)
{
- if (EVP_DigestSignFinal(dctx->mdctx, sig, len) != 1) {
- ERR_print_errors_fp(stderr);
- return -APKE_SIGNATURE_FAIL;
- }
+ if (EVP_DigestSignFinal(dctx->mdctx, sig, len) != 1)
+ return -APKE_SIGNATURE_GEN_FAILURE;
return 0;
}
-int apk_verify_start(struct apk_digest_ctx *dctx, struct apk_pkey *pkey)
+int apk_verify_start(struct apk_digest_ctx *dctx, uint8_t alg, struct apk_pkey *pkey)
{
if (EVP_MD_CTX_reset(dctx->mdctx) != 1 ||
- EVP_DigestVerifyInit(dctx->mdctx, NULL, EVP_sha512(), NULL, pkey->key) != 1)
+ EVP_DigestVerifyInit(dctx->mdctx, NULL, apk_digest_alg_to_evp(alg), NULL, pkey->key) != 1)
return -APKE_CRYPTO_ERROR;
+ dctx->alg = alg;
return 0;
}
int apk_verify(struct apk_digest_ctx *dctx, void *sig, size_t len)
{
- if (EVP_DigestVerifyFinal(dctx->mdctx, sig, len) != 1) {
- ERR_print_errors_fp(stderr);
+ if (EVP_DigestVerifyFinal(dctx->mdctx, sig, len) != 1)
return -APKE_SIGNATURE_INVALID;
- }
return 0;
}
+
+#if OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
+
+static void apk_crypto_cleanup(void)
+{
+ EVP_cleanup();
+#ifndef OPENSSL_NO_ENGINE
+ ENGINE_cleanup();
+#endif
+ CRYPTO_cleanup_all_ex_data();
+}
+
+void apk_crypto_init(void)
+{
+ atexit(apk_crypto_cleanup);
+ OpenSSL_add_all_algorithms();
+#ifndef OPENSSL_NO_ENGINE
+ ENGINE_load_builtin_engines();
+ ENGINE_register_all_complete();
+#endif
+}
+
+#else
+
+void apk_crypto_init(void)
+{
+}
+
+#endif
diff --git a/src/ctype.c b/src/ctype.c
new file mode 100644
index 0000000..0fdcf5d
--- /dev/null
+++ b/src/ctype.c
@@ -0,0 +1,134 @@
+/* ctype.c - Alpine Package Keeper (APK)
+ *
+ * Copyright (C) 2024 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#include "apk_defines.h"
+#include "apk_blob.h"
+#include "apk_ctype.h"
+
+#define HEXDGT BIT(APK_CTYPE_HEXDIGIT)
+#define PKGNAME BIT(APK_CTYPE_PACKAGE_NAME)|BIT(APK_CTYPE_DEPENDENCY_NAME)
+#define VERSUF BIT(APK_CTYPE_VERSION_SUFFIX)
+#define DEPNAME BIT(APK_CTYPE_DEPENDENCY_NAME)
+#define DEPCOMP BIT(APK_CTYPE_DEPENDENCY_COMPARER)
+#define DEPSEP BIT(APK_CTYPE_DEPENDENCY_SEPARATOR)
+#define REPOSEP BIT(APK_CTYPE_REPOSITORY_SEPARATOR)
+
+static uint8_t apk_ctype[128] = {
+ ['\t'] = REPOSEP,
+ ['\n'] = DEPSEP,
+ [' '] = REPOSEP|DEPSEP,
+ ['+'] = PKGNAME,
+ [','] = DEPNAME,
+ ['-'] = PKGNAME,
+ ['.'] = PKGNAME,
+ [':'] = REPOSEP|DEPNAME,
+ ['<'] = DEPCOMP,
+ ['='] = DEPCOMP,
+ ['>'] = DEPCOMP,
+ ['/'] = DEPNAME,
+ ['0'] = HEXDGT|PKGNAME,
+ ['1'] = HEXDGT|PKGNAME,
+ ['2'] = HEXDGT|PKGNAME,
+ ['3'] = HEXDGT|PKGNAME,
+ ['4'] = HEXDGT|PKGNAME,
+ ['5'] = HEXDGT|PKGNAME,
+ ['6'] = HEXDGT|PKGNAME,
+ ['7'] = HEXDGT|PKGNAME,
+ ['8'] = HEXDGT|PKGNAME,
+ ['9'] = HEXDGT|PKGNAME,
+ ['A'] = PKGNAME,
+ ['B'] = PKGNAME,
+ ['C'] = PKGNAME,
+ ['D'] = PKGNAME,
+ ['E'] = PKGNAME,
+ ['F'] = PKGNAME,
+ ['G'] = PKGNAME,
+ ['H'] = PKGNAME,
+ ['I'] = PKGNAME,
+ ['J'] = PKGNAME,
+ ['K'] = PKGNAME,
+ ['L'] = PKGNAME,
+ ['M'] = PKGNAME,
+ ['N'] = PKGNAME,
+ ['O'] = PKGNAME,
+ ['P'] = PKGNAME,
+ ['Q'] = PKGNAME,
+ ['R'] = PKGNAME,
+ ['S'] = PKGNAME,
+ ['T'] = PKGNAME,
+ ['U'] = PKGNAME,
+ ['V'] = PKGNAME,
+ ['W'] = PKGNAME,
+ ['X'] = PKGNAME,
+ ['Y'] = PKGNAME,
+ ['Z'] = PKGNAME,
+ ['['] = DEPNAME,
+ [']'] = DEPNAME,
+ ['_'] = PKGNAME,
+ ['a'] = HEXDGT|VERSUF|PKGNAME,
+ ['b'] = HEXDGT|VERSUF|PKGNAME,
+ ['c'] = HEXDGT|VERSUF|PKGNAME,
+ ['d'] = HEXDGT|VERSUF|PKGNAME,
+ ['e'] = HEXDGT|VERSUF|PKGNAME,
+ ['f'] = HEXDGT|VERSUF|PKGNAME,
+ ['g'] = VERSUF|PKGNAME,
+ ['h'] = VERSUF|PKGNAME,
+ ['i'] = VERSUF|PKGNAME,
+ ['j'] = VERSUF|PKGNAME,
+ ['k'] = VERSUF|PKGNAME,
+ ['l'] = VERSUF|PKGNAME,
+ ['m'] = VERSUF|PKGNAME,
+ ['n'] = VERSUF|PKGNAME,
+ ['o'] = VERSUF|PKGNAME,
+ ['p'] = VERSUF|PKGNAME,
+ ['q'] = VERSUF|PKGNAME,
+ ['r'] = VERSUF|PKGNAME,
+ ['s'] = VERSUF|PKGNAME,
+ ['t'] = VERSUF|PKGNAME,
+ ['u'] = VERSUF|PKGNAME,
+ ['v'] = VERSUF|PKGNAME,
+ ['w'] = VERSUF|PKGNAME,
+ ['x'] = VERSUF|PKGNAME,
+ ['y'] = VERSUF|PKGNAME,
+ ['z'] = VERSUF|PKGNAME,
+ ['~'] = DEPCOMP,
+};
+
+int apk_blob_spn(apk_blob_t blob, unsigned char ctype, apk_blob_t *l, apk_blob_t *r)
+{
+ uint8_t mask = BIT(ctype);
+ int i, ret = 0;
+
+ for (i = 0; i < blob.len; i++) {
+ uint8_t ch = blob.ptr[i];
+ if (ch < ARRAY_SIZE(apk_ctype) && !(apk_ctype[ch]&mask)) {
+ ret = 1;
+ break;
+ }
+ }
+ if (l != NULL) *l = APK_BLOB_PTR_LEN(blob.ptr, i);
+ if (r != NULL) *r = APK_BLOB_PTR_LEN(blob.ptr+i, blob.len-i);
+ return ret;
+}
+
+int apk_blob_cspn(apk_blob_t blob, unsigned char ctype, apk_blob_t *l, apk_blob_t *r)
+{
+ uint8_t mask = BIT(ctype);
+ int i, ret = 0;
+
+ for (i = 0; i < blob.len; i++) {
+ uint8_t ch = blob.ptr[i];
+ if (ch >= ARRAY_SIZE(apk_ctype) || (apk_ctype[ch]&mask)) {
+ ret = 1;
+ break;
+ }
+ }
+ if (l != NULL) *l = APK_BLOB_PTR_LEN(blob.ptr, i);
+ if (r != NULL) *r = APK_BLOB_PTR_LEN(blob.ptr+i, blob.len-i);
+ return ret;
+}
diff --git a/src/database.c b/src/database.c
index 10e9776..b631dde 100644
--- a/src/database.c
+++ b/src/database.c
@@ -10,37 +10,35 @@
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
-#include <mntent.h>
#include <libgen.h>
#include <limits.h>
#include <unistd.h>
-#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <fnmatch.h>
-#include <sys/vfs.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <sys/stat.h>
-#include <sys/mount.h>
-#include <sys/statvfs.h>
-#include <sys/sysmacros.h>
-#include <linux/magic.h>
+
+#ifdef __linux__
+# include <mntent.h>
+# include <sys/vfs.h>
+# include <sys/mount.h>
+# include <sys/statvfs.h>
+# include <linux/magic.h>
+#endif
#include "apk_defines.h"
#include "apk_package.h"
#include "apk_database.h"
#include "apk_applet.h"
-#include "apk_archive.h"
+#include "apk_ctype.h"
+#include "apk_extract.h"
#include "apk_print.h"
-#include "apk_openssl.h"
-
-static const apk_spn_match_def apk_spn_repo_separators = {
- [1] = (1<<1) /* tab */,
- [4] = (1<<0) /* */,
- [7] = (1<<2) /*:*/,
-};
+#include "apk_tar.h"
+#include "apk_adb.h"
+#include "apk_fs.h"
enum {
APK_DIR_FREE = 0,
@@ -52,9 +50,6 @@ static const char * const apk_static_cache_dir = "var/cache/apk";
static const char * const apk_world_file = "etc/apk/world";
static const char * const apk_arch_file = "etc/apk/arch";
static const char * const apk_lock_file = "lib/apk/db/lock";
-static const char * const apk_scripts_file = "lib/apk/db/scripts.tar";
-static const char * const apk_triggers_file = "lib/apk/db/triggers";
-const char * const apk_installed_file = "lib/apk/db/installed";
static struct apk_db_acl *apk_default_acl_dir, *apk_default_acl_file;
@@ -65,12 +60,12 @@ struct install_ctx {
int script;
char **script_args;
- int script_pending : 1;
- int missing_checksum : 1;
+ unsigned int script_pending : 1;
+ unsigned int missing_checksum : 1;
struct apk_db_dir_instance *diri;
struct apk_checksum data_csum;
- struct apk_sign_ctx sctx;
+ struct apk_extract_ctx ectx;
apk_progress_cb cb;
void *cb_ctx;
@@ -81,6 +76,18 @@ struct install_ctx {
struct hlist_node **file_diri_node;
};
+static mode_t apk_db_dir_get_mode(struct apk_database *db, mode_t mode)
+{
+ // in usermode, return mode that makes the file readable for user
+ if (db->usermode) return mode | S_IWUSR | S_IXUSR;
+ return mode;
+}
+
+static apk_blob_t apk_pkg_ctx(struct apk_package *pkg)
+{
+ return APK_BLOB_PTR_LEN(pkg->name->name, strlen(pkg->name->name)+1);
+}
+
static apk_blob_t pkg_name_get_key(apk_hash_item item)
{
return APK_BLOB_STR(((struct apk_name *) item)->name);
@@ -207,10 +214,26 @@ struct apk_name *apk_db_get_name(struct apk_database *db, apk_blob_t name)
apk_name_array_init(&pn->rdepends);
apk_name_array_init(&pn->rinstall_if);
apk_hash_insert_hashed(&db->available.names, pn, hash);
+ db->sorted_names = 0;
return pn;
}
+static int cmp_provider(const void *a, const void *b)
+{
+ const struct apk_provider *pa = a, *pb = b;
+ return apk_pkg_cmp_display(pa->pkg, pb->pkg);
+}
+
+struct apk_provider_array *apk_name_sorted_providers(struct apk_name *name)
+{
+ if (!name->providers_sorted) {
+ qsort(name->providers->item, name->providers->num, sizeof name->providers->item[0], cmp_provider);
+ name->providers_sorted = 0;
+ }
+ return name->providers;
+}
+
static struct apk_db_acl *__apk_db_acl_atomize(struct apk_database *db, mode_t mode, uid_t uid, gid_t gid, uint8_t csum_type, const uint8_t *csum_data)
{
struct apk_db_acl acl = { .mode = mode & 07777, .uid = uid, .gid = gid };
@@ -240,25 +263,39 @@ static struct apk_db_acl *apk_db_acl_atomize_digest(struct apk_database *db, mod
return __apk_db_acl_atomize(db, mode, uid, gid, dig->len, dig->data);
}
-static void apk_db_dir_prepare(struct apk_database *db, struct apk_db_dir *dir, mode_t newmode)
+static int apk_db_dir_mkdir(struct apk_database *db, struct apk_fsdir *d, struct apk_db_acl *acl)
{
- struct stat st;
+ if (db->ctx->flags & APK_SIMULATE) return 0;
+ return apk_fsdir_create(d, apk_db_dir_get_mode(db, acl->mode), acl->uid, acl->gid);
+}
+
+void apk_db_dir_prepare(struct apk_database *db, struct apk_db_dir *dir, struct apk_db_acl *expected_acl, struct apk_db_acl *new_acl)
+{
+ struct apk_fsdir d;
if (dir->namelen == 0) return;
if (dir->created) return;
+ dir->created = 1;
+
+ apk_fsdir_get(&d, APK_BLOB_PTR_LEN(dir->name, dir->namelen), db->extract_flags, db->ctx, APK_BLOB_NULL);
+ if (!expected_acl) {
+ /* Directory should not exist. Create it. */
+ if (apk_db_dir_mkdir(db, &d, new_acl) == 0)
+ dir->permissions_ok = 1;
+ return;
+ }
- if (fstatat(db->root_fd, dir->name, &st, AT_SYMLINK_NOFOLLOW) == 0) {
- /* If directory exists and stats match what we expect,
- * then we can allow auto updating the permissions */
- dir->created = 1;
- dir->update_permissions |=
- (st.st_mode & 07777) == (dir->mode & 07777) &&
- st.st_uid == dir->uid && st.st_gid == dir->gid;
- } else if (newmode) {
- if (!(db->ctx->flags & APK_SIMULATE))
- mkdirat(db->root_fd, dir->name, newmode);
- dir->created = 1;
- dir->update_permissions = 1;
+ switch (apk_fsdir_check(&d, apk_db_dir_get_mode(db, expected_acl->mode), expected_acl->uid, expected_acl->gid)) {
+ case -ENOENT:
+ if (apk_db_dir_mkdir(db, &d, new_acl) == 0)
+ dir->permissions_ok = 1;
+ break;
+ case 0:
+ dir->permissions_ok = 1;
+ break;
+ case APK_FS_DIR_MODIFIED:
+ default:
+ break;
}
}
@@ -267,17 +304,21 @@ void apk_db_dir_unref(struct apk_database *db, struct apk_db_dir *dir, int rmdir
if (--dir->refs > 0) return;
db->installed.stats.dirs--;
apk_protected_path_array_free(&dir->protected_paths);
+ list_del(&dir->diris);
if (dir->namelen != 0) {
if (rmdir_mode == APK_DIR_REMOVE) {
dir->modified = 1;
- if (!(db->ctx->flags & APK_SIMULATE) &&
- unlinkat(db->root_fd, dir->name, AT_REMOVEDIR) != 0)
- ;
+ if (!(db->ctx->flags & APK_SIMULATE)) {
+ struct apk_fsdir d;
+ apk_fsdir_get(&d, APK_BLOB_PTR_LEN(dir->name, dir->namelen),
+ db->extract_flags, db->ctx, APK_BLOB_NULL);
+ apk_fsdir_delete(&d);
+ }
}
apk_db_dir_unref(db, dir->parent, rmdir_mode);
dir->parent = NULL;
}
- dir->seen = dir->created = dir->update_permissions = 0;
+ dir->created = dir->permissions_ok = 0;
}
struct apk_db_dir *apk_db_dir_ref(struct apk_db_dir *dir)
@@ -312,14 +353,13 @@ struct apk_db_dir *apk_db_dir_get(struct apk_database *db, apk_blob_t name)
dir->name[name.len] = 0;
dir->namelen = name.len;
dir->hash = hash;
+ list_init(&dir->diris);
apk_protected_path_array_init(&dir->protected_paths);
apk_hash_insert_hashed(&db->installed.dirs, dir, hash);
}
db->installed.stats.dirs++;
dir->refs = 1;
- dir->uid = (uid_t) -1;
- dir->gid = (gid_t) -1;
if (name.len == 0) {
dir->parent = NULL;
@@ -328,7 +368,7 @@ struct apk_db_dir *apk_db_dir_get(struct apk_database *db, apk_blob_t name)
} else if (apk_blob_rsplit(name, '/', &bparent, NULL)) {
dir->parent = apk_db_dir_get(db, bparent);
dir->protect_mode = dir->parent->protect_mode;
- dir->has_protected_children = (dir->protect_mode != APK_PROTECT_NONE);
+ dir->has_protected_children = !apk_protect_mode_none(dir->protect_mode);
ppaths = dir->parent->protected_paths;
} else {
dir->parent = apk_db_dir_get(db, APK_BLOB_NULL);
@@ -359,7 +399,7 @@ struct apk_db_dir *apk_db_dir_get(struct apk_database *db, apk_blob_t name)
dir->protect_mode = ppath->protect_mode;
}
- dir->has_protected_children |= (ppath->protect_mode != APK_PROTECT_NONE);
+ dir->has_protected_children |= !apk_protect_mode_none(ppath->protect_mode);
}
return dir;
@@ -374,9 +414,12 @@ static struct apk_db_dir_instance *apk_db_diri_new(struct apk_database *db,
diri = calloc(1, sizeof(struct apk_db_dir_instance));
if (diri != NULL) {
+ struct apk_db_dir *dir = apk_db_dir_get(db, name);
+ list_init(&diri->dir_diri_list);
+ list_add(&diri->dir_diri_list, &dir->diris);
hlist_add_after(&diri->pkg_dirs_list, *after);
*after = &diri->pkg_dirs_list.next;
- diri->dir = apk_db_dir_get(db, name);
+ diri->dir = dir;
diri->pkg = pkg;
diri->acl = apk_default_acl_dir;
}
@@ -384,36 +427,52 @@ static struct apk_db_dir_instance *apk_db_diri_new(struct apk_database *db,
return diri;
}
-static void apk_db_dir_apply_diri_permissions(struct apk_db_dir_instance *diri)
+void apk_db_dir_update_permissions(struct apk_database *db, struct apk_db_dir_instance *diri)
{
struct apk_db_dir *dir = diri->dir;
struct apk_db_acl *acl = diri->acl;
+ struct apk_fsdir d;
- if (acl->uid < dir->uid || (acl->uid == dir->uid && acl->gid < dir->gid)) {
- dir->uid = acl->uid;
- dir->gid = acl->gid;
- dir->mode = acl->mode;
- } else if (acl->uid == dir->uid && acl->gid == dir->gid) {
- dir->mode &= acl->mode;
- }
+ if (!dir->permissions_ok) return;
+ if (db->ctx->flags & APK_SIMULATE) return;
+
+ dir->modified = 1;
+ apk_fsdir_get(&d, APK_BLOB_PTR_LEN(dir->name, dir->namelen), db->extract_flags, db->ctx, APK_BLOB_NULL);
+ if (apk_fsdir_update_perms(&d, apk_db_dir_get_mode(db, acl->mode), acl->uid, acl->gid) != 0)
+ db->num_dir_update_errors++;
}
-static void apk_db_diri_set(struct apk_db_dir_instance *diri, struct apk_db_acl *acl)
+static void apk_db_dir_apply_diri_permissions(struct apk_database *db, struct apk_db_dir_instance *diri)
{
- diri->acl = acl;
- apk_db_dir_apply_diri_permissions(diri);
+ struct apk_db_dir *dir = diri->dir;
+ struct apk_db_acl *acl = diri->acl;
+
+ if (dir->owner && apk_pkg_replaces_dir(dir->owner->pkg, diri->pkg) != APK_PKG_REPLACES_YES)
+ return;
+
+ // Check if the ACL changed and the directory needs update
+ if (dir->owner && dir->owner->acl != acl) apk_db_dir_update_permissions(db, diri);
+ dir->owner = diri;
}
static void apk_db_diri_free(struct apk_database *db,
struct apk_db_dir_instance *diri,
int rmdir_mode)
{
- struct apk_db_dir *dir = diri->dir;
-
- if (rmdir_mode == APK_DIR_REMOVE)
- apk_db_dir_prepare(db, diri->dir, 0);
-
- apk_db_dir_unref(db, dir, rmdir_mode);
+ list_del(&diri->dir_diri_list);
+ if (rmdir_mode == APK_DIR_REMOVE && diri->dir->owner == diri) {
+ // Walk the directory instance to determine new owner
+ struct apk_db_dir *dir = diri->dir;
+ struct apk_db_dir_instance *di;
+ dir->owner = NULL;
+ list_for_each_entry(di, &dir->diris, dir_diri_list) {
+ if (dir->owner == NULL ||
+ apk_pkg_replaces_dir(dir->owner->pkg, di->pkg) == APK_PKG_REPLACES_YES)
+ dir->owner = di;
+ }
+ if (dir->owner) apk_db_dir_update_permissions(db, dir->owner);
+ }
+ apk_db_dir_unref(db, diri->dir, rmdir_mode);
free(diri);
}
@@ -486,29 +545,29 @@ static struct apk_db_file *apk_db_file_get(struct apk_database *db,
return file;
}
+static void add_name_to_array(struct apk_name *name, struct apk_name_array **a)
+{
+ struct apk_name **n;
+
+ foreach_array_item(n, *a)
+ if (*n == name) return;
+ *apk_name_array_add(a) = name;
+}
+
static void apk_db_pkg_rdepends(struct apk_database *db, struct apk_package *pkg)
{
- struct apk_name *rname, **rd;
+ struct apk_name *rname;
struct apk_dependency *d;
foreach_array_item(d, pkg->depends) {
rname = d->name;
- rname->is_dependency |= !d->conflict;
- foreach_array_item(rd, rname->rdepends)
- if (*rd == pkg->name)
- goto rdeps_done;
- *apk_name_array_add(&rname->rdepends) = pkg->name;
-rdeps_done: ;
+ rname->is_dependency |= !apk_dep_conflict(d);
+ add_name_to_array(pkg->name, &rname->rdepends);
}
foreach_array_item(d, pkg->install_if) {
rname = d->name;
- foreach_array_item(rd, rname->rinstall_if)
- if (*rd == pkg->name)
- goto riif_done;
- *apk_name_array_add(&rname->rinstall_if) = pkg->name;
-riif_done: ;
+ add_name_to_array(pkg->name, &rname->rinstall_if);
}
- return;
}
static inline void add_provider(struct apk_name *name, struct apk_provider p)
@@ -525,10 +584,8 @@ struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package *
if (!pkg->license) pkg->license = &apk_atom_null;
- /* Set as "cached" if installing from specified file, and
- * for virtual packages */
- if (pkg->filename != NULL || pkg->installed_size == 0)
- pkg->repos |= BIT(APK_REPOSITORY_CACHED);
+ // Set as "cached" if installing from specified file
+ if (pkg->filename_ndx) pkg->repos |= BIT(APK_REPOSITORY_CACHED);
idb = apk_hash_get(&db->available.packages, APK_BLOB_CSUM(pkg->csum));
if (idb == NULL) {
@@ -541,10 +598,7 @@ struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package *
apk_db_pkg_rdepends(db, pkg);
} else {
idb->repos |= pkg->repos;
- if (idb->filename == NULL && pkg->filename != NULL) {
- idb->filename = pkg->filename;
- pkg->filename = NULL;
- }
+ if (!idb->filename_ndx) idb->filename_ndx = pkg->filename_ndx;
if (idb->ipkg == NULL && pkg->ipkg != NULL) {
idb->ipkg = pkg->ipkg;
idb->ipkg->pkg = idb;
@@ -575,7 +629,7 @@ int apk_repo_format_cache_index(apk_blob_t to, struct apk_repository *repo)
{
/* APKINDEX.12345678.tar.gz */
apk_blob_push_blob(&to, APK_BLOB_STR("APKINDEX."));
- apk_blob_push_hexdump(&to, APK_BLOB_PTR_LEN((char *) repo->csum.data, APK_CACHE_CSUM_BYTES));
+ apk_blob_push_hexdump(&to, APK_BLOB_PTR_LEN((char *) repo->hash.data, APK_CACHE_CSUM_BYTES));
apk_blob_push_blob(&to, APK_BLOB_STR(".tar.gz"));
apk_blob_push_blob(&to, APK_BLOB_PTR_LEN("", 1));
if (APK_BLOB_IS_NULL(to))
@@ -587,20 +641,32 @@ int apk_repo_format_real_url(apk_blob_t *default_arch, struct apk_repository *re
struct apk_package *pkg, char *buf, size_t len,
struct apk_url_print *urlp)
{
+
+ apk_blob_t uri = APK_BLOB_STR(repo->url);
apk_blob_t arch;
int r;
if (pkg && pkg->arch) arch = *pkg->arch;
else arch = *default_arch;
- if (pkg != NULL)
- r = snprintf(buf, len, "%s%s" BLOB_FMT "/" PKG_FILE_FMT,
- repo->url, repo->url[strlen(repo->url)-1] == '/' ? "" : "/",
- BLOB_PRINTF(arch), PKG_FILE_PRINTF(pkg));
- else
- r = snprintf(buf, len, "%s%s" BLOB_FMT "/%s",
- repo->url, repo->url[strlen(repo->url)-1] == '/' ? "" : "/",
- BLOB_PRINTF(arch), apkindex_tar_gz);
+ if (apk_blob_ends_with(uri, APK_BLOB_STR(".adb"))) {
+ if (pkg != NULL) {
+ apk_blob_rsplit(uri, '/', &uri, NULL);
+ r = snprintf(buf, len, BLOB_FMT "/" PKG_FILE_FMT,
+ BLOB_PRINTF(uri), PKG_FILE_PRINTF(pkg));
+ } else {
+ r = snprintf(buf, len, BLOB_FMT, BLOB_PRINTF(uri));
+ }
+ } else {
+ while (uri.len && uri.ptr[uri.len-1] == '/') uri.len--;
+ if (pkg != NULL)
+ r = snprintf(buf, len, BLOB_FMT "/" BLOB_FMT "/" PKG_FILE_FMT,
+ BLOB_PRINTF(uri), BLOB_PRINTF(arch), PKG_FILE_PRINTF(pkg));
+ else
+ r = snprintf(buf, len, BLOB_FMT "/" BLOB_FMT "/%s",
+ BLOB_PRINTF(uri), BLOB_PRINTF(arch), apkindex_tar_gz);
+ }
+
if (r >= len)
return -ENOBUFS;
@@ -612,16 +678,17 @@ int apk_repo_format_item(struct apk_database *db, struct apk_repository *repo, s
int *fd, char *buf, size_t len)
{
if (repo->url == db->repos[APK_REPOSITORY_CACHED].url) {
+ if (db->cache_fd < 0) return db->cache_fd;
*fd = db->cache_fd;
return apk_pkg_format_cache_pkg(APK_BLOB_PTR_LEN(buf, len), pkg);
- } else {
- *fd = AT_FDCWD;
- return apk_repo_format_real_url(db->arch, repo, pkg, buf, len, 0);
}
+
+ *fd = AT_FDCWD;
+ return apk_repo_format_real_url(db->arch, repo, pkg, buf, len, 0);
}
int apk_cache_download(struct apk_database *db, struct apk_repository *repo,
- struct apk_package *pkg, int verify, int autoupdate,
+ struct apk_package *pkg, int autoupdate,
apk_progress_cb cb, void *cb_ctx)
{
struct apk_out *out = &db->ctx->out;
@@ -629,12 +696,14 @@ int apk_cache_download(struct apk_database *db, struct apk_repository *repo,
struct apk_url_print urlp;
struct apk_istream *is;
struct apk_ostream *os;
- struct apk_sign_ctx sctx;
+ struct apk_extract_ctx ectx;
char url[PATH_MAX];
char cacheitem[128];
int r;
time_t now = time(NULL);
+ if (db->cache_fd < 0) return db->cache_fd;
+
if (pkg != NULL)
r = apk_pkg_format_cache_pkg(APK_BLOB_BUF(cacheitem), pkg);
else
@@ -658,29 +727,16 @@ int apk_cache_download(struct apk_database *db, struct apk_repository *repo,
if (cb) cb(cb_ctx, 0);
- if (verify != APK_SIGN_NONE) {
- apk_sign_ctx_init(&sctx, APK_SIGN_VERIFY, NULL, apk_ctx_get_trust(db->ctx));
- is = apk_istream_from_url(url, apk_db_url_since(db, st.st_mtime));
- is = apk_istream_tee(is, os, autoupdate ? 0 : APK_ISTREAM_TEE_COPY_META, cb, cb_ctx);
- is = apk_istream_gunzip_mpart(is, apk_sign_ctx_mpart_cb, &sctx);
- r = apk_tar_parse(is, apk_sign_ctx_verify_tar, &sctx, db->id_cache);
- apk_sign_ctx_free(&sctx);
- } else {
- is = apk_istream_from_url(url, apk_db_url_since(db, st.st_mtime));
- if (!IS_ERR(is)) {
- apk_stream_copy(is, os, APK_IO_ALL, cb, cb_ctx, 0);
- if (!autoupdate) apk_ostream_copy_meta(os, is);
- apk_istream_close(is);
- } else {
- apk_ostream_cancel(os, PTR_ERR(is));
- }
- r = apk_ostream_close(os);
- }
+ is = apk_istream_from_url(url, apk_db_url_since(db, st.st_mtime));
+ is = apk_istream_tee(is, os, autoupdate ? 0 : APK_ISTREAM_TEE_COPY_META, cb, cb_ctx);
+ apk_extract_init(&ectx, db->ctx, 0);
+ if (pkg) apk_extract_verify_identity(&ectx, &pkg->csum);
+ r = apk_extract(&ectx, is);
if (r == -EALREADY) {
if (autoupdate) utimensat(db->cache_fd, cacheitem, NULL, 0);
return r;
}
- return 0;
+ return r;
}
static struct apk_db_dir_instance *find_diri(struct apk_installed_package *ipkg,
@@ -716,7 +772,7 @@ int apk_db_read_overlay(struct apk_database *db, struct apk_istream *is)
struct apk_installed_package *ipkg;
apk_blob_t token = APK_BLOB_STR("\n"), line, bdir, bfile;
- if (IS_ERR_OR_NULL(is)) return PTR_ERR(is);
+ if (IS_ERR(is)) return PTR_ERR(is);
pkg = apk_pkg_new();
if (!pkg) goto no_mem;
@@ -753,7 +809,7 @@ err:
return apk_istream_close(is);
}
-int apk_db_index_read(struct apk_database *db, struct apk_istream *is, int repo)
+static int apk_db_fdb_read(struct apk_database *db, struct apk_istream *is, int repo, unsigned layer)
{
struct apk_out *out = &db->ctx->out;
struct apk_package *pkg = NULL;
@@ -770,7 +826,7 @@ int apk_db_index_read(struct apk_database *db, struct apk_istream *is, int repo)
gid_t gid;
int field, r, lineno = 0;
- if (IS_ERR_OR_NULL(is)) return PTR_ERR(is);
+ if (IS_ERR(is)) return PTR_ERR(is);
while (apk_istream_get_delim(is, token, &l) == 0) {
lineno++;
@@ -779,7 +835,7 @@ int apk_db_index_read(struct apk_database *db, struct apk_istream *is, int repo)
if (pkg == NULL)
continue;
- if (diri) apk_db_dir_apply_diri_permissions(diri);
+ if (diri) apk_db_dir_apply_diri_permissions(db, diri);
if (repo >= 0) {
pkg->repos |= BIT(repo);
@@ -806,6 +862,7 @@ int apk_db_index_read(struct apk_database *db, struct apk_istream *is, int repo)
/* If no package, create new */
if (pkg == NULL) {
pkg = apk_pkg_new();
+ pkg->layer = layer;
ipkg = NULL;
diri = NULL;
file_diri_node = NULL;
@@ -828,7 +885,7 @@ int apk_db_index_read(struct apk_database *db, struct apk_istream *is, int repo)
/* Check FDB special entries */
switch (field) {
case 'F':
- if (diri) apk_db_dir_apply_diri_permissions(diri);
+ if (diri) apk_db_dir_apply_diri_permissions(db, diri);
if (pkg->name == NULL) goto bad_entry;
diri = find_diri(ipkg, l, NULL, &diri_node);
if (!diri) diri = apk_db_diri_new(db, pkg, l, &diri_node);
@@ -877,6 +934,7 @@ int apk_db_index_read(struct apk_database *db, struct apk_istream *is, int repo)
case 'f': ipkg->broken_files = 1; break;
case 's': ipkg->broken_script = 1; break;
case 'x': ipkg->broken_xattr = 1; break;
+ case 'S': ipkg->sha256_160 = 1; break;
default:
if (!(db->ctx->force & APK_FORCE_OLD_APK))
goto old_apk_tools;
@@ -887,7 +945,7 @@ int apk_db_index_read(struct apk_database *db, struct apk_istream *is, int repo)
if (r != 0 && !(db->ctx->force & APK_FORCE_OLD_APK))
goto old_apk_tools;
/* Installed. So mark the package as installable. */
- pkg->filename = NULL;
+ pkg->filename_ndx = 0;
continue;
}
if (APK_BLOB_IS_NULL(l)) goto bad_entry;
@@ -904,6 +962,11 @@ err_fmt:
return apk_istream_close(is);
}
+int apk_db_index_read(struct apk_database *db, struct apk_istream *is, int repo)
+{
+ return apk_db_fdb_read(db, is, repo, 0);
+}
+
static void apk_blob_push_db_acl(apk_blob_t *b, char field, struct apk_db_acl *acl)
{
char hdr[2] = { field, ':' };
@@ -921,128 +984,134 @@ static void apk_blob_push_db_acl(apk_blob_t *b, char field, struct apk_db_acl *a
apk_blob_push_blob(b, APK_BLOB_STR("\n"));
}
-static int apk_db_write_fdb(struct apk_database *db, struct apk_ostream *os)
+static int apk_db_fdb_write(struct apk_database *db, struct apk_installed_package *ipkg, struct apk_ostream *os)
{
- struct apk_installed_package *ipkg;
- struct apk_package *pkg;
+ struct apk_package *pkg = ipkg->pkg;
struct apk_db_dir_instance *diri;
struct apk_db_file *file;
struct hlist_node *c1, *c2;
- char buf[1024];
+ char buf[1024+PATH_MAX];
apk_blob_t bbuf = APK_BLOB_BUF(buf);
- int r;
+ int r = 0;
- list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
- pkg = ipkg->pkg;
- r = apk_pkg_write_index_entry(pkg, os);
- if (r < 0) {
- return r;
- }
+ if (IS_ERR(os)) return PTR_ERR(os);
- if (ipkg->replaces->num) {
- apk_blob_push_blob(&bbuf, APK_BLOB_STR("r:"));
- apk_blob_push_deps(&bbuf, db, ipkg->replaces);
- apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
- }
- if (ipkg->replaces_priority) {
- apk_blob_push_blob(&bbuf, APK_BLOB_STR("q:"));
- apk_blob_push_uint(&bbuf, ipkg->replaces_priority, 10);
- apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
- }
- if (ipkg->repository_tag) {
- apk_blob_push_blob(&bbuf, APK_BLOB_STR("s:"));
- apk_blob_push_blob(&bbuf, db->repo_tags[ipkg->repository_tag].plain_name);
- apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
+ r = apk_pkg_write_index_header(pkg, os);
+ if (r < 0) goto err;
+
+ if (ipkg->replaces->num) {
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("r:"));
+ apk_blob_push_deps(&bbuf, db, ipkg->replaces);
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
+ }
+ if (ipkg->replaces_priority) {
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("q:"));
+ apk_blob_push_uint(&bbuf, ipkg->replaces_priority, 10);
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
+ }
+ if (ipkg->repository_tag) {
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("s:"));
+ apk_blob_push_blob(&bbuf, db->repo_tags[ipkg->repository_tag].plain_name);
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
+ }
+ if (ipkg->broken_files || ipkg->broken_script || ipkg->broken_xattr || ipkg->sha256_160) {
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("f:"));
+ if (ipkg->broken_files)
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("f"));
+ if (ipkg->broken_script)
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("s"));
+ if (ipkg->broken_xattr)
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("x"));
+ if (ipkg->sha256_160)
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("S"));
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
+ }
+ hlist_for_each_entry(diri, c1, &ipkg->owned_dirs, pkg_dirs_list) {
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("F:"));
+ apk_blob_push_blob(&bbuf, APK_BLOB_PTR_LEN(diri->dir->name, diri->dir->namelen));
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
+
+ if (diri->acl != apk_default_acl_dir)
+ apk_blob_push_db_acl(&bbuf, 'M', diri->acl);
+
+ bbuf = apk_blob_pushed(APK_BLOB_BUF(buf), bbuf);
+ if (APK_BLOB_IS_NULL(bbuf)) {
+ r = -ENOBUFS;
+ goto err;
}
- if (ipkg->broken_files || ipkg->broken_script || ipkg->broken_xattr) {
- apk_blob_push_blob(&bbuf, APK_BLOB_STR("f:"));
- if (ipkg->broken_files)
- apk_blob_push_blob(&bbuf, APK_BLOB_STR("f"));
- if (ipkg->broken_script)
- apk_blob_push_blob(&bbuf, APK_BLOB_STR("s"));
- if (ipkg->broken_xattr)
- apk_blob_push_blob(&bbuf, APK_BLOB_STR("x"));
- apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
- }
- hlist_for_each_entry(diri, c1, &ipkg->owned_dirs, pkg_dirs_list) {
- apk_blob_push_blob(&bbuf, APK_BLOB_STR("F:"));
- apk_blob_push_blob(&bbuf, APK_BLOB_PTR_LEN(diri->dir->name, diri->dir->namelen));
+ r = apk_ostream_write(os, bbuf.ptr, bbuf.len);
+ if (r < 0) goto err;
+ bbuf = APK_BLOB_BUF(buf);
+
+ hlist_for_each_entry(file, c2, &diri->owned_files, diri_files_list) {
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("R:"));
+ apk_blob_push_blob(&bbuf, APK_BLOB_PTR_LEN(file->name, file->namelen));
apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
- if (diri->acl != apk_default_acl_dir)
- apk_blob_push_db_acl(&bbuf, 'M', diri->acl);
+ if (file->acl != apk_default_acl_file)
+ apk_blob_push_db_acl(&bbuf, 'a', file->acl);
- hlist_for_each_entry(file, c2, &diri->owned_files, diri_files_list) {
- apk_blob_push_blob(&bbuf, APK_BLOB_STR("R:"));
- apk_blob_push_blob(&bbuf, APK_BLOB_PTR_LEN(file->name, file->namelen));
+ if (file->csum.type != APK_CHECKSUM_NONE) {
+ apk_blob_push_blob(&bbuf, APK_BLOB_STR("Z:"));
+ apk_blob_push_csum(&bbuf, &file->csum);
apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
+ }
- if (file->acl != apk_default_acl_file)
- apk_blob_push_db_acl(&bbuf, 'a', file->acl);
-
- if (file->csum.type != APK_CHECKSUM_NONE) {
- apk_blob_push_blob(&bbuf, APK_BLOB_STR("Z:"));
- apk_blob_push_csum(&bbuf, &file->csum);
- apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));
- }
-
- r = apk_ostream_write(os, buf, bbuf.ptr - buf);
- if (r < 0) return r;
- bbuf = APK_BLOB_BUF(buf);
+ bbuf = apk_blob_pushed(APK_BLOB_BUF(buf), bbuf);
+ if (APK_BLOB_IS_NULL(bbuf)) {
+ r = -ENOBUFS;
+ goto err;
}
- r = apk_ostream_write(os, buf, bbuf.ptr - buf);
- if (r < 0) return r;
+ r = apk_ostream_write(os, bbuf.ptr, bbuf.len);
+ if (r < 0) goto err;
bbuf = APK_BLOB_BUF(buf);
}
- r = apk_ostream_write(os, "\n", 1);
- if (r < 0) return r;
}
-
- return 0;
+ r = apk_ostream_write(os, "\n", 1);
+err:
+ if (r < 0) apk_ostream_cancel(os, r);
+ return r;
}
-static int apk_db_scriptdb_write(struct apk_database *db, struct apk_ostream *os)
+static int apk_db_scriptdb_write(struct apk_database *db, struct apk_installed_package *ipkg, struct apk_ostream *os)
{
- struct apk_installed_package *ipkg;
- struct apk_package *pkg;
+ struct apk_package *pkg = ipkg->pkg;
struct apk_file_info fi;
char filename[256];
apk_blob_t bfn;
int r, i;
- time_t now = time(NULL);
- list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
- pkg = ipkg->pkg;
-
- for (i = 0; i < APK_SCRIPT_MAX; i++) {
- if (ipkg->script[i].ptr == NULL)
- continue;
+ if (IS_ERR(os)) return PTR_ERR(os);
- fi = (struct apk_file_info) {
- .name = filename,
- .size = ipkg->script[i].len,
- .mode = 0755 | S_IFREG,
- .mtime = now,
- };
- /* The scripts db expects file names in format:
- * pkg-version.<hexdump of package checksum>.action */
- bfn = APK_BLOB_BUF(filename);
- apk_blob_push_blob(&bfn, APK_BLOB_STR(pkg->name->name));
- apk_blob_push_blob(&bfn, APK_BLOB_STR("-"));
- apk_blob_push_blob(&bfn, *pkg->version);
- apk_blob_push_blob(&bfn, APK_BLOB_STR("."));
- apk_blob_push_csum(&bfn, &pkg->csum);
- apk_blob_push_blob(&bfn, APK_BLOB_STR("."));
- apk_blob_push_blob(&bfn, APK_BLOB_STR(apk_script_types[i]));
- apk_blob_push_blob(&bfn, APK_BLOB_PTR_LEN("", 1));
-
- r = apk_tar_write_entry(os, &fi, ipkg->script[i].ptr);
- if (r < 0)
- return r;
+ for (i = 0; i < APK_SCRIPT_MAX; i++) {
+ if (!ipkg->script[i].ptr) continue;
+
+ fi = (struct apk_file_info) {
+ .name = filename,
+ .size = ipkg->script[i].len,
+ .mode = 0755 | S_IFREG,
+ .mtime = pkg->build_time,
+ };
+ /* The scripts db expects file names in format:
+ * pkg-version.<hexdump of package checksum>.action */
+ bfn = APK_BLOB_BUF(filename);
+ apk_blob_push_blob(&bfn, APK_BLOB_STR(pkg->name->name));
+ apk_blob_push_blob(&bfn, APK_BLOB_STR("-"));
+ apk_blob_push_blob(&bfn, *pkg->version);
+ apk_blob_push_blob(&bfn, APK_BLOB_STR("."));
+ apk_blob_push_csum(&bfn, &pkg->csum);
+ apk_blob_push_blob(&bfn, APK_BLOB_STR("."));
+ apk_blob_push_blob(&bfn, APK_BLOB_STR(apk_script_types[i]));
+ apk_blob_push_blob(&bfn, APK_BLOB_PTR_LEN("", 1));
+
+ r = apk_tar_write_entry(os, &fi, ipkg->script[i].ptr);
+ if (r < 0) {
+ apk_ostream_cancel(os, -APKE_V2DB_FORMAT);
+ break;
}
}
- return apk_tar_write_entry(os, NULL, NULL);
+ return r;
}
static int apk_read_script_archive_entry(void *ctx,
@@ -1096,25 +1165,26 @@ static int parse_triggers(void *ctx, apk_blob_t blob)
return 0;
}
-static void apk_db_triggers_write(struct apk_database *db, struct apk_ostream *os)
+static int apk_db_triggers_write(struct apk_database *db, struct apk_installed_package *ipkg, struct apk_ostream *os)
{
- struct apk_installed_package *ipkg;
char buf[APK_BLOB_CHECKSUM_BUF];
apk_blob_t bfn;
char **trigger;
- list_for_each_entry(ipkg, &db->installed.triggers, trigger_pkgs_list) {
- bfn = APK_BLOB_BUF(buf);
- apk_blob_push_csum(&bfn, &ipkg->pkg->csum);
- bfn = apk_blob_pushed(APK_BLOB_BUF(buf), bfn);
- apk_ostream_write(os, bfn.ptr, bfn.len);
-
- foreach_array_item(trigger, ipkg->triggers) {
- apk_ostream_write(os, " ", 1);
- apk_ostream_write_string(os, *trigger);
- }
- apk_ostream_write(os, "\n", 1);
+ if (IS_ERR(os)) return PTR_ERR(os);
+ if (!ipkg->triggers || ipkg->triggers->num == 0) return 0;
+
+ bfn = APK_BLOB_BUF(buf);
+ apk_blob_push_csum(&bfn, &ipkg->pkg->csum);
+ bfn = apk_blob_pushed(APK_BLOB_BUF(buf), bfn);
+ apk_ostream_write(os, bfn.ptr, bfn.len);
+
+ foreach_array_item(trigger, ipkg->triggers) {
+ apk_ostream_write(os, " ", 1);
+ apk_ostream_write_string(os, *trigger);
}
+ apk_ostream_write(os, "\n", 1);
+ return 0;
}
static int apk_db_triggers_read(struct apk_database *db, struct apk_istream *is)
@@ -1144,107 +1214,75 @@ static int apk_db_triggers_read(struct apk_database *db, struct apk_istream *is)
return apk_istream_close(is);
}
-static int apk_db_read_state(struct apk_database *db, int flags)
+static int apk_db_read_layer(struct apk_database *db, unsigned layer)
{
apk_blob_t blob, world;
- int r;
+ int r, fd, ret = 0, flags = db->ctx->open_flags;
/* Read:
- * 1. /etc/apk/world
+ * 1. world
* 2. installed packages db
* 3. triggers db
* 4. scripts db
*/
+
+ fd = openat(db->root_fd, apk_db_layer_name(layer), O_RDONLY | O_CLOEXEC);
+ if (fd < 0) return -errno;
+
if (!(flags & APK_OPENF_NO_WORLD)) {
- blob = world = apk_blob_from_file(db->root_fd, apk_world_file);
- if (APK_BLOB_IS_NULL(blob)) return -ENOENT;
- blob = apk_blob_trim(blob);
- apk_blob_pull_deps(&blob, db, &db->world);
- free(world.ptr);
+ if (layer == APK_DB_LAYER_ROOT)
+ ret = apk_blob_from_file(db->root_fd, apk_world_file, &world);
+ else
+ ret = apk_blob_from_file(fd, "world", &world);
+
+ if (!ret) {
+ blob = apk_blob_trim(world);
+ apk_blob_pull_deps(&blob, db, &db->world);
+ free(world.ptr);
+ } else if (layer == APK_DB_LAYER_ROOT) {
+ ret = -ENOENT;
+ }
}
if (!(flags & APK_OPENF_NO_INSTALLED)) {
- r = apk_db_index_read(db, apk_istream_from_file(db->root_fd, apk_installed_file), -1);
- if (r && r != -ENOENT) return r;
- r = apk_db_triggers_read(db, apk_istream_from_file(db->root_fd, apk_triggers_file));
- if (r && r != -ENOENT) return r;
+ r = apk_db_fdb_read(db, apk_istream_from_file(fd, "installed"), -1, layer);
+ if (!ret && r != -ENOENT) ret = r;
+ r = apk_db_triggers_read(db, apk_istream_from_file(fd, "triggers"));
+ if (!ret && r != -ENOENT) ret = r;
}
if (!(flags & APK_OPENF_NO_SCRIPTS)) {
- r = apk_tar_parse(apk_istream_from_file(db->root_fd, apk_scripts_file),
+ r = apk_tar_parse(apk_istream_from_file(fd, "scripts.tar"),
apk_read_script_archive_entry, db, db->id_cache);
- if (r && r != -ENOENT) return r;
+ if (!ret && r != -ENOENT) ret = r;
}
- return 0;
-}
-
-struct index_write_ctx {
- struct apk_ostream *os;
- int count;
- int force;
-};
-
-static int write_index_entry(apk_hash_item item, void *ctx)
-{
- struct index_write_ctx *iwctx = (struct index_write_ctx *) ctx;
- struct apk_package *pkg = (struct apk_package *) item;
- int r;
-
- if (!iwctx->force && pkg->filename == NULL)
- return 0;
-
- r = apk_pkg_write_index_entry(pkg, iwctx->os);
- if (r < 0) return r;
-
- r = apk_ostream_write(iwctx->os, "\n", 1);
- if (r < 0) return r;
-
- iwctx->count++;
- return 0;
+ close(fd);
+ return ret;
}
static int apk_db_index_write_nr_cache(struct apk_database *db)
{
- struct index_write_ctx ctx = { NULL, 0, TRUE };
- struct apk_installed_package *ipkg;
+ struct apk_package_array *pkgs;
+ struct apk_package **ppkg;
struct apk_ostream *os;
- int r;
- if (!apk_db_cache_active(db))
- return 0;
+ if (!apk_db_cache_active(db)) return 0;
/* Write list of installed non-repository packages to
* cached index file */
os = apk_ostream_to_file(db->cache_fd, "installed", 0644);
- if (IS_ERR_OR_NULL(os)) return PTR_ERR(os);
+ if (IS_ERR(os)) return PTR_ERR(os);
- ctx.os = os;
- list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
- struct apk_package *pkg = ipkg->pkg;
- if (pkg->repos != BIT(APK_REPOSITORY_CACHED))
- continue;
- r = write_index_entry(pkg, &ctx);
- if (r != 0)
- return r;
+ pkgs = apk_db_sorted_installed_packages(db);
+ foreach_array_item(ppkg, pkgs) {
+ struct apk_package *pkg = *ppkg;
+ if ((pkg->repos == BIT(APK_REPOSITORY_CACHED) ||
+ (pkg->repos == 0 && !pkg->installed_size))) {
+ if (apk_pkg_write_index_entry(pkg, os) < 0) break;
+ }
}
- r = apk_ostream_close(os);
- if (r < 0)
- return r;
-
- return ctx.count;
-}
-
-int apk_db_index_write(struct apk_database *db, struct apk_ostream *os)
-{
- struct index_write_ctx ctx = { os, 0, FALSE };
- int r;
-
- r = apk_hash_foreach(&db->available.packages, write_index_entry, &ctx);
- if (r < 0)
- return r;
-
- return ctx.count;
+ return apk_ostream_close(os);
}
static int add_protected_path(void *ctx, apk_blob_t blob)
@@ -1260,7 +1298,7 @@ static int add_protected_path(void *ctx, apk_blob_t blob)
case '#':
return 0;
case '-':
- protect_mode = APK_PROTECT_NONE;
+ protect_mode = APK_PROTECT_IGNORE;
break;
case '+':
protect_mode = APK_PROTECT_CHANGED;
@@ -1301,54 +1339,21 @@ static int file_ends_with_dot_list(const char *file)
return TRUE;
}
-static int add_protected_paths_from_file(void *ctx, int dirfd, const char *file)
+static int add_protected_paths_from_istream(struct apk_database *db, struct apk_istream *is)
{
- struct apk_database *db = (struct apk_database *) ctx;
- apk_blob_t blob;
-
- if (!file_ends_with_dot_list(file))
- return 0;
-
- blob = apk_blob_from_file(dirfd, file);
- if (APK_BLOB_IS_NULL(blob))
- return 0;
-
- apk_blob_for_each_segment(blob, "\n", add_protected_path, db);
- free(blob.ptr);
-
- return 0;
+ apk_blob_t token = APK_BLOB_STRLIT("\n"), line;
+ if (IS_ERR(is)) return PTR_ERR(is);
+ while (apk_istream_get_delim(is, token, &line) == 0)
+ add_protected_path(db, line);
+ return apk_istream_close(is);
}
-static int apk_db_create(struct apk_database *db)
+static int add_protected_paths_from_file(void *ctx, int dirfd, const char *file)
{
- int fd;
-
- mkdirat(db->root_fd, "tmp", 01777);
- mkdirat(db->root_fd, "dev", 0755);
- mknodat(db->root_fd, "dev/null", S_IFCHR | 0666, makedev(1, 3));
- mknodat(db->root_fd, "dev/zero", S_IFCHR | 0666, makedev(1, 5));
- mknodat(db->root_fd, "dev/random", S_IFCHR | 0666, makedev(1, 8));
- mknodat(db->root_fd, "dev/urandom", S_IFCHR | 0666, makedev(1, 9));
- mknodat(db->root_fd, "dev/console", S_IFCHR | 0600, makedev(5, 1));
- mkdirat(db->root_fd, "etc", 0755);
- mkdirat(db->root_fd, "etc/apk", 0755);
- mkdirat(db->root_fd, "lib", 0755);
- mkdirat(db->root_fd, "lib/apk", 0755);
- mkdirat(db->root_fd, "lib/apk/db", 0755);
- mkdirat(db->root_fd, "var", 0755);
- mkdirat(db->root_fd, "var/cache", 0755);
- mkdirat(db->root_fd, "var/cache/apk", 0755);
- mkdirat(db->root_fd, "var/cache/misc", 0755);
-
- fd = openat(db->root_fd, apk_world_file, O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC, 0644);
- if (fd < 0)
- return -errno;
- close(fd);
- fd = openat(db->root_fd, apk_installed_file, O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC, 0644);
- if (fd < 0)
- return -errno;
- close(fd);
+ struct apk_database *db = (struct apk_database *) ctx;
+ if (!file_ends_with_dot_list(file)) return 0;
+ add_protected_paths_from_istream(db, apk_istream_from_file(dirfd, file));
return 0;
}
@@ -1356,36 +1361,7 @@ static void handle_alarm(int sig)
{
}
-static char *find_mountpoint(int atfd, const char *rel_path)
-{
- struct mntent *me;
- struct stat64 st;
- FILE *f;
- char *ret = NULL;
- dev_t dev;
-
- if (fstatat64(atfd, rel_path, &st, 0) != 0)
- return NULL;
- dev = st.st_dev;
-
- f = setmntent("/proc/mounts", "r");
- if (f == NULL)
- return NULL;
- while ((me = getmntent(f)) != NULL) {
- if (strcmp(me->mnt_fsname, "rootfs") == 0)
- continue;
- if (fstatat64(atfd, me->mnt_dir, &st, 0) == 0 &&
- st.st_dev == dev) {
- ret = strdup(me->mnt_dir);
- break;
- }
- }
- endmntent(f);
-
- return ret;
-}
-
-static void mark_in_cache(struct apk_database *db, int dirfd, const char *name, struct apk_package *pkg)
+static void mark_in_cache(struct apk_database *db, int static_cache, int dirfd, const char *name, struct apk_package *pkg)
{
if (pkg == NULL)
return;
@@ -1405,8 +1381,7 @@ static int add_repos_from_file(void *ctx, int dirfd, const char *file)
return 0;
}
- blob = apk_blob_from_file(dirfd, file);
- if (APK_BLOB_IS_NULL(blob)) {
+ if (apk_blob_from_file(dirfd, file, &blob)) {
if (dirfd != AT_FDCWD) return 0;
apk_err(out, "failed to read repositories: %s", file);
apk_msg(out, "NOTE: --repositories-file is relative to the startup directory since apk 2.12.0_rc2");
@@ -1425,10 +1400,11 @@ static void apk_db_setup_repositories(struct apk_database *db, const char *cache
* are truncated to APK_CACHE_CSUM_BYTES and always use SHA-1. */
db->repos[APK_REPOSITORY_CACHED] = (struct apk_repository) {
.url = cache_dir,
- .csum.data = {
+ .hash.data = {
0xb0,0x35,0x92,0x80,0x6e,0xfa,0xbf,0xee,0xb7,0x09,
0xf5,0xa7,0x0a,0x7c,0x17,0x26,0x69,0xb0,0x05,0x38 },
- .csum.type = APK_CHECKSUM_SHA1,
+ .hash.len = 20,
+ .hash.alg = APK_DIGEST_SHA1,
};
db->num_repos = APK_REPOSITORY_FIRST_CONFIGURED;
@@ -1440,20 +1416,23 @@ static void apk_db_setup_repositories(struct apk_database *db, const char *cache
static int apk_db_name_rdepends(apk_hash_item item, void *pctx)
{
- struct apk_name *name = item, *rname, **n0;
+ struct apk_name *name = item, *rname;
struct apk_provider *p;
struct apk_dependency *dep;
- struct apk_name_array *touched;
- unsigned num_virtual = 0;
+ struct apk_name *touched[128];
+ unsigned num_virtual = 0, num_touched = 0;
- apk_name_array_init(&touched);
foreach_array_item(p, name->providers) {
num_virtual += (p->pkg->name != name);
foreach_array_item(dep, p->pkg->depends) {
rname = dep->name;
- rname->is_dependency |= !dep->conflict;
+ rname->is_dependency |= !apk_dep_conflict(dep);
if (!(rname->state_int & 1)) {
- if (!rname->state_int) *apk_name_array_add(&touched) = rname;
+ if (!rname->state_int) {
+ if (num_touched < ARRAY_SIZE(touched))
+ touched[num_touched] = rname;
+ num_touched++;
+ }
rname->state_int |= 1;
*apk_name_array_add(&rname->rdepends) = name;
}
@@ -1461,7 +1440,11 @@ static int apk_db_name_rdepends(apk_hash_item item, void *pctx)
foreach_array_item(dep, p->pkg->install_if) {
rname = dep->name;
if (!(rname->state_int & 2)) {
- if (!rname->state_int) *apk_name_array_add(&touched) = rname;
+ if (!rname->state_int) {
+ if (num_touched < ARRAY_SIZE(touched))
+ touched[num_touched] = rname;
+ num_touched++;
+ }
rname->state_int |= 2;
*apk_name_array_add(&rname->rinstall_if) = name;
}
@@ -1473,13 +1456,43 @@ static int apk_db_name_rdepends(apk_hash_item item, void *pctx)
name->priority = 1;
else
name->priority = 2;
- foreach_array_item(n0, touched)
- (*n0)->state_int = 0;
- apk_name_array_free(&touched);
+
+ if (num_touched > ARRAY_SIZE(touched)) {
+ foreach_array_item(p, name->providers) {
+ foreach_array_item(dep, p->pkg->depends)
+ dep->name->state_int = 0;
+ foreach_array_item(dep, p->pkg->install_if)
+ dep->name->state_int = 0;
+ }
+ } else for (unsigned i = 0; i < num_touched; i++)
+ touched[i]->state_int = 0;
return 0;
}
+static inline int setup_static_cache(struct apk_database *db, struct apk_ctx *ac)
+{
+ db->cache_dir = apk_static_cache_dir;
+ db->cache_fd = openat(db->root_fd, db->cache_dir, O_RDONLY | O_CLOEXEC);
+ if (db->cache_fd < 0) {
+ apk_make_dirs(db->root_fd, db->cache_dir, 0755, 0755);
+ db->cache_fd = openat(db->root_fd, db->cache_dir, O_RDONLY | O_CLOEXEC);
+ if (db->cache_fd < 0) {
+ if (ac->open_flags & APK_OPENF_WRITE) return -EROFS;
+ db->cache_fd = -APKE_CACHE_NOT_AVAILABLE;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef __linux__
+static int detect_tmpfs_root(struct apk_database *db)
+{
+ struct statfs stfs;
+
+ return fstatfs(db->root_fd, &stfs) == 0 && stfs.f_type == TMPFS_MAGIC;
+}
static unsigned long map_statfs_flags(unsigned long f_flag)
{
@@ -1498,6 +1511,165 @@ static unsigned long map_statfs_flags(unsigned long f_flag)
return mnt_flags;
}
+static char *find_mountpoint(int atfd, const char *rel_path)
+{
+ struct mntent *me;
+ struct stat st;
+ FILE *f;
+ char *ret = NULL;
+ dev_t dev;
+
+ if (fstatat(atfd, rel_path, &st, 0) != 0)
+ return NULL;
+ dev = st.st_dev;
+
+ f = setmntent("/proc/mounts", "r");
+ if (f == NULL)
+ return NULL;
+ while ((me = getmntent(f)) != NULL) {
+ if (strcmp(me->mnt_fsname, "rootfs") == 0)
+ continue;
+ if (fstatat(atfd, me->mnt_dir, &st, 0) == 0 &&
+ st.st_dev == dev) {
+ ret = strdup(me->mnt_dir);
+ break;
+ }
+ }
+ endmntent(f);
+
+ return ret;
+}
+
+static int setup_cache(struct apk_database *db, struct apk_ctx *ac)
+{
+ struct apk_out *out = &ac->out;
+ int fd;
+ struct statfs stfs;
+
+ fd = openat(db->root_fd, ac->cache_dir, O_RDONLY | O_CLOEXEC);
+ if (fd >= 0 && fstatfs(fd, &stfs) == 0) {
+ db->cache_dir = ac->cache_dir;
+ db->cache_fd = fd;
+ db->cache_remount_flags = map_statfs_flags(stfs.f_flags);
+ if ((ac->open_flags & (APK_OPENF_WRITE | APK_OPENF_CACHE_WRITE)) &&
+ (db->cache_remount_flags & MS_RDONLY) != 0) {
+ /* remount cache read/write */
+ db->cache_remount_dir = find_mountpoint(db->root_fd, db->cache_dir);
+ if (db->cache_remount_dir == NULL) {
+ apk_warn(out, "Unable to find cache directory mount point");
+ } else if (mount(0, db->cache_remount_dir, 0, MS_REMOUNT | (db->cache_remount_flags & ~MS_RDONLY), 0) != 0) {
+ free(db->cache_remount_dir);
+ db->cache_remount_dir = NULL;
+ return -EROFS;
+ }
+ }
+ } else {
+ if (fd >= 0) close(fd);
+ if (setup_static_cache(db, ac) < 0) return -EROFS;
+ }
+
+ return 0;
+}
+
+static void remount_cache(struct apk_database *db)
+{
+ if (db->cache_remount_dir) {
+ mount(0, db->cache_remount_dir, 0, MS_REMOUNT | db->cache_remount_flags, 0);
+ free(db->cache_remount_dir);
+ db->cache_remount_dir = NULL;
+ }
+}
+
+static int mount_proc(struct apk_database *db)
+{
+ struct statfs stfs;
+
+ /* mount /proc */
+ if (asprintf(&db->root_proc_dir, "%s/proc", db->ctx->root) == -1)
+ return -1;
+ if (statfs(db->root_proc_dir, &stfs) != 0) {
+ if (errno == ENOENT) mkdir(db->root_proc_dir, 0555);
+ stfs.f_type = 0;
+ }
+ if (stfs.f_type != PROC_SUPER_MAGIC) {
+ mount("proc", db->root_proc_dir, "proc", 0, 0);
+ } else {
+ /* was already mounted. prevent umount on close */
+ free(db->root_proc_dir);
+ db->root_proc_dir = NULL;
+ }
+
+ return 0;
+}
+
+static void unmount_proc(struct apk_database *db)
+{
+ if (db->root_proc_dir) {
+ umount2(db->root_proc_dir, MNT_DETACH|UMOUNT_NOFOLLOW);
+ free(db->root_proc_dir);
+ db->root_proc_dir = NULL;
+ }
+}
+#else
+static int detect_tmpfs_root(struct apk_database *db)
+{
+ (void) db;
+ return 0;
+}
+
+static int setup_cache(struct apk_database *db, struct apk_ctx *ac)
+{
+ return setup_static_cache(db, ac);
+}
+
+static void remount_cache(struct apk_database *db)
+{
+ (void) db;
+}
+
+static int mount_proc(struct apk_database *db)
+{
+ (void) db;
+ return 0;
+}
+
+static void unmount_proc(struct apk_database *db)
+{
+ (void) db;
+}
+#endif
+
+const char *apk_db_layer_name(int layer)
+{
+ switch (layer) {
+ case APK_DB_LAYER_ROOT: return "lib/apk/db";
+ case APK_DB_LAYER_UVOL: return "lib/apk/db-uvol";
+ default:
+ assert(!"invalid layer");
+ return 0;
+ }
+}
+
+#ifdef APK_UVOL_DB_TARGET
+static void setup_uvol_target(struct apk_database *db)
+{
+ const struct apk_ctx *ac = db->ctx;
+ const char *uvol_db = apk_db_layer_name(APK_DB_LAYER_UVOL);
+ const char *uvol_target = APK_UVOL_DB_TARGET;
+ const char *uvol_symlink_target = "../../" APK_UVOL_DB_TARGET;
+
+ if (!(ac->open_flags & (APK_OPENF_WRITE|APK_OPENF_CREATE))) return;
+ if (IS_ERR(ac->uvol)) return;
+ if (faccessat(db->root_fd, uvol_db, F_OK, 0) == 0) return;
+ if (faccessat(db->root_fd, uvol_target, F_OK, 0) != 0) return;
+
+ // Create symlink from uvol_db to uvol_target in relative form
+ symlinkat(uvol_symlink_target, db->root_fd, uvol_db);
+}
+#else
+static void setup_uvol_target(struct apk_database *db) { }
+#endif
+
void apk_db_init(struct apk_database *db)
{
memset(db, 0, sizeof(*db));
@@ -1510,6 +1682,9 @@ void apk_db_init(struct apk_database *db)
list_init(&db->installed.triggers);
apk_dependency_array_init(&db->world);
apk_protected_path_array_init(&db->protected_paths);
+ apk_string_array_init(&db->filename_array);
+ apk_name_array_init(&db->available.sorted_names);
+ apk_package_array_init(&db->installed.sorted_packages);
db->permanent = 1;
db->root_fd = -1;
}
@@ -1518,9 +1693,8 @@ int apk_db_open(struct apk_database *db, struct apk_ctx *ac)
{
struct apk_out *out = &ac->out;
const char *msg = NULL;
- struct statfs stfs;
apk_blob_t blob;
- int r, fd, write_arch = FALSE;
+ int r = -1, i;
apk_default_acl_dir = apk_db_acl_atomize(db, 0755, 0, 0);
apk_default_acl_file = apk_db_acl_atomize(db, 0644, 0, 0);
@@ -1528,7 +1702,6 @@ int apk_db_open(struct apk_database *db, struct apk_ctx *ac)
db->ctx = ac;
if (ac->open_flags == 0) {
msg = "Invalid open flags (internal error)";
- r = -1;
goto ret_r;
}
if ((ac->open_flags & APK_OPENF_WRITE) &&
@@ -1538,119 +1711,83 @@ int apk_db_open(struct apk_database *db, struct apk_ctx *ac)
apk_db_setup_repositories(db, ac->cache_dir);
db->root_fd = apk_ctx_fd_root(ac);
+ db->cache_fd = -APKE_CACHE_NOT_AVAILABLE;
+ db->permanent = !detect_tmpfs_root(db);
+ db->usermode = !!(ac->open_flags & APK_OPENF_USERMODE);
+
+ if (!(ac->open_flags & APK_OPENF_CREATE)) {
+ // Autodetect usermode from the installeddb owner
+ struct stat st;
+ if (fstatat(db->root_fd, apk_db_layer_name(APK_DB_LAYER_ROOT), &st, 0) == 0 &&
+ st.st_uid != 0)
+ db->usermode = 1;
+ }
+ if (db->usermode) db->extract_flags |= APK_FSEXTRACTF_NO_CHOWN | APK_FSEXTRACTF_NO_SYS_XATTRS;
- if (fstatfs(db->root_fd, &stfs) == 0 &&
- stfs.f_type == TMPFS_MAGIC)
- db->permanent = 0;
+ setup_uvol_target(db);
- if (ac->root && ac->arch) {
+ if (ac->arch && (ac->root_set || (ac->open_flags & APK_OPENF_ALLOW_ARCH))) {
db->arch = apk_atomize(&db->atoms, APK_BLOB_STR(ac->arch));
- write_arch = TRUE;
+ db->write_arch = ac->root_set;
} else {
apk_blob_t arch;
- arch = apk_blob_from_file(db->root_fd, apk_arch_file);
- if (!APK_BLOB_IS_NULL(arch)) {
+ if (!apk_blob_from_file(db->root_fd, apk_arch_file, &arch)) {
db->arch = apk_atomize_dup(&db->atoms, apk_blob_trim(arch));
free(arch.ptr);
} else {
db->arch = apk_atomize(&db->atoms, APK_BLOB_STR(APK_DEFAULT_ARCH));
- write_arch = TRUE;
+ db->write_arch = 1;
}
}
db->id_cache = apk_ctx_get_id_cache(ac);
if (ac->open_flags & APK_OPENF_WRITE) {
+ msg = "Unable to lock database";
db->lock_fd = openat(db->root_fd, apk_lock_file,
O_CREAT | O_RDWR | O_CLOEXEC, 0600);
- if (db->lock_fd < 0 && errno == ENOENT &&
- (ac->open_flags & APK_OPENF_CREATE)) {
- r = apk_db_create(db);
- if (r != 0) {
- msg = "Unable to create database";
- goto ret_r;
- }
- db->lock_fd = openat(db->root_fd, apk_lock_file,
- O_CREAT | O_RDWR | O_CLOEXEC, 0600);
- }
- if (db->lock_fd < 0 ||
- flock(db->lock_fd, LOCK_EX | LOCK_NB) < 0) {
- msg = "Unable to lock database";
- if (ac->lock_wait) {
- struct sigaction sa, old_sa;
-
- apk_msg(out, "Waiting for repository lock");
- memset(&sa, 0, sizeof sa);
- sa.sa_handler = handle_alarm;
- sa.sa_flags = SA_ONESHOT;
- sigaction(SIGALRM, &sa, &old_sa);
-
- alarm(ac->lock_wait);
- if (flock(db->lock_fd, LOCK_EX) < 0)
- goto ret_errno;
-
- alarm(0);
- sigaction(SIGALRM, &old_sa, NULL);
- } else
+ if (db->lock_fd < 0) {
+ if (!(ac->open_flags & APK_OPENF_CREATE))
goto ret_errno;
+ } else if (flock(db->lock_fd, LOCK_EX | LOCK_NB) < 0) {
+ struct sigaction sa, old_sa;
+
+ if (!ac->lock_wait) goto ret_errno;
+
+ apk_msg(out, "Waiting for repository lock");
+ memset(&sa, 0, sizeof sa);
+ sa.sa_handler = handle_alarm;
+ sa.sa_flags = SA_RESETHAND;
+ sigaction(SIGALRM, &sa, &old_sa);
+
+ alarm(ac->lock_wait);
+ if (flock(db->lock_fd, LOCK_EX) < 0)
+ goto ret_errno;
+
+ alarm(0);
+ sigaction(SIGALRM, &old_sa, NULL);
}
- if (write_arch)
- apk_blob_to_file(db->root_fd, apk_arch_file, *db->arch, APK_BTF_ADD_EOL);
- /* mount /proc */
- if (asprintf(&db->root_proc_dir, "%s/proc", db->ctx->root) == -1)
+ if (mount_proc(db) < 0)
goto ret_errno;
- if (statfs(db->root_proc_dir, &stfs) != 0) {
- if (errno == ENOENT) mkdir(db->root_proc_dir, 0555);
- stfs.f_type = 0;
- }
- if (stfs.f_type != PROC_SUPER_MAGIC) {
- mount("proc", db->root_proc_dir, "proc", 0, 0);
- } else {
- /* was already mounted. prevent umount on close */
- free(db->root_proc_dir);
- db->root_proc_dir = NULL;
- }
}
- blob = APK_BLOB_STR("+etc\n" "@etc/init.d\n" "!etc/apk\n");
- apk_blob_for_each_segment(blob, "\n", add_protected_path, db);
+ if (ac->protected_paths) {
+ add_protected_paths_from_istream(db, ac->protected_paths);
+ ac->protected_paths = NULL;
+ } else {
+ blob = APK_BLOB_STR("+etc\n" "@etc/init.d\n" "!etc/apk\n");
+ apk_blob_for_each_segment(blob, "\n", add_protected_path, db);
- apk_dir_foreach_file(openat(db->root_fd, "etc/apk/protected_paths.d", O_RDONLY | O_CLOEXEC),
- add_protected_paths_from_file, db);
+ apk_dir_foreach_file(openat(db->root_fd, "etc/apk/protected_paths.d", O_RDONLY | O_CLOEXEC),
+ add_protected_paths_from_file, db);
+ }
/* figure out where to have the cache */
- fd = openat(db->root_fd, ac->cache_dir, O_RDONLY | O_CLOEXEC);
- if (fd >= 0 && fstatfs(fd, &stfs) == 0) {
- db->cache_dir = ac->cache_dir;
- db->cache_fd = fd;
- db->cache_remount_flags = map_statfs_flags(stfs.f_flags);
- if ((ac->open_flags & (APK_OPENF_WRITE | APK_OPENF_CACHE_WRITE)) &&
- (db->cache_remount_flags & MS_RDONLY) != 0) {
- /* remount cache read/write */
- db->cache_remount_dir = find_mountpoint(db->root_fd, db->cache_dir);
- if (db->cache_remount_dir == NULL) {
- apk_warn(out, "Unable to find cache directory mount point");
- } else if (mount(0, db->cache_remount_dir, 0, MS_REMOUNT | (db->cache_remount_flags & ~MS_RDONLY), 0) != 0) {
- free(db->cache_remount_dir);
- db->cache_remount_dir = NULL;
- apk_err(out, "Unable to remount cache read/write");
- r = EROFS;
- goto ret_r;
- }
- }
- } else {
- if (fd >= 0) close(fd);
- db->cache_dir = apk_static_cache_dir;
- db->cache_fd = openat(db->root_fd, db->cache_dir, O_RDONLY | O_CLOEXEC);
- if (db->cache_fd < 0) {
- mkdirat(db->root_fd, "var/cache", 0755);
- mkdirat(db->root_fd, "var/cache/apk", 0755);
- db->cache_fd = openat(db->root_fd, db->cache_dir, O_RDONLY | O_CLOEXEC);
- if (db->cache_fd < 0) {
- if (ac->open_flags & APK_OPENF_WRITE) goto ret_errno;
- db->cache_fd = -APKE_CACHE_NOT_AVAILABLE;
- }
+ if (!(db->ctx->flags & APK_NO_CACHE)) {
+ if ((r = setup_cache(db, ac)) < 0) {
+ apk_err(out, "Unable to setup the cache");
+ goto ret_r;
}
}
@@ -1659,18 +1796,18 @@ int apk_db_open(struct apk_database *db, struct apk_ctx *ac)
apk_db_read_overlay(db, apk_istream_from_fd(STDIN_FILENO));
}
- r = apk_db_read_state(db, ac->open_flags);
- if (r == -ENOENT && (ac->open_flags & APK_OPENF_CREATE)) {
- r = apk_db_create(db);
- if (r != 0) {
- msg = "Unable to create database";
- goto ret_r;
+ if ((db->ctx->open_flags & APK_OPENF_NO_STATE) != APK_OPENF_NO_STATE) {
+ for (i = 0; i < APK_DB_LAYER_NUM; i++) {
+ r = apk_db_read_layer(db, i);
+ if (r) {
+ if (i != APK_DB_LAYER_ROOT) continue;
+ if (!(r == -ENOENT && (ac->open_flags & APK_OPENF_CREATE))) {
+ msg = "Unable to read database";
+ goto ret_r;
+ }
+ }
+ db->active_layers |= BIT(i);
}
- r = apk_db_read_state(db, ac->open_flags);
- }
- if (r != 0) {
- msg = "Unable to read database state";
- goto ret_r;
}
if (!(ac->open_flags & APK_OPENF_NO_INSTALLED_REPO)) {
@@ -1679,12 +1816,13 @@ int apk_db_open(struct apk_database *db, struct apk_ctx *ac)
}
}
- if (!(ac->open_flags & APK_OPENF_NO_SYS_REPOS)) {
+ if (!(ac->open_flags & APK_OPENF_NO_CMDLINE_REPOS)) {
char **repo;
-
foreach_array_item(repo, ac->repository_list)
apk_db_add_repository(db, APK_BLOB_STR(*repo));
+ }
+ if (!(ac->open_flags & APK_OPENF_NO_SYS_REPOS)) {
if (ac->repositories_file == NULL) {
add_repos_from_file(db, db->root_fd, "etc/apk/repositories");
apk_dir_foreach_file(openat(db->root_fd, "etc/apk/repositories.d", O_RDONLY | O_CLOEXEC),
@@ -1693,14 +1831,14 @@ int apk_db_open(struct apk_database *db, struct apk_ctx *ac)
add_repos_from_file(db, AT_FDCWD, ac->repositories_file);
}
- if (db->repo_update_counter)
+ if (db->repositories.updated > 0)
apk_db_index_write_nr_cache(db);
-
- apk_hash_foreach(&db->available.names, apk_db_name_rdepends, db);
}
+ apk_hash_foreach(&db->available.names, apk_db_name_rdepends, db);
+
if (apk_db_cache_active(db) && (ac->open_flags & (APK_OPENF_NO_REPOS|APK_OPENF_NO_INSTALLED)) == 0)
- apk_db_cache_foreach_item(db, mark_in_cache);
+ apk_db_cache_foreach_item(db, mark_in_cache, 0);
db->open_complete = 1;
@@ -1709,6 +1847,11 @@ int apk_db_open(struct apk_database *db, struct apk_ctx *ac)
"This apk-tools is OLD! Some packages %s.",
db->compat_notinstallable ? "are not installable" : "might not function properly");
}
+ if (db->compat_depversions) {
+ apk_warn(out,
+ "The indexes contain broken packages which %s.",
+ db->compat_notinstallable ? "are not installable" : "might not function properly");
+ }
ac->db = db;
return 0;
@@ -1728,48 +1871,109 @@ struct write_ctx {
int fd;
};
+static int apk_db_write_layers(struct apk_database *db)
+{
+ struct layer_data {
+ int fd;
+ struct apk_ostream *installed, *scripts, *triggers;
+ } layers[APK_DB_LAYER_NUM] = {0};
+ struct apk_ostream *os;
+ struct apk_package **ppkg;
+ struct apk_package_array *pkgs;
+ int i, r, rr = 0;
+
+ for (i = 0; i < APK_DB_LAYER_NUM; i++) {
+ struct layer_data *ld = &layers[i];
+ if (!(db->active_layers & BIT(i))) continue;
+
+ ld->fd = openat(db->root_fd, apk_db_layer_name(i), O_RDONLY | O_CLOEXEC);
+ if (ld->fd < 0) {
+ if (i == 0) return -errno;
+ continue;
+ }
+ ld->installed = apk_ostream_to_file(ld->fd, "installed", 0644);
+ ld->scripts = apk_ostream_to_file(ld->fd, "scripts.tar", 0644);
+ ld->triggers = apk_ostream_to_file(ld->fd, "triggers", 0644);
+
+ if (i == 0)
+ os = apk_ostream_to_file(db->root_fd, apk_world_file, 0644);
+ else
+ os = apk_ostream_to_file(ld->fd, "world", 0644);
+ if (IS_ERR(os)) {
+ if (!rr) rr = PTR_ERR(os);
+ continue;
+ }
+ apk_deps_write_layer(db, db->world, os, APK_BLOB_PTR_LEN("\n", 1), i);
+ apk_ostream_write(os, "\n", 1);
+ r = apk_ostream_close(os);
+ if (!rr) rr = r;
+ }
+
+ pkgs = apk_db_sorted_installed_packages(db);
+ foreach_array_item(ppkg, pkgs) {
+ struct apk_package *pkg = *ppkg;
+ struct layer_data *ld = &layers[pkg->layer];
+ if (!ld->fd) continue;
+ apk_db_fdb_write(db, pkg->ipkg, ld->installed);
+ apk_db_scriptdb_write(db, pkg->ipkg, ld->scripts);
+ apk_db_triggers_write(db, pkg->ipkg, ld->triggers);
+ }
+
+ for (i = 0; i < APK_DB_LAYER_NUM; i++) {
+ struct layer_data *ld = &layers[i];
+ if (!(db->active_layers & BIT(i))) continue;
+
+ if (!IS_ERR(ld->installed))
+ r = apk_ostream_close(ld->installed);
+ else r = PTR_ERR(ld->installed);
+ if (!rr) rr = r;
+
+ if (!IS_ERR(ld->scripts)) {
+ apk_tar_write_entry(ld->scripts, NULL, NULL);
+ r = apk_ostream_close(ld->scripts);
+ } else r = PTR_ERR(ld->scripts);
+ if (!rr) rr = r;
+
+ if (!IS_ERR(ld->triggers))
+ r = apk_ostream_close(ld->triggers);
+ else r = PTR_ERR(ld->triggers);
+ if (!rr) rr = r;
+
+ close(ld->fd);
+ }
+ return rr;
+}
+
int apk_db_write_config(struct apk_database *db)
{
struct apk_out *out = &db->ctx->out;
- struct apk_ostream *os;
- int r;
+ int r, rr = 0;
if ((db->ctx->flags & APK_SIMULATE) || db->ctx->root == NULL)
return 0;
- if (db->lock_fd == 0) {
+ if (db->ctx->open_flags & APK_OPENF_CREATE) {
+ apk_make_dirs(db->root_fd, "lib/apk/db", 0755, 0755);
+ apk_make_dirs(db->root_fd, "etc/apk", 0755, 0755);
+ } else if (db->lock_fd == 0) {
apk_err(out, "Refusing to write db without write lock!");
return -1;
}
- os = apk_ostream_to_file(db->root_fd, apk_world_file, 0644);
- if (IS_ERR_OR_NULL(os)) return PTR_ERR(os);
- apk_deps_write(db, db->world, os, APK_BLOB_PTR_LEN("\n", 1));
- apk_ostream_write(os, "\n", 1);
- r = apk_ostream_close(os);
- if (r < 0) return r;
-
- os = apk_ostream_to_file(db->root_fd, apk_installed_file, 0644);
- if (IS_ERR_OR_NULL(os)) return PTR_ERR(os);
- apk_db_write_fdb(db, os);
- r = apk_ostream_close(os);
- if (r < 0) return r;
-
- os = apk_ostream_to_file(db->root_fd, apk_scripts_file, 0644);
- if (IS_ERR_OR_NULL(os)) return PTR_ERR(os);
- apk_db_scriptdb_write(db, os);
- r = apk_ostream_close(os);
- if (r < 0) return r;
+ if (db->write_arch)
+ apk_blob_to_file(db->root_fd, apk_arch_file, *db->arch, APK_BTF_ADD_EOL);
- apk_db_index_write_nr_cache(db);
+ r = apk_db_write_layers(db);
+ if (!rr ) rr = r;
- os = apk_ostream_to_file(db->root_fd, apk_triggers_file, 0644);
- if (IS_ERR_OR_NULL(os)) return PTR_ERR(os);
- apk_db_triggers_write(db, os);
- r = apk_ostream_close(os);
- if (r < 0) return r;
+ r = apk_db_index_write_nr_cache(db);
+ if (r < 0 && !rr) rr = r;
- return 0;
+ if (rr) {
+ apk_err(out, "System state may be inconsistent: failed to write database: %s",
+ apk_error_str(rr));
+ }
+ return rr;
}
void apk_db_close(struct apk_database *db)
@@ -1778,6 +1982,7 @@ void apk_db_close(struct apk_database *db)
struct apk_db_dir_instance *diri;
struct apk_protected_path *ppath;
struct hlist_node *dc, *dn;
+ char **pstr;
int i;
/* Cleaning up the directory tree will cause mode, uid and gid
@@ -1797,25 +2002,22 @@ void apk_db_close(struct apk_database *db)
free(ppath->relative_pattern);
apk_protected_path_array_free(&db->protected_paths);
+ foreach_array_item(pstr, db->filename_array)
+ free(*pstr);
+ apk_string_array_free(&db->filename_array);
+
apk_dependency_array_free(&db->world);
+ apk_name_array_free(&db->available.sorted_names);
+ apk_package_array_free(&db->installed.sorted_packages);
apk_hash_free(&db->available.packages);
apk_hash_free(&db->available.names);
apk_hash_free(&db->installed.files);
apk_hash_free(&db->installed.dirs);
apk_atom_free(&db->atoms);
- if (db->root_proc_dir) {
- umount2(db->root_proc_dir, MNT_DETACH|UMOUNT_NOFOLLOW);
- free(db->root_proc_dir);
- db->root_proc_dir = NULL;
- }
-
- if (db->cache_remount_dir) {
- mount(0, db->cache_remount_dir, 0, MS_REMOUNT | db->cache_remount_flags, 0);
- free(db->cache_remount_dir);
- db->cache_remount_dir = NULL;
- }
+ unmount_proc(db);
+ remount_cache(db);
if (db->cache_fd > 0) close(db->cache_fd);
if (db->lock_fd > 0) close(db->lock_fd);
@@ -1899,10 +2101,11 @@ int apk_db_fire_triggers(struct apk_database *db)
int apk_db_run_script(struct apk_database *db, char *fn, char **argv)
{
+ char buf[APK_EXIT_STATUS_MAX_SIZE];
struct apk_out *out = &db->ctx->out;
int status;
pid_t pid;
- static char * const environment[] = {
+ static char * const clean_environment[] = {
"PATH=/usr/sbin:/usr/bin:/sbin:/bin",
NULL
};
@@ -1925,81 +2128,27 @@ int apk_db_run_script(struct apk_database *db, char *fn, char **argv)
exit(127);
}
- execve(fn, argv, environment);
+ execve(fn, argv, (db->ctx->flags & APK_PRESERVE_ENV) ? environ : clean_environment);
exit(127); /* should not get here */
}
- waitpid(pid, &status, 0);
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- apk_err(out, "%s: script exited with error %d", basename(fn), WEXITSTATUS(status));
+ while (waitpid(pid, &status, 0) < 0 && errno == EINTR);
+
+ if (apk_exit_status_str(status, buf, sizeof buf)) {
+ apk_err(out, "%s: script %s", basename(fn), buf);
return -1;
}
return 0;
}
-struct update_permissions_ctx {
- struct apk_database *db;
- unsigned int errors;
-};
-
-static int update_permissions(apk_hash_item item, void *pctx)
-{
- struct update_permissions_ctx *ctx = pctx;
- struct apk_database *db = ctx->db;
- struct apk_db_dir *dir = (struct apk_db_dir *) item;
- struct stat st;
- int r;
-
- if (dir->refs == 0) return 0;
- if (!dir->update_permissions) return 0;
- dir->seen = 0;
-
- r = fstatat(db->root_fd, dir->name, &st, AT_SYMLINK_NOFOLLOW);
- if (r < 0 || (st.st_mode & 07777) != (dir->mode & 07777))
- if (fchmodat(db->root_fd, dir->name, dir->mode, 0) < 0)
- ctx->errors++;
- if (r < 0 || st.st_uid != dir->uid || st.st_gid != dir->gid)
- if (fchownat(db->root_fd, dir->name, dir->uid, dir->gid, 0) < 0)
- ctx->errors++;
-
- return 0;
-}
-
-void apk_db_update_directory_permissions(struct apk_database *db)
-{
- struct apk_out *out = &db->ctx->out;
- struct apk_installed_package *ipkg;
- struct apk_db_dir_instance *diri;
- struct apk_db_dir *dir;
- struct hlist_node *dc, *dn;
- struct update_permissions_ctx ctx = {
- .db = db,
- };
-
- list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) {
- hlist_for_each_entry_safe(diri, dc, dn, &ipkg->owned_dirs, pkg_dirs_list) {
- dir = diri->dir;
- if (!dir->update_permissions) continue;
- if (!dir->seen) {
- dir->seen = 1;
- dir->mode = 0;
- dir->uid = (uid_t) -1;
- dir->gid = (gid_t) -1;
- }
- apk_db_dir_apply_diri_permissions(diri);
- }
- }
- apk_hash_foreach(&db->installed.dirs, update_permissions, &ctx);
- if (ctx.errors) apk_err(out, "%d errors updating directory permissions", ctx.errors);
-}
-
int apk_db_cache_active(struct apk_database *db)
{
- return db->ctx->cache_dir != apk_static_cache_dir;
+ return db->cache_fd > 0 && db->cache_dir != apk_static_cache_dir;
}
struct foreach_cache_item_ctx {
struct apk_database *db;
apk_cache_item_cb cb;
+ int static_cache;
};
static int foreach_cache_file(void *pctx, int dirfd, const char *name)
@@ -2029,15 +2178,29 @@ static int foreach_cache_file(void *pctx, int dirfd, const char *name)
}
}
no_pkg:
- ctx->cb(db, dirfd, name, pkg);
+ ctx->cb(db, ctx->static_cache, dirfd, name, pkg);
return 0;
}
-int apk_db_cache_foreach_item(struct apk_database *db, apk_cache_item_cb cb)
+int apk_db_cache_foreach_item(struct apk_database *db, apk_cache_item_cb cb, int static_cache)
{
- struct foreach_cache_item_ctx ctx = { db, cb };
+ struct foreach_cache_item_ctx ctx = { db, cb, static_cache };
+ if (static_cache) {
+ struct stat st1, st2;
+ int fd = openat(db->root_fd, apk_static_cache_dir, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) return fd;
+ /* Do not handle static cache as static cache if the explicit
+ * cache is enabled at the static cache location */
+ if (fstat(fd, &st1) == 0 && fstat(db->cache_fd, &st2) == 0 &&
+ st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino) {
+ close(fd);
+ return 0;
+ }
+ return apk_dir_foreach_file(fd, foreach_cache_file, &ctx);
+ }
+ if (db->cache_fd < 0) return db->cache_fd;
return apk_dir_foreach_file(dup(db->cache_fd), foreach_cache_file, &ctx);
}
@@ -2133,88 +2296,75 @@ struct apk_repository *apk_db_select_repo(struct apk_database *db,
return &db->repos[APK_REPOSITORY_CACHED];
}
-static int apk_repository_update(struct apk_database *db, struct apk_repository *repo)
-{
- struct apk_out *out = &db->ctx->out;
- struct apk_url_print urlp;
- int r, verify = (db->ctx->flags & APK_ALLOW_UNTRUSTED) ? APK_SIGN_NONE : APK_SIGN_VERIFY;
-
- r = apk_cache_download(db, repo, NULL, verify, 1, NULL, NULL);
- if (r == -EALREADY) return 0;
- if (r != 0) {
- apk_url_parse(&urlp, repo->url);
- apk_err(out, URL_FMT ": %s", URL_PRINTF(urlp), apk_error_str(r));
- db->repo_update_errors++;
- } else {
- db->repo_update_counter++;
- }
-
- return r;
-}
-
struct apkindex_ctx {
struct apk_database *db;
- struct apk_sign_ctx sctx;
+ struct apk_extract_ctx ectx;
int repo, found;
};
-static int load_apkindex(void *sctx, const struct apk_file_info *fi,
- struct apk_istream *is)
+static int load_v2index(struct apk_extract_ctx *ectx, apk_blob_t *desc, struct apk_istream *is)
{
- struct apkindex_ctx *ctx = (struct apkindex_ctx *) sctx;
- struct apk_repository *repo;
- int r;
-
- r = apk_sign_ctx_process_file(&ctx->sctx, fi, is);
- if (r <= 0)
- return r;
-
- r = 0;
- repo = &ctx->db->repos[ctx->repo];
-
- if (strcmp(fi->name, "DESCRIPTION") == 0) {
- repo->description = apk_blob_from_istream(is, fi->size);
- } else if (strcmp(fi->name, "APKINDEX") == 0) {
- ctx->found = 1;
- r = apk_db_index_read(ctx->db, is, ctx->repo);
- }
+ struct apkindex_ctx *ctx = container_of(ectx, struct apkindex_ctx, ectx);
+ struct apk_repository *repo = &ctx->db->repos[ctx->repo];
- return r;
+ repo->description = *desc;
+ *desc = APK_BLOB_NULL;
+ return apk_db_index_read(ctx->db, is, ctx->repo);
}
-static int load_index(struct apk_database *db, struct apk_istream *is,
- int targz, int repo)
+static int load_v3index(struct apk_extract_ctx *ectx, struct adb_obj *ndx)
{
- int r = 0;
+ struct apkindex_ctx *ctx = container_of(ectx, struct apkindex_ctx, ectx);
+ struct apk_database *db = ctx->db;
+ struct apk_repository *repo = &db->repos[ctx->repo];
+ struct apk_package *pkg;
+ struct adb_obj pkgs, pkginfo;
+ int i;
- if (IS_ERR_OR_NULL(is)) return is ? PTR_ERR(is) : -EINVAL;
+ repo->description = apk_blob_dup(adb_ro_blob(ndx, ADBI_NDX_DESCRIPTION));
+ adb_ro_obj(ndx, ADBI_NDX_PACKAGES, &pkgs);
- if (targz) {
- struct apkindex_ctx ctx;
+ for (i = ADBI_FIRST; i <= adb_ra_num(&pkgs); i++) {
+ adb_ro_obj(&pkgs, i, &pkginfo);
+ pkg = apk_pkg_new();
+ if (!pkg) return -ENOMEM;
+ apk_pkg_from_adb(db, pkg, &pkginfo);
+ pkg->repos |= BIT(ctx->repo);
+ if (!apk_db_pkg_add(db, pkg)) return -APKE_ADB_SCHEMA;
+ }
- ctx.db = db;
- ctx.repo = repo;
- ctx.found = 0;
- apk_sign_ctx_init(&ctx.sctx, APK_SIGN_VERIFY, NULL, apk_ctx_get_trust(db->ctx));
- r = apk_tar_parse(apk_istream_gunzip_mpart(is, apk_sign_ctx_mpart_cb, &ctx.sctx), load_apkindex, &ctx, db->id_cache);
- apk_sign_ctx_free(&ctx.sctx);
+ return 0;
+}
- if (r >= 0 && ctx.found == 0)
- r = -APKE_V2NDX_FORMAT;
- } else {
- apk_db_index_read(db, apk_istream_gunzip(is), repo);
- }
- return r;
+static const struct apk_extract_ops extract_index = {
+ .v2index = load_v2index,
+ .v3index = load_v3index,
+};
+
+static int load_index(struct apk_database *db, struct apk_istream *is, int repo)
+{
+ struct apkindex_ctx ctx = {
+ .db = db,
+ .repo = repo,
+ };
+ if (IS_ERR(is)) return PTR_ERR(is);
+ apk_extract_init(&ctx.ectx, db->ctx, &extract_index);
+ return apk_extract(&ctx.ectx, is);
}
int apk_db_index_read_file(struct apk_database *db, const char *file, int repo)
{
- int targz = 1;
-
- if (strstr(file, ".tar.gz") == NULL && strstr(file, ".gz") != NULL)
- targz = 0;
+ return load_index(db, apk_istream_from_file(AT_FDCWD, file), repo);
+}
- return load_index(db, apk_istream_from_file(AT_FDCWD, file), targz, repo);
+int apk_db_repository_check(struct apk_database *db)
+{
+ if (db->ctx->force & APK_FORCE_MISSING_REPOSITORIES) return 0;
+ if (!db->repositories.stale && !db->repositories.unavailable) return 0;
+ apk_err(&db->ctx->out,
+ "Not continuing due to stale/unavailable repositories."
+ "Use --force-missing-repositories to continue.");
+ return -1;
}
int apk_db_add_repository(apk_database_t _db, apk_blob_t _repository)
@@ -2224,8 +2374,9 @@ int apk_db_add_repository(apk_database_t _db, apk_blob_t _repository)
struct apk_repository *repo;
struct apk_url_print urlp;
apk_blob_t brepo, btag;
- int repo_num, r, targz = 1, tag_id = 0;
+ int repo_num, r, tag_id = 0, atfd = AT_FDCWD, update_error = 0;
char buf[PATH_MAX], *url;
+ const char *error_action = "constructing url";
brepo = _repository;
btag = APK_BLOB_NULL;
@@ -2233,8 +2384,8 @@ int apk_db_add_repository(apk_database_t _db, apk_blob_t _repository)
return 0;
if (brepo.ptr[0] == '@') {
- apk_blob_cspn(brepo, apk_spn_repo_separators, &btag, &brepo);
- apk_blob_spn(brepo, apk_spn_repo_separators, NULL, &brepo);
+ apk_blob_cspn(brepo, APK_CTYPE_REPOSITORY_SEPARATOR, &btag, &brepo);
+ apk_blob_spn(brepo, APK_CTYPE_REPOSITORY_SEPARATOR, NULL, &brepo);
tag_id = apk_db_get_tag_id(db, btag);
}
@@ -2259,32 +2410,60 @@ int apk_db_add_repository(apk_database_t _db, apk_blob_t _repository)
.url = url,
};
- apk_blob_checksum(brepo, apk_checksum_default(), &repo->csum);
+ int is_remote = (apk_url_local_file(repo->url) == NULL);
- if (apk_url_local_file(repo->url) == NULL) {
+ r = apk_repo_format_real_url(db->arch, repo, NULL, buf, sizeof(buf), &urlp);
+ if (r != 0) goto err;
+
+ error_action = "opening";
+ apk_digest_calc(&repo->hash, APK_DIGEST_SHA256, buf, strlen(buf));
+
+ if (is_remote) {
if (!(db->ctx->flags & APK_NO_NETWORK))
db->available_repos |= BIT(repo_num);
if (db->ctx->flags & APK_NO_CACHE) {
- r = apk_repo_format_real_url(db->arch, repo, NULL, buf, sizeof(buf), &urlp);
- if (r == 0) apk_msg(out, "fetch " URL_FMT, URL_PRINTF(urlp));
+ error_action = "fetching";
+ apk_msg(out, "fetch " URL_FMT, URL_PRINTF(urlp));
} else {
- if (db->autoupdate) apk_repository_update(db, repo);
+ error_action = "opening from cache";
+ if (db->autoupdate) {
+ update_error = apk_cache_download(db, repo, NULL, 1, NULL, NULL);
+ switch (update_error) {
+ case 0:
+ db->repositories.updated++;
+ break;
+ case -EALREADY:
+ update_error = 0;
+ break;
+ }
+ }
r = apk_repo_format_cache_index(APK_BLOB_BUF(buf), repo);
+ if (r != 0) goto err;
+ atfd = db->cache_fd;
}
} else {
db->local_repos |= BIT(repo_num);
db->available_repos |= BIT(repo_num);
- r = apk_repo_format_real_url(db->arch, repo, NULL, buf, sizeof(buf), &urlp);
}
- if (r == 0) {
- r = load_index(db, apk_istream_from_fd_url(db->cache_fd, buf, apk_db_url_since(db, 0)), targz, repo_num);
+ r = load_index(db, apk_istream_from_fd_url(atfd, buf, apk_db_url_since(db, 0)), repo_num);
+
+err:
+ if (r || update_error) {
+ if (is_remote) {
+ if (r) db->repositories.unavailable++;
+ else db->repositories.stale++;
+ }
+ apk_url_parse(&urlp, repo->url);
+ if (update_error)
+ error_action = r ? "updating and opening" : "updating";
+ else
+ update_error = r;
+ apk_warn(out, "%s " URL_FMT ": %s", error_action, URL_PRINTF(urlp),
+ apk_error_str(update_error));
}
if (r != 0) {
- apk_url_parse(&urlp, repo->url);
- apk_warn(out, "Ignoring " URL_FMT ": %s", URL_PRINTF(urlp), apk_error_str(r));
db->available_repos &= ~BIT(repo_num);
- r = 0;
} else {
db->repo_tags[tag_id].allowed_repos |= BIT(repo_num);
}
@@ -2302,10 +2481,9 @@ static void extract_cb(void *_ctx, size_t bytes_done)
static void apk_db_run_pending_script(struct install_ctx *ctx)
{
- if (ctx->script_pending && ctx->sctx.control_verified) {
- ctx->script_pending = FALSE;
- apk_ipkg_run_script(ctx->ipkg, ctx->db, ctx->script, ctx->script_args);
- }
+ if (!ctx->script_pending) return;
+ ctx->script_pending = FALSE;
+ apk_ipkg_run_script(ctx->ipkg, ctx->db, ctx->script, ctx->script_args);
}
static int read_info_line(void *_ctx, apk_blob_t line)
@@ -2334,7 +2512,7 @@ static int read_info_line(void *_ctx, apk_blob_t line)
list_add_tail(&ipkg->trigger_pkgs_list,
&db->installed.triggers);
} else {
- apk_sign_ctx_parse_pkginfo_line(&ctx->sctx, line);
+ apk_extract_v2_control(&ctx->ectx, l, r);
}
return 0;
}
@@ -2354,90 +2532,107 @@ static struct apk_db_dir_instance *apk_db_install_directory_entry(struct install
return diri;
}
-#define TMPNAME_MAX (PATH_MAX + 64)
+static int contains_control_character(const char *str)
+{
+ for (const uint8_t *p = (const uint8_t *) str; *p; p++) {
+ if (*p < 0x20 || *p == 0x7f) return 1;
+ }
+ return 0;
+}
-static const char *format_tmpname(struct apk_package *pkg, struct apk_db_file *f, char tmpname[static TMPNAME_MAX])
+static int need_checksum(mode_t mode)
{
- struct apk_digest_ctx dctx;
- struct apk_digest d;
- apk_blob_t b = APK_BLOB_PTR_LEN(tmpname, TMPNAME_MAX);
+ switch (mode & S_IFMT) {
+ case S_IFSOCK:
+ case S_IFBLK:
+ case S_IFCHR:
+ case S_IFIFO:
+ return FALSE;
+ default:
+ return TRUE;
+ }
+}
- if (!f) return NULL;
+static int apk_db_install_v2meta(struct apk_extract_ctx *ectx, struct apk_istream *is)
+{
+ struct install_ctx *ctx = container_of(ectx, struct install_ctx, ectx);
+ apk_blob_t l, token = APK_BLOB_STR("\n");
+ int r;
- if (apk_digest_ctx_init(&dctx, APK_DIGEST_SHA256) != 0) return NULL;
+ while (apk_istream_get_delim(is, token, &l) == 0) {
+ r = read_info_line(ctx, l);
+ if (r < 0) return r;
+ }
- apk_digest_ctx_update(&dctx, pkg->name->name, strlen(pkg->name->name) + 1);
- apk_digest_ctx_update(&dctx, f->diri->dir->name, f->diri->dir->namelen);
- apk_digest_ctx_update(&dctx, "/", 1);
- apk_digest_ctx_update(&dctx, f->name, f->namelen);
- apk_digest_ctx_final(&dctx, &d);
- apk_digest_ctx_free(&dctx);
+ return 0;
+}
- apk_blob_push_blob(&b, APK_BLOB_PTR_LEN(f->diri->dir->name, f->diri->dir->namelen));
- if (f->diri->dir->namelen > 0) {
- apk_blob_push_blob(&b, APK_BLOB_STR("/.apk."));
- } else {
- apk_blob_push_blob(&b, APK_BLOB_STR(".apk."));
+static int apk_db_install_v3meta(struct apk_extract_ctx *ectx, struct adb_obj *pkg)
+{
+ static const int script_type_to_field[] = {
+ [APK_SCRIPT_PRE_INSTALL] = ADBI_SCRPT_PREINST,
+ [APK_SCRIPT_POST_INSTALL] = ADBI_SCRPT_POSTINST,
+ [APK_SCRIPT_PRE_DEINSTALL] = ADBI_SCRPT_PREDEINST,
+ [APK_SCRIPT_POST_DEINSTALL] = ADBI_SCRPT_POSTDEINST,
+ [APK_SCRIPT_PRE_UPGRADE] = ADBI_SCRPT_PREUPGRADE,
+ [APK_SCRIPT_POST_UPGRADE] = ADBI_SCRPT_POSTUPGRADE,
+ [APK_SCRIPT_TRIGGER] = ADBI_SCRPT_TRIGGER,
+ };
+ struct install_ctx *ctx = container_of(ectx, struct install_ctx, ectx);
+ struct apk_database *db = ctx->db;
+ struct apk_installed_package *ipkg = ctx->ipkg;
+ struct adb_obj scripts, triggers, pkginfo, obj;
+ int i;
+
+ // Extract the information not available in index
+ adb_ro_obj(pkg, ADBI_PKG_PKGINFO, &pkginfo);
+ apk_deps_from_adb(&ipkg->replaces, db, adb_ro_obj(&pkginfo, ADBI_PI_REPLACES, &obj));
+ ipkg->replaces_priority = adb_ro_int(pkg, ADBI_PKG_REPLACES_PRIORITY);
+ ipkg->v3 = 1;
+
+ adb_ro_obj(pkg, ADBI_PKG_SCRIPTS, &scripts);
+ for (i = 0; i < ARRAY_SIZE(script_type_to_field); i++) {
+ apk_blob_t b = adb_ro_blob(&scripts, script_type_to_field[i]);
+ if (APK_BLOB_IS_NULL(b)) continue;
+ apk_ipkg_assign_script(ipkg, i, apk_blob_dup(b));
+ ctx->script_pending |= (i == ctx->script);
}
- apk_blob_push_hexdump(&b, APK_BLOB_PTR_LEN((char *)d.data, 24));
- apk_blob_push_blob(&b, APK_BLOB_PTR_LEN("", 1));
- return tmpname;
+ apk_string_array_resize(&ipkg->triggers, 0);
+ adb_ro_obj(pkg, ADBI_PKG_TRIGGERS, &triggers);
+ for (i = ADBI_FIRST; i <= adb_ra_num(&triggers); i++)
+ *apk_string_array_add(&ipkg->triggers) = apk_blob_cstr(adb_ro_blob(&triggers, i));
+ if (ctx->ipkg->triggers->num != 0 && !list_hashed(&ipkg->trigger_pkgs_list))
+ list_add_tail(&ipkg->trigger_pkgs_list, &db->installed.triggers);
+
+ return 0;
}
-static int contains_control_character(const char *str)
+static int apk_db_install_script(struct apk_extract_ctx *ectx, unsigned int type, size_t size, struct apk_istream *is)
{
- for (const uint8_t *p = (const uint8_t *) str; *p; p++) {
- if (*p < 0x20 || *p == 0x7f) return 1;
- }
+ struct install_ctx *ctx = container_of(ectx, struct install_ctx, ectx);
+ struct apk_package *pkg = ctx->pkg;
+ apk_ipkg_add_script(pkg->ipkg, is, type, size);
+ ctx->script_pending |= (type == ctx->script);
return 0;
}
-static int apk_db_install_archive_entry(void *_ctx,
- const struct apk_file_info *ae,
- struct apk_istream *is)
+static int apk_db_install_file(struct apk_extract_ctx *ectx, const struct apk_file_info *ae, struct apk_istream *is)
{
+ struct install_ctx *ctx = container_of(ectx, struct install_ctx, ectx);
static const char dot1[] = "/./", dot2[] = "/../";
- struct install_ctx *ctx = (struct install_ctx *) _ctx;
struct apk_database *db = ctx->db;
- struct apk_out *out = &db->ctx->out;
+ struct apk_ctx *ac = db->ctx;
+ struct apk_out *out = &ac->out;
struct apk_package *pkg = ctx->pkg, *opkg;
- struct apk_dependency *dep;
struct apk_installed_package *ipkg = pkg->ipkg;
apk_blob_t name = APK_BLOB_STR(ae->name), bdir, bfile;
struct apk_db_dir_instance *diri = ctx->diri;
struct apk_db_file *file, *link_target_file = NULL;
int ret = 0, r;
- char tmpname_file[TMPNAME_MAX], tmpname_link_target[TMPNAME_MAX];
-
- r = apk_sign_ctx_process_file(&ctx->sctx, ae, is);
- if (r <= 0)
- return r;
- /* Package metainfo and script processing */
- if (ctx->sctx.control_started && !ctx->sctx.data_started) {
- if (ae->name[0] != '.') return 0;
- if (strcmp(ae->name, ".PKGINFO") == 0) {
- apk_blob_t l, token = APK_BLOB_STR("\n");
- while (apk_istream_get_delim(is, token, &l) == 0)
- read_info_line(ctx, l);
- return 0;
- }
- r = apk_script_type(&ae->name[1]);
- if (r != APK_SCRIPT_INVALID) {
- apk_ipkg_add_script(ipkg, is, r, ae->size);
- ctx->script_pending |= (r == ctx->script);
- apk_db_run_pending_script(ctx);
- }
- return 0;
- }
-
- /* Handle script */
apk_db_run_pending_script(ctx);
-
- /* Rest of files need to be inside data portion */
- if (!ctx->sctx.data_started || ae->name[0] == '.')
- return 0;
+ if (ae->name[0] == '.') return 0;
/* Sanity check the file name */
if (ae->name[0] == '/' || contains_control_character(ae->name) ||
@@ -2458,9 +2653,6 @@ static int apk_db_install_archive_entry(void *_ctx,
bfile = name;
}
- if (bfile.len > 6 && memcmp(bfile.ptr, ".keep_", 6) == 0)
- return 0;
-
/* Make sure the file is part of the cached directory tree */
diri = ctx->diri = find_diri(ipkg, bdir, diri, &ctx->file_diri_node);
if (diri == NULL) {
@@ -2511,50 +2703,21 @@ static int apk_db_install_archive_entry(void *_ctx,
file = apk_db_file_query(db, bdir, bfile);
if (file != NULL) {
opkg = file->diri->pkg;
- do {
- int opkg_prio = -1, pkg_prio = -1;
-
- /* Overlay file? */
- if (opkg->name == NULL)
- break;
- /* Upgrading package? */
- if (opkg->name == pkg->name)
- break;
- /* Or same source package? */
- if (opkg->origin == pkg->origin && pkg->origin)
- break;
- /* Does the original package replace the new one? */
- foreach_array_item(dep, opkg->ipkg->replaces) {
- if (apk_dep_is_materialized(dep, pkg)) {
- opkg_prio = opkg->ipkg->replaces_priority;
- break;
- }
- }
- /* Does the new package replace the original one? */
- foreach_array_item(dep, ctx->ipkg->replaces) {
- if (apk_dep_is_materialized(dep, opkg)) {
- pkg_prio = ctx->ipkg->replaces_priority;
- break;
- }
- }
- /* If the original package is more important,
- * skip this file */
- if (opkg_prio > pkg_prio)
- return 0;
- /* If the new package has valid 'replaces', we
- * will overwrite the file without warnings. */
- if (pkg_prio >= 0)
- break;
-
- if (!(db->ctx->force & APK_FORCE_OVERWRITE)) {
- apk_err(out, PKG_VER_FMT": trying to overwrite %s owned by "PKG_VER_FMT".",
+ switch (apk_pkg_replaces_file(opkg, pkg)) {
+ case APK_PKG_REPLACES_CONFLICT:
+ if (db->ctx->force & APK_FORCE_OVERWRITE) {
+ apk_warn(out, PKG_VER_FMT": overwriting %s owned by "PKG_VER_FMT".",
PKG_VER_PRINTF(pkg), ae->name, PKG_VER_PRINTF(opkg));
- ipkg->broken_files = 1;
- return 0;
+ break;
}
- apk_warn(out, PKG_VER_FMT": overwriting %s owned by "PKG_VER_FMT".",
+ apk_err(out, PKG_VER_FMT": trying to overwrite %s owned by "PKG_VER_FMT".",
PKG_VER_PRINTF(pkg), ae->name, PKG_VER_PRINTF(opkg));
- } while (0);
+ ipkg->broken_files = 1;
+ case APK_PKG_REPLACES_NO:
+ return 0;
+ case APK_PKG_REPLACES_YES:
+ break;
+ }
}
if (opkg != pkg) {
@@ -2566,27 +2729,41 @@ static int apk_db_install_archive_entry(void *_ctx,
/* Extract the file with temporary name */
file->acl = apk_db_acl_atomize_digest(db, ae->mode, ae->uid, ae->gid, &ae->xattr_digest);
- r = apk_archive_entry_extract(
- db->root_fd, ae,
- format_tmpname(pkg, file, tmpname_file),
- format_tmpname(pkg, link_target_file, tmpname_link_target),
- is, extract_cb, ctx, 0, db->extract_flags, out);
-
+ r = apk_fs_extract(ac, ae, is, extract_cb, ctx, db->extract_flags, apk_pkg_ctx(pkg));
switch (r) {
case 0:
- /* Hardlinks need special care for checksum */
+ // Hardlinks need special care for checksum
if (link_target_file)
memcpy(&file->csum, &link_target_file->csum, sizeof file->csum);
else
apk_checksum_from_digest(&file->csum, &ae->digest);
- /* only warn once per package */
- if (file->csum.type == APK_CHECKSUM_NONE && !ctx->missing_checksum) {
- apk_warn(out,
- PKG_VER_FMT": support for packages without embedded "
- "checksums will be dropped in apk-tools 3.",
- PKG_VER_PRINTF(pkg));
- ipkg->broken_files = 1;
- ctx->missing_checksum = 1;
+
+ if (ipkg->v3 && S_ISLNK(ae->mode)) {
+ struct apk_digest d;
+ apk_digest_calc(&d, APK_DIGEST_SHA256,
+ ae->link_target, strlen(ae->link_target));
+ ipkg->sha256_160 = 1;
+ file->csum.type = APK_CHECKSUM_SHA1;
+ memcpy(file->csum.data, d.data, file->csum.type);
+ } else if (file->csum.type == APK_CHECKSUM_NONE && ae->digest.alg == APK_DIGEST_SHA256) {
+ ipkg->sha256_160 = 1;
+ file->csum.type = APK_CHECKSUM_SHA1;
+ memcpy(file->csum.data, ae->digest.data, file->csum.type);
+ } else if (link_target_file == NULL && need_checksum(ae->mode) && !ctx->missing_checksum) {
+ if (ae->digest.alg == APK_DIGEST_NONE) {
+ apk_warn(out,
+ PKG_VER_FMT": support for packages without embedded "
+ "checksums will be dropped in apk-tools 3.",
+ PKG_VER_PRINTF(pkg));
+ ipkg->broken_files = 1;
+ ctx->missing_checksum = 1;
+ } else if (file->csum.type == APK_CHECKSUM_NONE) {
+ apk_warn(out,
+ PKG_VER_FMT": unknown v3 checksum",
+ PKG_VER_PRINTF(pkg));
+ ipkg->broken_files = 1;
+ ctx->missing_checksum = 1;
+ }
}
break;
case -ENOTSUP:
@@ -2594,28 +2771,50 @@ static int apk_db_install_archive_entry(void *_ctx,
break;
case -ENOSPC:
ret = r;
+ case -APKE_UVOL_ROOT:
+ case -APKE_UVOL_NOT_AVAILABLE:
default:
ipkg->broken_files = 1;
break;
}
} else {
- apk_dbg2(out, "%s (dir)", ae->name);
+ struct apk_db_acl *expected_acl;
- if (name.ptr[name.len-1] == '/')
- name.len--;
+ apk_dbg2(out, "%s (dir)", ae->name);
+ if (name.ptr[name.len-1] == '/') name.len--;
diri = ctx->diri = find_diri(ipkg, name, NULL, &ctx->file_diri_node);
- if (!diri) {
- diri = apk_db_install_directory_entry(ctx, name);
- apk_db_dir_prepare(db, diri->dir, ae->mode);
- }
- apk_db_diri_set(diri, apk_db_acl_atomize_digest(db, ae->mode, ae->uid, ae->gid, &ae->xattr_digest));
+ if (!diri) diri = apk_db_install_directory_entry(ctx, name);
+ diri->acl = apk_db_acl_atomize_digest(db, ae->mode, ae->uid, ae->gid, &ae->xattr_digest);
+ expected_acl = diri->dir->owner ? diri->dir->owner->acl : NULL;
+ apk_db_dir_apply_diri_permissions(db, diri);
+ apk_db_dir_prepare(db, diri->dir, expected_acl, diri->dir->owner->acl);
+
}
ctx->installed_size += ctx->current_file_size;
return ret;
}
+static const struct apk_extract_ops extract_installer = {
+ .v2meta = apk_db_install_v2meta,
+ .v3meta = apk_db_install_v3meta,
+ .script = apk_db_install_script,
+ .file = apk_db_install_file,
+};
+
+static int apk_db_audit_file(struct apk_fsdir *d, apk_blob_t filename, struct apk_db_file *dbf)
+{
+ struct apk_file_info fi;
+ int r;
+
+ // Check file first
+ r = apk_fsdir_file_info(d, filename, APK_FI_NOFOLLOW | APK_FI_DIGEST(apk_dbf_digest(dbf)), &fi);
+ if (r != 0 || !dbf || dbf->csum.type == APK_CHECKSUM_NONE) return r != -ENOENT;
+ if (apk_digest_cmp_csum(&fi.digest, &dbf->csum) != 0) return 1;
+ return 0;
+}
+
static void apk_db_purge_pkg(struct apk_database *db,
struct apk_installed_package *ipkg,
int is_installed)
@@ -2624,32 +2823,29 @@ static void apk_db_purge_pkg(struct apk_database *db,
struct apk_db_dir_instance *diri;
struct apk_db_file *file;
struct apk_db_file_hash_key key;
- struct apk_file_info fi;
+ struct apk_fsdir d;
struct hlist_node *dc, *dn, *fc, *fn;
unsigned long hash;
- char name[TMPNAME_MAX];
+ int ctrl = is_installed ? APK_FS_CTRL_DELETE : APK_FS_CTRL_CANCEL;
hlist_for_each_entry_safe(diri, dc, dn, &ipkg->owned_dirs, pkg_dirs_list) {
+ apk_blob_t dirname = APK_BLOB_PTR_LEN(diri->dir->name, diri->dir->namelen);
if (is_installed) diri->dir->modified = 1;
+ apk_fsdir_get(&d, dirname, db->extract_flags, db->ctx, apk_pkg_ctx(ipkg->pkg));
hlist_for_each_entry_safe(file, fc, fn, &diri->owned_files, diri_files_list) {
- if (is_installed)
- snprintf(name, sizeof name, DIR_FILE_FMT, DIR_FILE_PRINTF(diri->dir, file));
- else
- format_tmpname(ipkg->pkg, file, name);
-
key = (struct apk_db_file_hash_key) {
- .dirname = APK_BLOB_PTR_LEN(diri->dir->name, diri->dir->namelen),
+ .dirname = dirname,
.filename = APK_BLOB_PTR_LEN(file->name, file->namelen),
};
hash = apk_blob_hash_seed(key.filename, diri->dir->hash);
- if ((diri->dir->protect_mode == APK_PROTECT_NONE) ||
+ if (!is_installed ||
+ apk_protect_mode_none(diri->dir->protect_mode) ||
(db->ctx->flags & APK_PURGE) ||
- (file->csum.type != APK_CHECKSUM_NONE &&
- apk_fileinfo_get(db->root_fd, name, APK_FI_NOFOLLOW | APK_FI_CSUM(file->csum.type), &fi, &db->atoms) == 0 &&
- apk_digest_cmp_csum(&fi.digest, &file->csum) == 0))
- unlinkat(db->root_fd, name, 0);
- apk_dbg2(out, "%s", name);
+ apk_db_audit_file(&d, key.filename, file) == 0)
+ apk_fsdir_file_control(&d, key.filename, ctrl);
+
+ apk_dbg2(out, DIR_FILE_FMT, DIR_FILE_PRINTF(diri->dir, file));
__hlist_del(fc, &diri->owned_files.first);
if (is_installed) {
apk_hash_delete_hashed(&db->installed.files, APK_BLOB_BUF(&key), hash);
@@ -2661,31 +2857,39 @@ static void apk_db_purge_pkg(struct apk_database *db,
}
}
-
-static void apk_db_migrate_files(struct apk_database *db,
- struct apk_installed_package *ipkg)
+static uint8_t apk_db_migrate_files_for_priority(struct apk_database *db,
+ struct apk_installed_package *ipkg,
+ uint8_t priority)
{
struct apk_out *out = &db->ctx->out;
struct apk_db_dir_instance *diri;
struct apk_db_dir *dir;
struct apk_db_file *file, *ofile;
struct apk_db_file_hash_key key;
- struct apk_file_info fi;
struct hlist_node *dc, *dn, *fc, *fn;
+ struct apk_fsdir d;
unsigned long hash;
- char name[PATH_MAX], tmpname[TMPNAME_MAX];
- int cstype, r;
+ apk_blob_t dirname;
+ int r, ctrl, inetc;
+ uint8_t dir_priority, next_priority = APK_FS_PRIO_MAX;
hlist_for_each_entry_safe(diri, dc, dn, &ipkg->owned_dirs, pkg_dirs_list) {
dir = diri->dir;
- dir->modified = 1;
+ dirname = APK_BLOB_PTR_LEN(dir->name, dir->namelen);
+ apk_fsdir_get(&d, dirname, db->extract_flags, db->ctx, apk_pkg_ctx(ipkg->pkg));
+ dir_priority = apk_fsdir_priority(&d);
+ if (dir_priority != priority) {
+ if (dir_priority > priority && dir_priority < next_priority)
+ next_priority = dir_priority;
+ continue;
+ }
+ // Used for passwd/group check later
+ inetc = !apk_blob_compare(dirname, APK_BLOB_STRLIT("etc"));
+ dir->modified = 1;
hlist_for_each_entry_safe(file, fc, fn, &diri->owned_files, diri_files_list) {
- snprintf(name, sizeof(name), DIR_FILE_FMT, DIR_FILE_PRINTF(diri->dir, file));
- format_tmpname(ipkg->pkg, file, tmpname);
-
key = (struct apk_db_file_hash_key) {
- .dirname = APK_BLOB_PTR_LEN(dir->name, dir->namelen),
+ .dirname = dirname,
.filename = APK_BLOB_PTR_LEN(file->name, file->namelen),
};
@@ -2695,61 +2899,46 @@ static void apk_db_migrate_files(struct apk_database *db,
ofile = (struct apk_db_file *) apk_hash_get_hashed(
&db->installed.files, APK_BLOB_BUF(&key), hash);
- /* We want to compare checksums only if one exists
- * in db, and the file is in a protected path */
- cstype = APK_CHECKSUM_NONE;
- if (ofile != NULL && diri->dir->protect_mode != APK_PROTECT_NONE)
- cstype = APK_FI_CSUM(ofile->csum.type);
- cstype |= APK_FI_NOFOLLOW;
-
- r = apk_fileinfo_get(db->root_fd, name, cstype, &fi, &db->atoms);
+ ctrl = APK_FS_CTRL_COMMIT;
if (ofile && ofile->diri->pkg->name == NULL) {
- /* File was from overlay, delete the
- * packages version */
- unlinkat(db->root_fd, tmpname, 0);
- } else if ((diri->dir->protect_mode != APK_PROTECT_NONE) &&
- (r == 0) &&
- (ofile == NULL ||
- ofile->csum.type == APK_CHECKSUM_NONE ||
- apk_digest_cmp_csum(&fi.digest, &ofile->csum) != 0)) {
- /* Protected directory, with file without
- * db entry, or local modifications.
- *
- * Delete the apk-new if it's identical with the
- * existing file */
- if (ofile == NULL ||
- ofile->csum.type != file->csum.type)
- apk_fileinfo_get(db->root_fd, name,
- APK_FI_NOFOLLOW |APK_FI_CSUM(file->csum.type),
- &fi, &db->atoms);
+ // File was from overlay, delete the package's version
+ ctrl = APK_FS_CTRL_CANCEL;
+ } else if (!apk_protect_mode_none(diri->dir->protect_mode) &&
+ apk_db_audit_file(&d, key.filename, ofile) != 0) {
+ // Protected directory, and a file without db entry
+ // or with local modifications. Keep the filesystem file.
+ // Determine if the package's file should be kept as .apk-new
if ((db->ctx->flags & APK_CLEAN_PROTECTED) ||
- (file->csum.type != APK_CHECKSUM_NONE &&
- apk_digest_cmp_csum(&fi.digest, &file->csum) == 0)) {
- unlinkat(db->root_fd, tmpname, 0);
+ apk_db_audit_file(&d, key.filename, file) == 0) {
+ // No .apk-new files allowed, or the file on disk has the same
+ // hash as the file from new package. Keep the on disk one.
+ ctrl = APK_FS_CTRL_CANCEL;
} else {
- snprintf(name, sizeof name,
- DIR_FILE_FMT ".apk-new",
- DIR_FILE_PRINTF(diri->dir, file));
- if (renameat(db->root_fd, tmpname,
- db->root_fd, name) != 0) {
- apk_err(out, PKG_VER_FMT": failed to rename %s to %s.",
- PKG_VER_PRINTF(ipkg->pkg),
- tmpname, name);
- ipkg->broken_files = 1;
- }
+ // All files differ. Use the package's file as .apk-new.
+ ctrl = APK_FS_CTRL_APKNEW;
}
+ }
- } else {
- /* Overwrite the old file */
- if (renameat(db->root_fd, tmpname,
- db->root_fd, name) != 0) {
- apk_err(out, PKG_VER_FMT": failed to rename %s to %s.",
- PKG_VER_PRINTF(ipkg->pkg), tmpname, name);
- ipkg->broken_files = 1;
+ // Commit changes
+ r = apk_fsdir_file_control(&d, key.filename, ctrl);
+ if (r < 0) {
+ apk_err(out, PKG_VER_FMT": failed to commit " DIR_FILE_FMT ": %s",
+ PKG_VER_PRINTF(ipkg->pkg),
+ DIR_FILE_PRINTF(diri->dir, file),
+ apk_error_str(r));
+ ipkg->broken_files = 1;
+ } else if (inetc && ctrl == APK_FS_CTRL_COMMIT) {
+ // This is called when we successfully migrated the files
+ // in the filesystem; we explicitly do not care about apk-new
+ // or cancel cases, as that does not change the original file
+ if (!apk_blob_compare(key.filename, APK_BLOB_STRLIT("passwd")) ||
+ !apk_blob_compare(key.filename, APK_BLOB_STRLIT("group"))) {
+ // Reset the idcache because we have a new passwd/group
+ apk_id_cache_reset(db->id_cache);
}
}
- /* Claim ownership of the file in db */
+ // Claim ownership of the file in db
if (ofile != file) {
if (ofile != NULL) {
hlist_del(&ofile->diri_files_list,
@@ -2763,6 +2952,14 @@ static void apk_db_migrate_files(struct apk_database *db,
}
}
}
+ return next_priority;
+}
+
+static void apk_db_migrate_files(struct apk_database *db,
+ struct apk_installed_package *ipkg)
+{
+ for (uint8_t prio = APK_FS_PRIO_DISK; prio != APK_FS_PRIO_MAX; )
+ prio = apk_db_migrate_files_for_priority(db, ipkg, prio);
}
static int apk_db_unpack_pkg(struct apk_database *db,
@@ -2779,7 +2976,7 @@ static int apk_db_unpack_pkg(struct apk_database *db,
char cacheitem[128];
int r, filefd = AT_FDCWD, need_copy = FALSE;
- if (pkg->filename == NULL) {
+ if (!pkg->filename_ndx) {
repo = apk_db_select_repo(db, pkg);
if (repo == NULL) {
r = -APKE_PACKAGE_NOT_FOUND;
@@ -2791,7 +2988,7 @@ static int apk_db_unpack_pkg(struct apk_database *db,
if (!(pkg->repos & db->local_repos))
need_copy = TRUE;
} else {
- if (strlcpy(file, pkg->filename, sizeof file) >= sizeof file) {
+ if (strlcpy(file, db->filename_array->item[pkg->filename_ndx-1], sizeof file) >= sizeof file) {
r = -ENAMETOOLONG;
goto err_msg;
}
@@ -2801,9 +2998,9 @@ static int apk_db_unpack_pkg(struct apk_database *db,
need_copy = FALSE;
is = apk_istream_from_fd_url(filefd, file, apk_db_url_since(db, 0));
- if (IS_ERR_OR_NULL(is)) {
+ if (IS_ERR(is)) {
r = PTR_ERR(is);
- if (r == -ENOENT && pkg->filename == NULL)
+ if (r == -ENOENT && !pkg->filename_ndx)
r = -APKE_INDEX_STALE;
goto err_msg;
}
@@ -2827,10 +3024,9 @@ static int apk_db_unpack_pkg(struct apk_database *db,
.cb = cb,
.cb_ctx = cb_ctx,
};
- apk_sign_ctx_init(&ctx.sctx, APK_SIGN_VERIFY_IDENTITY, &pkg->csum, apk_ctx_get_trust(db->ctx));
- r = apk_tar_parse(apk_istream_gunzip_mpart(is, apk_sign_ctx_mpart_cb, &ctx.sctx), apk_db_install_archive_entry, &ctx, db->id_cache);
- apk_sign_ctx_free(&ctx.sctx);
-
+ apk_extract_init(&ctx.ectx, db->ctx, &extract_installer);
+ apk_extract_verify_identity(&ctx.ectx, &pkg->csum);
+ r = apk_extract(&ctx.ectx, is);
if (need_copy && r == 0) pkg->repos |= BIT(APK_REPOSITORY_CACHED);
if (r != 0) goto err_msg;
@@ -2913,76 +3109,251 @@ ret_r:
struct match_ctx {
struct apk_database *db;
struct apk_string_array *filter;
- unsigned int match;
- void (*cb)(struct apk_database *db, const char *match, struct apk_name *name, void *ctx);
+ apk_db_foreach_name_cb cb;
void *cb_ctx;
};
-static int match_names(apk_hash_item item, void *pctx)
+static int apk_string_match(const char *str, struct apk_string_array *filter, const char **res)
{
- struct match_ctx *ctx = (struct match_ctx *) pctx;
- struct apk_name *name = (struct apk_name *) item;
- unsigned int genid = ctx->match & APK_FOREACH_GENID_MASK;
char **pmatch;
- if (genid) {
- if (name->foreach_genid >= genid)
- return 0;
- name->foreach_genid = genid;
+ foreach_array_item(pmatch, filter) {
+ if (fnmatch(*pmatch, str, FNM_CASEFOLD) == 0) {
+ *res = *pmatch;
+ return 1;
+ }
}
+ return 0;
+}
- if (ctx->filter->num == 0) {
- ctx->cb(ctx->db, NULL, name, ctx->cb_ctx);
- return 0;
+static int apk_name_match(struct apk_name *name, struct apk_string_array *filter, const char **res)
+{
+ if (!filter) {
+ *res = NULL;
+ return 1;
}
+ return apk_string_match(name->name, filter, res);
+}
- foreach_array_item(pmatch, ctx->filter) {
- if (fnmatch(*pmatch, name->name, 0) == 0) {
- ctx->cb(ctx->db, *pmatch, name, ctx->cb_ctx);
- if (genid)
- break;
- }
+static int apk_pkg_match(struct apk_package *pkg, struct apk_string_array *filter, const char **res, int provides)
+{
+ struct apk_dependency *d;
+
+ if (apk_name_match(pkg->name, filter, res)) return 1;
+ if (!provides) return 0;
+ foreach_array_item(d, pkg->provides) {
+ if (apk_string_match(d->name->name, filter, res)) return 1;
}
+ return 0;
+}
+static int match_names(apk_hash_item item, void *pctx)
+{
+ struct match_ctx *ctx = (struct match_ctx *) pctx;
+ struct apk_name *name = (struct apk_name *) item;
+ const char *match;
+
+ if (apk_name_match(name, ctx->filter, &match))
+ return ctx->cb(ctx->db, match, name, ctx->cb_ctx);
return 0;
}
-void apk_name_foreach_matching(struct apk_database *db, struct apk_string_array *filter, unsigned int match,
- void (*cb)(struct apk_database *db, const char *match, struct apk_name *name, void *ctx),
- void *ctx)
+int apk_db_foreach_matching_name(
+ struct apk_database *db, struct apk_string_array *filter,
+ apk_db_foreach_name_cb cb, void *ctx)
{
char **pmatch;
- unsigned int genid = match & APK_FOREACH_GENID_MASK;
struct apk_name *name;
struct match_ctx mctx = {
.db = db,
- .filter = filter,
- .match = match,
.cb = cb,
.cb_ctx = ctx,
};
+ int r;
- if (filter == NULL || filter->num == 0) {
- if (!(match & APK_FOREACH_NULL_MATCHES_ALL))
- return;
- apk_string_array_init(&mctx.filter);
- goto all;
- }
+ if (!filter || !filter->num) goto all;
+
+ mctx.filter = filter;
foreach_array_item(pmatch, filter)
if (strchr(*pmatch, '*') != NULL)
goto all;
foreach_array_item(pmatch, filter) {
name = (struct apk_name *) apk_hash_get(&db->available.names, APK_BLOB_STR(*pmatch));
- if (genid && name) {
- if (name->foreach_genid >= genid)
+ r = cb(db, *pmatch, name, ctx);
+ if (r) return r;
+ }
+ return 0;
+
+all:
+ return apk_hash_foreach(&db->available.names, match_names, &mctx);
+}
+
+static int cmp_name(const void *a, const void *b)
+{
+ const struct apk_name * const* na = a, * const* nb = b;
+ return apk_name_cmp_display(*na, *nb);
+}
+
+static int cmp_package(const void *a, const void *b)
+{
+ const struct apk_package * const* pa = a, * const* pb = b;
+ return apk_pkg_cmp_display(*pa, *pb);
+}
+
+struct add_name_ctx {
+ struct apk_name_array *a;
+ size_t i;
+};
+
+static int add_name(apk_hash_item item, void *ctx)
+{
+ struct apk_name *name = (struct apk_name *) item;
+ struct add_name_ctx *a = ctx;
+ a->a->item[a->i++] = name;
+ return 0;
+}
+
+static struct apk_name_array *apk_db_sorted_names(struct apk_database *db)
+{
+ if (!db->sorted_names) {
+ apk_name_array_resize(&db->available.sorted_names, db->available.names.num_items);
+
+ struct add_name_ctx ctx = { .a = db->available.sorted_names, .i = 0 };
+ apk_hash_foreach(&db->available.names, add_name, &ctx);
+
+ qsort(db->available.sorted_names->item, db->available.sorted_names->num,
+ sizeof(db->available.sorted_names->item[0]), cmp_name);
+ db->sorted_names = 1;
+ }
+ return db->available.sorted_names;
+}
+
+struct apk_package_array *apk_db_sorted_installed_packages(struct apk_database *db)
+{
+ struct apk_installed_package *ipkg;
+ int n = 0;
+
+ if (!db->sorted_installed_packages) {
+ db->sorted_installed_packages = 1;
+ apk_package_array_resize(&db->installed.sorted_packages, db->installed.stats.packages);
+ list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list)
+ db->installed.sorted_packages->item[n++] = ipkg->pkg;
+ qsort(db->installed.sorted_packages->item, db->installed.sorted_packages->num,
+ sizeof db->installed.sorted_packages->item[0], cmp_package);
+ }
+ return db->installed.sorted_packages;
+}
+
+int apk_db_foreach_sorted_name(struct apk_database *db, struct apk_string_array *filter,
+ apk_db_foreach_name_cb cb, void *cb_ctx)
+{
+ int r, walk_all = 0;
+ char **pmatch;
+ const char *match;
+ struct apk_name *name;
+ struct apk_name *results[128], **res;
+ size_t i, num_res = 0;
+
+ if (filter && filter->num) {
+ foreach_array_item(pmatch, filter) {
+ name = (struct apk_name *) apk_hash_get(&db->available.names, APK_BLOB_STR(*pmatch));
+ if (strchr(*pmatch, '*')) {
+ walk_all = 1;
continue;
- name->foreach_genid = genid;
+ }
+ if (!name) {
+ cb(db, *pmatch, NULL, cb_ctx);
+ continue;
+ }
+ if (walk_all) continue;
+ if (num_res >= ARRAY_SIZE(results)) {
+ walk_all = 1;
+ continue;
+ }
+ results[num_res++] = name;
}
- cb(db, *pmatch, name, ctx);
+ } else {
+ filter = NULL;
+ walk_all = 1;
}
- return;
-all:
- apk_hash_foreach(&db->available.names, match_names, &mctx);
+ if (walk_all) {
+ struct apk_name_array *a = apk_db_sorted_names(db);
+ res = a->item;
+ num_res = a->num;
+ } else {
+ qsort(results, num_res, sizeof results[0], cmp_name);
+ res = results;
+ }
+
+ for (i = 0; i < num_res; i++) {
+ name = res[i];
+ if (apk_name_match(name, filter, &match)) {
+ r = cb(db, match, name, cb_ctx);
+ if (r) return r;
+ }
+ }
+ return 0;
+}
+
+int __apk_db_foreach_sorted_package(struct apk_database *db, struct apk_string_array *filter,
+ apk_db_foreach_package_cb cb, void *cb_ctx, int provides)
+{
+ char **pmatch;
+ const char *match;
+ struct apk_name *name;
+ struct apk_package *results[128];
+ struct apk_provider *p;
+ size_t i, num_res = 0;
+ int r;
+
+ if (!filter || !filter->num) {
+ filter = NULL;
+ goto walk_all;
+ }
+
+ foreach_array_item(pmatch, filter) {
+ name = (struct apk_name *) apk_hash_get(&db->available.names, APK_BLOB_STR(*pmatch));
+ if (strchr(*pmatch, '*')) goto walk_all;
+ if (!name) {
+ cb(db, *pmatch, NULL, cb_ctx);
+ continue;
+ }
+
+ foreach_array_item(p, name->providers) {
+ if (!provides && p->pkg->name != name) continue;
+ if (p->pkg->seen) continue;
+ p->pkg->seen = 1;
+ if (num_res >= ARRAY_SIZE(results)) goto walk_all;
+ results[num_res++] = p->pkg;
+ }
+ }
+ for (i = 0; i < num_res; i++) results[i]->seen = 0;
+
+ qsort(results, num_res, sizeof results[0], cmp_package);
+ for (i = 0; i < num_res; i++) {
+ if (apk_pkg_match(results[i], filter, &match, provides)) {
+ r = cb(db, match, results[i], cb_ctx);
+ if (r) return r;
+ }
+ }
+ return 0;
+
+walk_all:
+ for (i = 0; i < num_res; i++) results[i]->seen = 0;
+
+ struct apk_name_array *a = apk_db_sorted_names(db);
+ for (i = 0; i < a->num; i++) {
+ name = a->item[i];
+ apk_name_sorted_providers(name);
+ foreach_array_item(p, name->providers) {
+ if (p->pkg->name != name) continue;
+ if (apk_pkg_match(p->pkg, filter, &match, provides)) {
+ r = cb(db, match, p->pkg, cb_ctx);
+ if (r) return r;
+ }
+ }
+ }
+ return 0;
}
diff --git a/src/extract_v2.c b/src/extract_v2.c
new file mode 100644
index 0000000..b1b7e06
--- /dev/null
+++ b/src/extract_v2.c
@@ -0,0 +1,387 @@
+/* extract_v2.c - Alpine Package Keeper (APK)
+ *
+ * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
+ * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#include "apk_context.h"
+#include "apk_extract.h"
+#include "apk_package.h"
+#include "apk_crypto.h"
+#include "apk_tar.h"
+
+#define APK_SIGN_VERIFY 1
+#define APK_SIGN_VERIFY_IDENTITY 2
+#define APK_SIGN_VERIFY_AND_GENERATE 3
+
+struct apk_sign_ctx {
+ struct apk_trust *trust;
+ int action;
+ int num_signatures;
+ int verify_error;
+ unsigned char control_started : 1;
+ unsigned char data_started : 1;
+ unsigned char has_data_checksum : 1;
+ unsigned char control_verified : 1;
+ unsigned char data_verified : 1;
+ unsigned char allow_untrusted : 1;
+ unsigned char end_seen : 1;
+ uint8_t alg;
+ struct apk_digest data_hash;
+ struct apk_digest identity;
+ struct apk_digest_ctx digest_ctx;
+ struct apk_digest_ctx identity_ctx;
+
+ struct {
+ apk_blob_t data;
+ struct apk_pkey *pkey;
+ char *identity;
+ } signature;
+};
+
+static void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action, struct apk_checksum *identity, struct apk_trust *trust)
+{
+ memset(ctx, 0, sizeof(struct apk_sign_ctx));
+ ctx->trust = trust;
+ ctx->action = action;
+ ctx->allow_untrusted = trust->allow_untrusted;
+ ctx->verify_error = -APKE_SIGNATURE_UNTRUSTED;
+ ctx->alg = APK_DIGEST_SHA1;
+ switch (action) {
+ case APK_SIGN_VERIFY_AND_GENERATE:
+ apk_digest_ctx_init(&ctx->identity_ctx, APK_DIGEST_SHA1);
+ break;
+ case APK_SIGN_VERIFY:
+ break;
+ case APK_SIGN_VERIFY_IDENTITY:
+ apk_digest_from_checksum(&ctx->identity, identity);
+ break;
+ default:
+ assert(!"unreachable");
+ break;
+ }
+ apk_digest_ctx_init(&ctx->digest_ctx, ctx->alg);
+}
+
+static void apk_sign_ctx_free(struct apk_sign_ctx *ctx)
+{
+ free(ctx->signature.data.ptr);
+ apk_digest_ctx_free(&ctx->identity_ctx);
+ apk_digest_ctx_free(&ctx->digest_ctx);
+}
+
+static int check_signing_key_trust(struct apk_sign_ctx *sctx)
+{
+ switch (sctx->action) {
+ case APK_SIGN_VERIFY:
+ case APK_SIGN_VERIFY_AND_GENERATE:
+ if (sctx->signature.pkey == NULL) {
+ if (sctx->allow_untrusted)
+ break;
+ return -APKE_SIGNATURE_UNTRUSTED;
+ }
+ }
+ return 0;
+}
+
+static int apk_sign_ctx_process_file(struct apk_sign_ctx *ctx, const struct apk_file_info *fi,
+ struct apk_istream *is)
+{
+ static struct {
+ char type[7];
+ uint8_t alg;
+ } signature_type[] = {
+ { "RSA512", APK_DIGEST_SHA512 },
+ { "RSA256", APK_DIGEST_SHA256 },
+ { "RSA", APK_DIGEST_SHA1 },
+ { "DSA", APK_DIGEST_SHA1 },
+ };
+ uint8_t alg = APK_DIGEST_NONE;
+ const char *name = NULL;
+ struct apk_pkey *pkey;
+ int r, i;
+
+ if (ctx->data_started)
+ return 1;
+
+ if (fi->name[0] != '.' || strchr(fi->name, '/') != NULL) {
+ /* APKv1.0 compatibility - first non-hidden file is
+ * considered to start the data section of the file.
+ * This does not make any sense if the file has v2.0
+ * style .PKGINFO */
+ if (ctx->has_data_checksum)
+ return -APKE_V2PKG_FORMAT;
+ /* Error out early if identity part is missing */
+ if (ctx->action == APK_SIGN_VERIFY_IDENTITY)
+ return -APKE_V2PKG_FORMAT;
+ ctx->data_started = 1;
+ ctx->control_started = 1;
+ r = check_signing_key_trust(ctx);
+ if (r != 0) return r;
+ return 1;
+ }
+
+ if (ctx->control_started)
+ return 1;
+
+ if (strncmp(fi->name, ".SIGN.", 6) != 0) {
+ ctx->control_started = 1;
+ return 1;
+ }
+
+ /* By this point, we must be handling a signature file */
+ ctx->num_signatures++;
+
+ /* Already found a signature by a trusted key; no need to keep searching */
+ if (ctx->signature.pkey != NULL) return 0;
+ if (ctx->action == APK_SIGN_VERIFY_IDENTITY) return 0;
+
+ for (i = 0; i < ARRAY_SIZE(signature_type); i++) {
+ size_t slen = strlen(signature_type[i].type);
+ if (strncmp(&fi->name[6], signature_type[i].type, slen) == 0 &&
+ fi->name[6+slen] == '.') {
+ alg = signature_type[i].alg;
+ name = &fi->name[6+slen+1];
+ break;
+ }
+ }
+ if (alg == APK_DIGEST_NONE) return 0;
+
+ pkey = apk_trust_key_by_name(ctx->trust, name);
+ if (pkey) {
+ ctx->alg = alg;
+ ctx->signature.pkey = pkey;
+ apk_blob_from_istream(is, fi->size, &ctx->signature.data);
+ }
+ return 0;
+}
+
+
+/* apk_sign_ctx_mpart_cb() handles hashing archives and checking signatures, but
+ it can't do it alone. apk_sign_ctx_process_file() must be in the loop to
+ actually select which signature is to be verified and load the corresponding
+ public key into the context object, and apk_sign_ctx_parse_pkginfo_line()
+ needs to be called when handling the .PKGINFO file to find any applicable
+ datahash and load it into the context for this function to check against. */
+static int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data)
+{
+ struct apk_sign_ctx *sctx = (struct apk_sign_ctx *) ctx;
+ struct apk_digest calculated;
+ int r, end_of_control;
+
+ if (sctx->end_seen || sctx->data_verified) return -APKE_FORMAT_INVALID;
+ if (part == APK_MPART_BOUNDARY && sctx->data_started) return -APKE_FORMAT_INVALID;
+ if (part == APK_MPART_END) sctx->end_seen = 1;
+ if (part == APK_MPART_DATA) {
+ /* Update digest with the data now. Only _DATA callbacks can have data. */
+ r = apk_digest_ctx_update(&sctx->digest_ctx, data.ptr, data.len);
+ if (r != 0) return r;
+
+ /* Update identity generated also if needed. */
+ if (sctx->control_started && !sctx->data_started &&
+ sctx->identity_ctx.alg != APK_DIGEST_NONE) {
+ r = apk_digest_ctx_update(&sctx->identity_ctx, data.ptr, data.len);
+ if (r != 0) return r;
+ }
+ return 0;
+ }
+ if (data.len) return -APKE_FORMAT_INVALID;
+
+ /* Still in signature blocks? */
+ if (!sctx->control_started) {
+ if (part == APK_MPART_END) return -APKE_FORMAT_INVALID;
+
+ r = apk_digest_ctx_reset(&sctx->identity_ctx);
+ if (r != 0) return r;
+
+ /* Control block starting, prepare for signature verification */
+ if (sctx->signature.pkey == NULL || sctx->action == APK_SIGN_VERIFY_IDENTITY)
+ return apk_digest_ctx_reset_alg(&sctx->digest_ctx, sctx->alg);
+
+ return apk_verify_start(&sctx->digest_ctx, sctx->alg, sctx->signature.pkey);
+ }
+
+ /* Grab state and mark all remaining block as data */
+ end_of_control = (sctx->data_started == 0);
+ sctx->data_started = 1;
+
+ /* End of control-block and control does not have data checksum? */
+ if (sctx->has_data_checksum == 0 && end_of_control && part != APK_MPART_END)
+ return 0;
+
+ if (sctx->has_data_checksum && !end_of_control) {
+ /* End of data-block with a checksum read from the control block */
+ r = apk_digest_ctx_final(&sctx->digest_ctx, &calculated);
+ if (r != 0) return r;
+ if (apk_digest_cmp(&calculated, &sctx->data_hash) != 0)
+ return -APKE_V2PKG_INTEGRITY;
+ sctx->data_verified = 1;
+ if (!sctx->allow_untrusted && !sctx->control_verified)
+ return -APKE_SIGNATURE_UNTRUSTED;
+ return 0;
+ }
+
+ /* Either end of control block with a data checksum or end
+ * of the data block following a control block without a data
+ * checksum. In either case, we're checking a signature. */
+ r = check_signing_key_trust(sctx);
+ if (r != 0) return r;
+
+ switch (sctx->action) {
+ case APK_SIGN_VERIFY_AND_GENERATE:
+ /* Package identity is the checksum */
+ apk_digest_ctx_final(&sctx->identity_ctx, &sctx->identity);
+ if (!sctx->has_data_checksum) return -APKE_V2PKG_FORMAT;
+ /* Fallthrough to check signature */
+ case APK_SIGN_VERIFY:
+ if (sctx->signature.pkey != NULL) {
+ sctx->verify_error = apk_verify(&sctx->digest_ctx,
+ (unsigned char *) sctx->signature.data.ptr,
+ sctx->signature.data.len);
+ }
+ if (sctx->verify_error) {
+ if (sctx->verify_error != -APKE_SIGNATURE_UNTRUSTED ||
+ !sctx->allow_untrusted)
+ return sctx->verify_error;
+ }
+ sctx->control_verified = 1;
+ if (!sctx->has_data_checksum && part == APK_MPART_END)
+ sctx->data_verified = 1;
+ break;
+ case APK_SIGN_VERIFY_IDENTITY:
+ /* Reset digest for hashing data */
+ apk_digest_ctx_final(&sctx->digest_ctx, &calculated);
+ if (apk_digest_cmp(&calculated, &sctx->identity) != 0)
+ return -APKE_V2PKG_INTEGRITY;
+ sctx->verify_error = 0;
+ sctx->control_verified = 1;
+ if (!sctx->has_data_checksum && part == APK_MPART_END)
+ sctx->data_verified = 1;
+ break;
+ }
+
+ r = apk_digest_ctx_reset(&sctx->identity_ctx);
+ if (r != 0) return r;
+
+ return apk_digest_ctx_reset_alg(&sctx->digest_ctx, sctx->alg);
+}
+
+static int apk_extract_verify_v2index(struct apk_extract_ctx *ectx, apk_blob_t *desc, struct apk_istream *is)
+{
+ return 0;
+}
+
+static int apk_extract_verify_v2file(struct apk_extract_ctx *ectx, const struct apk_file_info *fi, struct apk_istream *is)
+{
+ return 0;
+}
+
+static const struct apk_extract_ops extract_v2verify_ops = {
+ .v2index = apk_extract_verify_v2index,
+ .v2meta = apk_extract_v2_meta,
+ .file = apk_extract_verify_v2file,
+};
+
+static int apk_extract_v2_entry(void *pctx, const struct apk_file_info *fi, struct apk_istream *is)
+{
+ struct apk_extract_ctx *ectx = pctx;
+ struct apk_sign_ctx *sctx = ectx->pctx;
+ int r, type;
+
+ r = apk_sign_ctx_process_file(sctx, fi, is);
+ if (r <= 0) return r;
+
+ if (!sctx->control_started) return 0;
+ if (!sctx->data_started || !sctx->has_data_checksum) {
+ if (fi->name[0] == '.') {
+ ectx->is_package = 1;
+ if (ectx->is_index) return -APKE_V2NDX_FORMAT;
+ if (!ectx->ops->v2meta) return -APKE_FORMAT_NOT_SUPPORTED;
+ if (strcmp(fi->name, ".PKGINFO") == 0) {
+ return ectx->ops->v2meta(ectx, is);
+ } else if (strcmp(fi->name, ".INSTALL") == 0) {
+ return -APKE_V2PKG_FORMAT;
+ } else if ((type = apk_script_type(&fi->name[1])) != APK_SCRIPT_INVALID) {
+ if (ectx->ops->script) return ectx->ops->script(ectx, type, fi->size, is);
+ }
+ } else {
+ ectx->is_index = 1;
+ if (ectx->is_package) return -APKE_V2PKG_FORMAT;
+ if (!ectx->ops->v2index) return -APKE_FORMAT_NOT_SUPPORTED;
+ if (strcmp(fi->name, "DESCRIPTION") == 0) {
+ free(ectx->desc.ptr);
+ apk_blob_from_istream(is, fi->size, &ectx->desc);
+ } else if (strcmp(fi->name, "APKINDEX") == 0) {
+ return ectx->ops->v2index(ectx, &ectx->desc, is);
+ }
+ }
+ return 0;
+ }
+
+ if (!sctx->data_started) return 0;
+ if (!ectx->ops->file) return -ECANCELED;
+ return ectx->ops->file(ectx, fi, is);
+}
+
+int apk_extract_v2(struct apk_extract_ctx *ectx, struct apk_istream *is)
+{
+ struct apk_ctx *ac = ectx->ac;
+ struct apk_trust *trust = apk_ctx_get_trust(ac);
+ struct apk_sign_ctx sctx;
+ int r, action;
+
+ if (ectx->generate_identity)
+ action = APK_SIGN_VERIFY_AND_GENERATE;
+ else if (ectx->identity)
+ action = APK_SIGN_VERIFY_IDENTITY;
+ else
+ action = APK_SIGN_VERIFY;
+
+ if (!ectx->ops) ectx->ops = &extract_v2verify_ops;
+ ectx->pctx = &sctx;
+ apk_sign_ctx_init(&sctx, action, ectx->identity, trust);
+ r = apk_tar_parse(
+ apk_istream_gunzip_mpart(is, apk_sign_ctx_mpart_cb, &sctx),
+ apk_extract_v2_entry, ectx, apk_ctx_get_id_cache(ac));
+ if ((r == 0 || r == -ECANCELED || r == -APKE_EOF) && !ectx->is_package && !ectx->is_index)
+ r = -APKE_FORMAT_INVALID;
+ if (r == 0 && (!sctx.data_verified || !sctx.end_seen)) r = -APKE_V2PKG_INTEGRITY;
+ if ((r == 0 || r == -ECANCELED) && sctx.verify_error) r = sctx.verify_error;
+ if (r == -APKE_SIGNATURE_UNTRUSTED && sctx.allow_untrusted) r = 0;
+ if (ectx->generate_identity) apk_checksum_from_digest(ectx->identity, &sctx.identity);
+ apk_sign_ctx_free(&sctx);
+ free(ectx->desc.ptr);
+ apk_extract_reset(ectx);
+
+ return r;
+}
+
+void apk_extract_v2_control(struct apk_extract_ctx *ectx, apk_blob_t l, apk_blob_t r)
+{
+ struct apk_sign_ctx *sctx = ectx->pctx;
+
+ if (!sctx || !sctx->control_started || sctx->data_started) return;
+
+ if (apk_blob_compare(APK_BLOB_STR("datahash"), l) == 0) {
+ sctx->has_data_checksum = 1;
+ sctx->alg = APK_DIGEST_SHA256;
+ apk_digest_set(&sctx->data_hash, sctx->alg);
+ apk_blob_pull_hexdump(&r, APK_DIGEST_BLOB(sctx->data_hash));
+ }
+}
+
+int apk_extract_v2_meta(struct apk_extract_ctx *ectx, struct apk_istream *is)
+{
+ apk_blob_t k, v, token = APK_BLOB_STRLIT("\n");
+ while (apk_istream_get_delim(is, token, &k) == 0) {
+ if (k.len < 1 || k.ptr[0] == '#') continue;
+ if (apk_blob_split(k, APK_BLOB_STRLIT(" = "), &k, &v)) {
+ apk_extract_v2_control(ectx, k, v);
+ }
+ }
+ return 0;
+}
+
diff --git a/src/extract_v3.c b/src/extract_v3.c
new file mode 100644
index 0000000..ba4edec
--- /dev/null
+++ b/src/extract_v3.c
@@ -0,0 +1,295 @@
+/* extract_v3.c - Alpine Package Keeper (APK)
+ *
+ * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
+ * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#include <sys/stat.h>
+
+#include "apk_context.h"
+#include "apk_extract.h"
+#include "apk_adb.h"
+#include "apk_pathbuilder.h"
+
+struct apk_extract_v3_ctx {
+ struct apk_extract_ctx *ectx;
+ struct adb db;
+ struct adb_obj pkg, paths, path, files, file;
+ unsigned int cur_path, cur_file;
+ struct apk_pathbuilder pb;
+};
+
+static void apk_extract_v3_acl(struct apk_file_info *fi, struct adb_obj *o, struct apk_id_cache *idc)
+{
+ struct adb_obj xa;
+ apk_blob_t x, key, value;
+ int i;
+
+ fi->mode = adb_ro_int(o, ADBI_ACL_MODE);
+ fi->uid = apk_id_cache_resolve_uid(idc, adb_ro_blob(o, ADBI_ACL_USER), 65534);
+ fi->gid = apk_id_cache_resolve_gid(idc, adb_ro_blob(o, ADBI_ACL_GROUP), 65534);
+
+ adb_ro_obj(o, ADBI_ACL_XATTRS, &xa);
+
+ apk_xattr_array_resize(&fi->xattrs, adb_ra_num(&xa));
+ for (i = ADBI_FIRST; i <= adb_ra_num(&xa); i++) {
+ x = adb_ro_blob(&xa, i);
+ apk_blob_split(x, APK_BLOB_BUF(""), &key, &value);
+
+ fi->xattrs->item[i-1] = (struct apk_xattr) {
+ .name = key.ptr,
+ .value = value,
+ };
+ }
+ apk_fileinfo_hash_xattr(fi, APK_DIGEST_SHA1);
+}
+
+static int apk_extract_v3_file(struct apk_extract_ctx *ectx, off_t sz, struct apk_istream *is)
+{
+ struct apk_extract_v3_ctx *ctx = ectx->pctx;
+ const char *path_name = apk_pathbuilder_cstr(&ctx->pb);
+ struct apk_file_info fi = {
+ .name = path_name,
+ .size = adb_ro_int(&ctx->file, ADBI_FI_SIZE),
+ .mtime = adb_ro_int(&ctx->file, ADBI_FI_MTIME),
+ };
+ struct adb_obj acl;
+ struct apk_digest_istream dis;
+ apk_blob_t target;
+ int r;
+
+ apk_xattr_array_init(&fi.xattrs);
+ apk_extract_v3_acl(&fi, adb_ro_obj(&ctx->file, ADBI_FI_ACL, &acl), apk_ctx_get_id_cache(ectx->ac));
+
+ target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET);
+ if (!APK_BLOB_IS_NULL(target)) {
+ char *target_path;
+ uint16_t mode;
+
+ if (target.len < 2) goto err_schema;
+ mode = le16toh(*(uint16_t*)target.ptr);
+ target.ptr += 2;
+ target.len -= 2;
+ switch (mode) {
+ case S_IFBLK:
+ case S_IFCHR:
+ case S_IFIFO:
+ if (target.len != sizeof(uint64_t)) goto err_schema;
+ struct unaligned64 {
+ uint64_t value;
+ } __attribute__((packed));
+ fi.device = le64toh(((struct unaligned64 *)target.ptr)->value);
+ break;
+ case S_IFLNK:
+ target_path = alloca(target.len + 1);
+ memcpy(target_path, target.ptr, target.len);
+ target_path[target.len] = 0;
+ fi.link_target = target_path;
+ break;
+ default:
+ err_schema:
+ r = -APKE_ADB_SCHEMA;
+ goto done;
+ }
+ fi.mode |= mode;
+ r = ectx->ops->file(ectx, &fi, is);
+ goto done;
+ }
+
+ apk_digest_from_blob(&fi.digest, adb_ro_blob(&ctx->file, ADBI_FI_HASHES));
+ if (fi.digest.alg == APK_DIGEST_NONE) goto err_schema;
+ fi.mode |= S_IFREG;
+ if (!is) {
+ r = ectx->ops->file(ectx, &fi, 0);
+ goto done;
+ }
+
+ r = ectx->ops->file(ectx, &fi, apk_istream_verify(&dis, is, fi.size, &fi.digest));
+ r = apk_istream_close_error(&dis.is, r);
+done:
+ apk_fileinfo_free(&fi);
+ return r;
+}
+
+static int apk_extract_v3_directory(struct apk_extract_ctx *ectx)
+{
+ struct apk_extract_v3_ctx *ctx = ectx->pctx;
+ struct apk_file_info fi = {
+ .name = apk_pathbuilder_cstr(&ctx->pb),
+ };
+ struct adb_obj acl;
+ int r;
+
+ apk_xattr_array_init(&fi.xattrs);
+ apk_extract_v3_acl(&fi, adb_ro_obj(&ctx->path, ADBI_DI_ACL, &acl), apk_ctx_get_id_cache(ectx->ac));
+ fi.mode |= S_IFDIR;
+ r = ectx->ops->file(ectx, &fi, 0);
+ apk_fileinfo_free(&fi);
+
+ return r;
+}
+
+static int apk_extract_v3_next_file(struct apk_extract_ctx *ectx)
+{
+ struct apk_extract_v3_ctx *ctx = ectx->pctx;
+ apk_blob_t target;
+ int r;
+
+ if (!ctx->cur_path) {
+ // one time init
+ ctx->cur_path = ADBI_FIRST;
+ ctx->cur_file = 0;
+ adb_r_rootobj(&ctx->db, &ctx->pkg, &schema_package);
+
+ r = ectx->ops->v3meta(ectx, &ctx->pkg);
+ if (r < 0) return r;
+
+ adb_ro_obj(&ctx->pkg, ADBI_PKG_PATHS, &ctx->paths);
+ adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path);
+ adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files);
+ if (!ectx->ops->file) return -ECANCELED;
+ }
+
+ do {
+ ctx->cur_file++;
+ while (ctx->cur_file > adb_ra_num(&ctx->files)) {
+ ctx->cur_path++;
+ ctx->cur_file = ADBI_FIRST;
+ if (ctx->cur_path > adb_ra_num(&ctx->paths)) return 1;
+ adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path);
+ apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME));
+ adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files);
+ r = apk_extract_v3_directory(ectx);
+ if (r != 0) return r;
+ }
+ adb_ro_obj(&ctx->files, ctx->cur_file, &ctx->file);
+ apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME));
+ apk_pathbuilder_pushb(&ctx->pb, adb_ro_blob(&ctx->file, ADBI_FI_NAME));
+ target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET);
+ if (adb_ro_int(&ctx->file, ADBI_FI_SIZE) != 0 &&
+ APK_BLOB_IS_NULL(target)) {
+ return 0;
+ }
+ r = apk_extract_v3_file(ectx, 0, 0);
+ if (r != 0) return r;
+ } while (1);
+}
+
+static int apk_extract_v3_data_block(struct adb *db, struct adb_block *b, struct apk_istream *is)
+{
+ struct apk_extract_v3_ctx *ctx = container_of(db, struct apk_extract_v3_ctx, db);
+ struct apk_extract_ctx *ectx = ctx->ectx;
+ struct adb_data_package *hdr;
+ uint64_t sz = adb_block_length(b);
+ int r;
+
+ if (adb_block_type(b) != ADB_BLOCK_DATA) return 0;
+ if (db->schema != ADB_SCHEMA_PACKAGE) return -APKE_ADB_SCHEMA;
+ if (!ectx->ops->v3meta) return -APKE_FORMAT_NOT_SUPPORTED;
+
+ r = apk_extract_v3_next_file(ectx);
+ if (r != 0) {
+ if (r > 0) r = -APKE_ADB_BLOCK;
+ return r;
+ }
+
+ hdr = apk_istream_get(is, sizeof *hdr);
+ sz -= sizeof *hdr;
+ if (IS_ERR(hdr)) return PTR_ERR(hdr);
+
+ if (le32toh(hdr->path_idx) != ctx->cur_path ||
+ le32toh(hdr->file_idx) != ctx->cur_file ||
+ sz != adb_ro_int(&ctx->file, ADBI_FI_SIZE)) {
+ // got data for some unexpected file
+ return -APKE_ADB_BLOCK;
+ }
+
+ return apk_extract_v3_file(ectx, sz, is);
+}
+
+static int apk_extract_v3_verify_index(struct apk_extract_ctx *ectx, struct adb_obj *obj)
+{
+ return 0;
+}
+
+static int apk_extract_v3_verify_meta(struct apk_extract_ctx *ectx, struct adb_obj *obj)
+{
+ return 0;
+}
+
+static int apk_extract_v3_verify_file(struct apk_extract_ctx *ectx, const struct apk_file_info *fi, struct apk_istream *is)
+{
+ if (is) {
+ apk_istream_read(is, 0, fi->size);
+ return apk_istream_close(is);
+ }
+ return 0;
+}
+
+static const struct apk_extract_ops extract_v3verify_ops = {
+ .v3index = apk_extract_v3_verify_index,
+ .v3meta = apk_extract_v3_verify_meta,
+ .file = apk_extract_v3_verify_file,
+};
+
+int apk_extract_v3(struct apk_extract_ctx *ectx, struct apk_istream *is)
+{
+ struct apk_ctx *ac = ectx->ac;
+ struct apk_trust *trust = apk_ctx_get_trust(ac);
+ struct apk_extract_v3_ctx ctx = {
+ .ectx = ectx,
+ };
+ struct adb_obj obj;
+ int r;
+
+ if (IS_ERR(is)) return PTR_ERR(is);
+ if (!ectx->ops) ectx->ops = &extract_v3verify_ops;
+ if (!ectx->ops->v3meta && !ectx->ops->v3index)
+ return apk_istream_close_error(is, -APKE_FORMAT_NOT_SUPPORTED);
+
+ ectx->pctx = &ctx;
+ r = adb_m_process(&ctx.db, adb_decompress(is, 0),
+ ADB_SCHEMA_ANY, trust, apk_extract_v3_data_block);
+ if (r == 0) {
+ switch (ctx.db.schema) {
+ case ADB_SCHEMA_PACKAGE:
+ r = apk_extract_v3_next_file(ectx);
+ if (r == 0) r = -APKE_ADB_BLOCK;
+ if (r == 1) r = 0;
+ break;
+ case ADB_SCHEMA_INDEX:
+ if (!ectx->ops->v3index) {
+ r = -APKE_FORMAT_NOT_SUPPORTED;
+ break;
+ }
+ adb_r_rootobj(&ctx.db, &obj, &schema_index);
+ r = ectx->ops->v3index(ectx, &obj);
+ break;
+ default:
+ r = -APKE_ADB_SCHEMA;
+ break;
+ }
+ }
+ if (r == -ECANCELED) r = 0;
+ if (r == 0 && !ctx.db.adb.len) r = -APKE_ADB_BLOCK;
+ adb_free(&ctx.db);
+ apk_extract_reset(ectx);
+
+ return r;
+}
+
+int apk_extract(struct apk_extract_ctx *ectx, struct apk_istream *is)
+{
+ void *sig;
+
+ if (IS_ERR(is)) return PTR_ERR(is);
+
+ sig = apk_istream_peek(is, 4);
+ if (IS_ERR(sig)) return apk_istream_close_error(is, PTR_ERR(sig));
+
+ if (memcmp(sig, "ADB", 3) == 0) return apk_extract_v3(ectx, is);
+ return apk_extract_v2(ectx, is);
+}
diff --git a/src/fs_fsys.c b/src/fs_fsys.c
new file mode 100644
index 0000000..316469f
--- /dev/null
+++ b/src/fs_fsys.c
@@ -0,0 +1,332 @@
+/* fsops_sys.c - Alpine Package Keeper (APK)
+ *
+ * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
+ * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "apk_fs.h"
+#include "apk_xattr.h"
+#include "apk_database.h" // for db->atoms
+
+#define TMPNAME_MAX (PATH_MAX + 64)
+
+static int do_fchmodat(int dirfd, const char *pathname, mode_t mode, int flags, struct apk_out *out)
+{
+ if (fchmodat(dirfd, pathname, mode & 07777, flags) == 0) return 0;
+ apk_err(out, "Failed to set permissions on %s: %s", pathname, strerror(errno));
+ return -errno;
+}
+
+static int do_fchownat(int dirfd, const char *pathname, uid_t uid, gid_t gid, int flags, struct apk_out *out)
+{
+ if (fchownat(dirfd, pathname, uid, gid, flags) == 0) return 0;
+ apk_err(out, "Failed to set ownership on %s: %s", pathname, strerror(errno));
+ return -errno;
+}
+
+static int fsys_dir_create(struct apk_fsdir *d, mode_t mode, uid_t uid, gid_t gid)
+{
+ const char *dirname = apk_pathbuilder_cstr(&d->pb);
+ if (mkdirat(apk_ctx_fd_dest(d->ac), dirname, mode) < 0) {
+ if (errno != EEXIST) apk_err(&d->ac->out, "Failed to create %s: %s", dirname, strerror(errno));
+ return -errno;
+ }
+ if (d->extract_flags & APK_FSEXTRACTF_NO_CHOWN) return 0;
+ if (do_fchownat(apk_ctx_fd_dest(d->ac), dirname, uid, gid, 0, &d->ac->out) < 0) return -errno;
+ return 0;
+}
+
+static int fsys_dir_delete(struct apk_fsdir *d)
+{
+ if (unlinkat(apk_ctx_fd_dest(d->ac), apk_pathbuilder_cstr(&d->pb), AT_REMOVEDIR) < 0)
+ return -errno;
+ return 0;
+}
+
+static int fsys_dir_check(struct apk_fsdir *d, mode_t mode, uid_t uid, gid_t gid)
+{
+ struct stat st;
+
+ if (fstatat(apk_ctx_fd_dest(d->ac), apk_pathbuilder_cstr(&d->pb), &st, AT_SYMLINK_NOFOLLOW) != 0)
+ return -errno;
+
+ if ((st.st_mode & 07777) != (mode & 07777) || st.st_uid != uid || st.st_gid != gid)
+ return APK_FS_DIR_MODIFIED;
+
+ return 0;
+}
+
+static int fsys_dir_update_perms(struct apk_fsdir *d, mode_t mode, uid_t uid, gid_t gid)
+{
+ int fd = apk_ctx_fd_dest(d->ac), rc = 0, r;
+ const char *dirname = apk_pathbuilder_cstr(&d->pb);
+
+ r = do_fchmodat(fd, dirname, mode, 0, &d->ac->out);
+ if (r) rc = r;
+ if (d->extract_flags & APK_FSEXTRACTF_NO_CHOWN) return rc;
+ r = do_fchownat(fd, dirname, uid, gid, 0, &d->ac->out);
+ if (r) rc = r;
+ return rc;
+}
+
+static const char *format_tmpname(struct apk_digest_ctx *dctx, apk_blob_t pkgctx,
+ apk_blob_t dirname, apk_blob_t fullname, char tmpname[static TMPNAME_MAX])
+{
+ struct apk_digest d;
+ apk_blob_t b = APK_BLOB_PTR_LEN(tmpname, TMPNAME_MAX);
+
+ apk_digest_ctx_reset_alg(dctx, APK_DIGEST_SHA256);
+ apk_digest_ctx_update(dctx, pkgctx.ptr, pkgctx.len);
+ apk_digest_ctx_update(dctx, fullname.ptr, fullname.len);
+ apk_digest_ctx_final(dctx, &d);
+
+ apk_blob_push_blob(&b, dirname);
+ if (dirname.len > 0) {
+ apk_blob_push_blob(&b, APK_BLOB_STR("/.apk."));
+ } else {
+ apk_blob_push_blob(&b, APK_BLOB_STR(".apk."));
+ }
+ apk_blob_push_hexdump(&b, APK_BLOB_PTR_LEN((char *)d.data, 24));
+ apk_blob_push_blob(&b, APK_BLOB_PTR_LEN("", 1));
+
+ return tmpname;
+}
+
+static apk_blob_t get_dirname(const char *fullname)
+{
+ char *slash = strrchr(fullname, '/');
+ if (!slash) return APK_BLOB_NULL;
+ return APK_BLOB_PTR_PTR((char*)fullname, slash);
+}
+
+static int is_system_xattr(const char *name)
+{
+ return strncmp(name, "user.", 5) != 0;
+}
+
+static int fsys_file_extract(struct apk_ctx *ac, const struct apk_file_info *fi, struct apk_istream *is,
+ apk_progress_cb cb, void *cb_ctx, unsigned int extract_flags, apk_blob_t pkgctx)
+{
+ char tmpname_file[TMPNAME_MAX], tmpname_linktarget[TMPNAME_MAX];
+ struct apk_out *out = &ac->out;
+ struct apk_xattr *xattr;
+ int fd, r = -1, atflags = 0, ret = 0;
+ int atfd = apk_ctx_fd_dest(ac);
+ const char *fn = fi->name, *link_target = fi->link_target;
+
+ if (pkgctx.ptr)
+ fn = format_tmpname(&ac->dctx, pkgctx, get_dirname(fn),
+ APK_BLOB_STR(fn), tmpname_file);
+
+ if (!S_ISDIR(fi->mode) && !(extract_flags & APK_FSEXTRACTF_NO_OVERWRITE)) {
+ if (unlinkat(atfd, fn, 0) != 0 && errno != ENOENT) return -errno;
+ }
+
+ switch (fi->mode & S_IFMT) {
+ case S_IFDIR:
+ r = mkdirat(atfd, fn, fi->mode & 07777);
+ if (r < 0 && errno != EEXIST)
+ ret = -errno;
+ break;
+ case S_IFREG:
+ if (!link_target) {
+ int flags = O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC | O_EXCL;
+ int fd = openat(atfd, fn, flags, fi->mode & 07777);
+ if (fd < 0) {
+ ret = -errno;
+ break;
+ }
+ struct apk_ostream *os = apk_ostream_to_fd(fd);
+ if (IS_ERR(os)) {
+ ret = PTR_ERR(os);
+ break;
+ }
+ apk_stream_copy(is, os, fi->size, cb, cb_ctx, 0);
+ r = apk_ostream_close(os);
+ if (r < 0) {
+ unlinkat(atfd, fn, 0);
+ ret = r;
+ }
+ } else {
+ // Hardlink needs to be done against the temporary name
+ if (pkgctx.ptr)
+ link_target = format_tmpname(&ac->dctx, pkgctx, get_dirname(link_target),
+ APK_BLOB_STR(link_target), tmpname_linktarget);
+ r = linkat(atfd, link_target, atfd, fn, 0);
+ if (r < 0) ret = -errno;
+ }
+ break;
+ case S_IFLNK:
+ r = symlinkat(link_target, atfd, fn);
+ if (r < 0) ret = -errno;
+ atflags |= AT_SYMLINK_NOFOLLOW;
+ break;
+ case S_IFBLK:
+ case S_IFCHR:
+ case S_IFIFO:
+ r = mknodat(atfd, fn, fi->mode, fi->device);
+ if (r < 0) ret = -errno;
+ break;
+ }
+ if (ret) {
+ apk_err(out, "Failed to create %s: %s", fi->name, strerror(-ret));
+ return ret;
+ }
+
+ if (!(extract_flags & APK_FSEXTRACTF_NO_CHOWN)) {
+ r = do_fchownat(atfd, fn, fi->uid, fi->gid, atflags, out);
+ if (!ret && r) ret = r;
+
+ /* chown resets suid bit so we need set it again */
+ if (fi->mode & 07000) {
+ r = do_fchmodat(atfd, fn, fi->mode, atflags, out);
+ if (!ret && r) ret = r;
+ }
+ }
+
+ /* extract xattrs */
+ if (!S_ISLNK(fi->mode) && fi->xattrs && fi->xattrs->num) {
+ r = 0;
+ fd = openat(atfd, fn, O_RDWR);
+ if (fd >= 0) {
+ foreach_array_item(xattr, fi->xattrs) {
+ if ((extract_flags & APK_FSEXTRACTF_NO_SYS_XATTRS) && is_system_xattr(xattr->name))
+ continue;
+ if (apk_fsetxattr(fd, xattr->name, xattr->value.ptr, xattr->value.len) < 0) {
+ r = -errno;
+ if (r != -ENOTSUP) break;
+ }
+ }
+ close(fd);
+ } else {
+ r = -errno;
+ }
+ if (r) {
+ if (r != -ENOTSUP)
+ apk_err(out, "Failed to set xattrs on %s: %s",
+ fn, strerror(-r));
+ if (!ret) ret = r;
+ }
+ }
+
+ if (!S_ISLNK(fi->mode)) {
+ /* preserve modification time */
+ struct timespec times[2];
+
+ times[0].tv_sec = times[1].tv_sec = fi->mtime;
+ times[0].tv_nsec = times[1].tv_nsec = 0;
+ r = utimensat(atfd, fn, times, atflags);
+ if (r < 0) {
+ apk_err(out, "Failed to preserve modification time on %s: %s",
+ fn, strerror(errno));
+ if (!ret || ret == -ENOTSUP) ret = -errno;
+ }
+ }
+
+ return ret;
+}
+
+static int fsys_file_control(struct apk_fsdir *d, apk_blob_t filename, int ctrl)
+{
+ struct apk_ctx *ac = d->ac;
+ char tmpname[TMPNAME_MAX], apknewname[TMPNAME_MAX];
+ const char *fn;
+ int n, rc = 0, atfd = apk_ctx_fd_dest(d->ac);
+ apk_blob_t dirname = apk_pathbuilder_get(&d->pb);
+
+ n = apk_pathbuilder_pushb(&d->pb, filename);
+ fn = apk_pathbuilder_cstr(&d->pb);
+
+ switch (ctrl) {
+ case APK_FS_CTRL_COMMIT:
+ // rename tmpname -> realname
+ if (renameat(atfd, format_tmpname(&ac->dctx, d->pkgctx, dirname, apk_pathbuilder_get(&d->pb), tmpname),
+ atfd, fn) < 0)
+ rc = -errno;
+ break;
+ case APK_FS_CTRL_APKNEW:
+ // rename tmpname -> realname.apk-new
+ snprintf(apknewname, sizeof apknewname, "%s%s", fn, ".apk-new");
+ if (renameat(atfd, format_tmpname(&ac->dctx, d->pkgctx, dirname, apk_pathbuilder_get(&d->pb), tmpname),
+ atfd, apknewname) < 0)
+ rc = -errno;
+ break;
+ case APK_FS_CTRL_CANCEL:
+ // unlink tmpname
+ if (unlinkat(atfd, format_tmpname(&ac->dctx, d->pkgctx, dirname, apk_pathbuilder_get(&d->pb), tmpname), 0) < 0)
+ rc = -errno;
+ break;
+ case APK_FS_CTRL_DELETE:
+ // unlink realname
+ if (unlinkat(atfd, fn, 0) < 0)
+ rc = -errno;
+ break;
+ default:
+ rc = -ENOSYS;
+ break;
+ }
+
+ apk_pathbuilder_pop(&d->pb, n);
+ return rc;
+}
+
+static int fsys_file_info(struct apk_fsdir *d, apk_blob_t filename,
+ unsigned int flags, struct apk_file_info *fi)
+{
+ struct apk_ctx *ac = d->ac;
+ int n, r;
+
+ n = apk_pathbuilder_pushb(&d->pb, filename);
+ r = apk_fileinfo_get(apk_ctx_fd_dest(ac), apk_pathbuilder_cstr(&d->pb), flags, fi, &ac->db->atoms);
+ apk_pathbuilder_pop(&d->pb, n);
+ return r;
+}
+
+static const struct apk_fsdir_ops fsdir_ops_fsys = {
+ .priority = APK_FS_PRIO_DISK,
+ .dir_create = fsys_dir_create,
+ .dir_delete = fsys_dir_delete,
+ .dir_check = fsys_dir_check,
+ .dir_update_perms = fsys_dir_update_perms,
+ .file_extract = fsys_file_extract,
+ .file_control = fsys_file_control,
+ .file_info = fsys_file_info,
+};
+
+static const struct apk_fsdir_ops *apk_fsops_get(apk_blob_t dir)
+{
+ if (dir.len >= 4 && memcmp(dir.ptr, "uvol", 4) == 0 && (dir.len == 4 || dir.ptr[4] == '/')) {
+ extern const struct apk_fsdir_ops fsdir_ops_uvol;
+ return &fsdir_ops_uvol;
+ }
+
+ return &fsdir_ops_fsys;
+}
+
+int apk_fs_extract(struct apk_ctx *ac, const struct apk_file_info *fi, struct apk_istream *is,
+ apk_progress_cb cb, void *cb_ctx, unsigned int extract_flags, apk_blob_t pkgctx)
+{
+ if (S_ISDIR(fi->mode)) {
+ struct apk_fsdir fsd;
+ apk_fsdir_get(&fsd, APK_BLOB_STR((char*)fi->name), extract_flags, ac, pkgctx);
+ return apk_fsdir_create(&fsd, fi->mode, fi->uid, fi->gid);
+ } else {
+ const struct apk_fsdir_ops *ops = apk_fsops_get(APK_BLOB_PTR_LEN((char*)fi->name, strnlen(fi->name, 5)));
+ return ops->file_extract(ac, fi, is, cb, cb_ctx, extract_flags, pkgctx);
+ }
+}
+
+void apk_fsdir_get(struct apk_fsdir *d, apk_blob_t dir, unsigned int extract_flags, struct apk_ctx *ac, apk_blob_t pkgctx)
+{
+ d->ac = ac;
+ d->pkgctx = pkgctx;
+ d->extract_flags = extract_flags;
+ d->ops = apk_fsops_get(dir);
+ apk_pathbuilder_setb(&d->pb, dir);
+}
diff --git a/src/fs_uvol.c b/src/fs_uvol.c
new file mode 100644
index 0000000..50e368e
--- /dev/null
+++ b/src/fs_uvol.c
@@ -0,0 +1,170 @@
+/* fsops_uvol.c - Alpine Package Keeper (APK)
+ *
+ * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
+ * Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#include <spawn.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "apk_context.h"
+#include "apk_fs.h"
+
+static int uvol_run(struct apk_ctx *ac, char *action, const char *volname, char *arg1, char *arg2)
+{
+ char buf[APK_EXIT_STATUS_MAX_SIZE];
+ struct apk_out *out = &ac->out;
+ pid_t pid;
+ int r, status;
+ char *argv[] = { (char*)apk_ctx_get_uvol(ac), action, (char*) volname, arg1, arg2, 0 };
+ posix_spawn_file_actions_t act;
+
+ posix_spawn_file_actions_init(&act);
+ posix_spawn_file_actions_addclose(&act, STDIN_FILENO);
+ r = posix_spawn(&pid, apk_ctx_get_uvol(ac), &act, 0, argv, environ);
+ posix_spawn_file_actions_destroy(&act);
+ if (r != 0) {
+ apk_err(out, "%s: uvol run exec error: %s", volname, apk_error_str(r));
+ return r;
+ }
+ while (waitpid(pid, &status, 0) < 0 && errno == EINTR);
+
+ if (apk_exit_status_str(status, buf, sizeof buf)) {
+ apk_err(out, "%s: uvol run %s", volname, buf);
+ return -APKE_UVOL_ERROR;
+ }
+ return 0;
+}
+
+static int uvol_extract(struct apk_ctx *ac, const char *volname, char *arg1, off_t sz,
+ struct apk_istream *is, apk_progress_cb cb, void *cb_ctx)
+{
+ char buf[APK_EXIT_STATUS_MAX_SIZE];
+ struct apk_out *out = &ac->out;
+ struct apk_ostream *os;
+ pid_t pid;
+ int r, status, pipefds[2];
+ char *argv[] = { (char*)apk_ctx_get_uvol(ac), "write", (char*) volname, arg1, 0 };
+ posix_spawn_file_actions_t act;
+
+ if (pipe2(pipefds, O_CLOEXEC) != 0) return -errno;
+
+ posix_spawn_file_actions_init(&act);
+ posix_spawn_file_actions_adddup2(&act, pipefds[0], STDIN_FILENO);
+ r = posix_spawn(&pid, apk_ctx_get_uvol(ac), &act, 0, argv, environ);
+ posix_spawn_file_actions_destroy(&act);
+ if (r != 0) {
+ apk_err(out, "%s: uvol exec error: %s", volname, apk_error_str(r));
+ return r;
+ }
+ close(pipefds[0]);
+ os = apk_ostream_to_fd(pipefds[1]);
+ apk_stream_copy(is, os, sz, cb, cb_ctx, 0);
+ r = apk_ostream_close(os);
+ if (r != 0) {
+ if (r >= 0) r = -APKE_UVOL_ERROR;
+ apk_err(out, "%s: uvol write error: %s", volname, apk_error_str(r));
+ return r;
+ }
+
+ while (waitpid(pid, &status, 0) < 0 && errno == EINTR);
+
+ if (apk_exit_status_str(status, buf, sizeof buf)) {
+ apk_err(out, "%s: uvol extract %s", volname, buf);
+ return -APKE_UVOL_ERROR;
+ }
+ return 0;
+}
+
+static int uvol_dir_create(struct apk_fsdir *d, mode_t mode, uid_t uid, gid_t gid)
+{
+ return 0;
+}
+
+static int uvol_dir_delete(struct apk_fsdir *d)
+{
+ return 0;
+}
+
+static int uvol_dir_check(struct apk_fsdir *d, mode_t mode, uid_t uid, gid_t gid)
+{
+ return 0;
+}
+
+static int uvol_dir_update_perms(struct apk_fsdir *d, mode_t mode, uid_t uid, gid_t gid)
+{
+ return 0;
+}
+
+static int uvol_file_extract(struct apk_ctx *ac, const struct apk_file_info *fi, struct apk_istream *is,
+ apk_progress_cb cb, void *cb_ctx, unsigned int extract_flags, apk_blob_t pkgctx)
+{
+ char size[64];
+ const char *uvol_name;
+ int r;
+
+ if (IS_ERR(ac->uvol)) return PTR_ERR(ac->uvol);
+
+ uvol_name = strrchr(fi->name, '/');
+ uvol_name = uvol_name ? uvol_name + 1 : fi->name;
+
+ snprintf(size, sizeof size, "%ju", (intmax_t) fi->size);
+ r = uvol_run(ac, "create", uvol_name, size, "ro");
+ if (r != 0) return r;
+
+ r = uvol_extract(ac, uvol_name, size, fi->size, is, cb, cb_ctx);
+ if (r == 0 && !pkgctx.ptr)
+ r = uvol_run(ac, "up", uvol_name, 0, 0);
+
+ if (r != 0) uvol_run(ac, "remove", uvol_name, 0, 0);
+
+ return r;
+}
+
+static int uvol_file_control(struct apk_fsdir *d, apk_blob_t filename, int ctrl)
+{
+ struct apk_ctx *ac = d->ac;
+ struct apk_pathbuilder pb;
+ const char *uvol_name;
+ int r;
+
+ if (IS_ERR(ac->uvol)) return PTR_ERR(ac->uvol);
+
+ apk_pathbuilder_setb(&pb, filename);
+ uvol_name = apk_pathbuilder_cstr(&pb);
+
+ switch (ctrl) {
+ case APK_FS_CTRL_COMMIT:
+ return uvol_run(ac, "up", uvol_name, 0, 0);
+ case APK_FS_CTRL_APKNEW:
+ case APK_FS_CTRL_CANCEL:
+ case APK_FS_CTRL_DELETE:
+ r = uvol_run(ac, "down", uvol_name, 0, 0);
+ if (r)
+ return r;
+ return uvol_run(ac, "remove", uvol_name, 0, 0);
+ default:
+ return -APKE_UVOL_ERROR;
+ }
+}
+
+static int uvol_file_info(struct apk_fsdir *d, apk_blob_t filename, unsigned int flags, struct apk_file_info *fi)
+{
+ return -APKE_UVOL_ERROR;
+}
+
+const struct apk_fsdir_ops fsdir_ops_uvol = {
+ .priority = APK_FS_PRIO_UVOL,
+ .dir_create = uvol_dir_create,
+ .dir_delete = uvol_dir_delete,
+ .dir_check = uvol_dir_check,
+ .dir_update_perms = uvol_dir_update_perms,
+ .file_extract = uvol_file_extract,
+ .file_control = uvol_file_control,
+ .file_info = uvol_file_info,
+};
diff --git a/src/genhelp.lua b/src/genhelp.lua
index 217ec1b..43910ed 100644
--- a/src/genhelp.lua
+++ b/src/genhelp.lua
@@ -139,7 +139,7 @@ function scdoc:parse_default(ln)
ln = ln:gsub("([^\\])%*(.-[^\\])%*", "%1%2")
ln = ln:gsub("^%*(.-[^\\])%*", "%1")
ln = ln:gsub("([^\\])_(.-[^\\])_", function(a,s) return a..s:upper() end)
- ln = ln:gsub("^_(.-[^\\])_", function(a,s) return a..s:upper() end)
+ ln = ln:gsub("^_(.-[^\\])_", function(s) return s:upper() end)
ln = ln:gsub("\\", "")
self:section_text(ln)
@@ -242,7 +242,11 @@ function scdoc:render(out)
table.insert(out, "\0")
end
+local do_compress = true
local function compress(data)
+ if not do_compress then
+ return data
+ end
local zlib = require 'zlib'
local level = 9
if type(zlib.version()) == "string" then
@@ -258,8 +262,9 @@ local function dump_compressed_vars(name, data, header)
local width = 16
local cout = compress(data)
if header then print(header) end
- print(("static const unsigned int uncompressed_%s_size = %d;"):format(name, #data))
- print(("static const unsigned char compressed_%s[] = { /* %d bytes */"):format(name, #cout))
+ if do_compress then print("#define COMPRESSED_HELP") end
+ print(("static const unsigned int payload_%s_size = %d;"):format(name, #data))
+ print(("static const unsigned char payload_%s[] = { /* %d bytes */"):format(name, #cout))
for i = 1, #cout do
if i % width == 1 then
io.write("\t")
@@ -275,17 +280,21 @@ end
local f = {}
for _, fn in ipairs(arg) do
- doc = setmetatable({
- width = 78,
- section = "HEADER",
- usage = {},
- description = {},
- commands = {},
- notes = {},
- optgroup = {},
- }, scdoc)
- doc:parse(fn)
- table.insert(f, doc)
+ if fn == '--no-zlib' then
+ do_compress = false
+ else
+ doc = setmetatable({
+ width = 78,
+ section = "HEADER",
+ usage = {},
+ description = {},
+ commands = {},
+ notes = {},
+ optgroup = {},
+ }, scdoc)
+ doc:parse(fn)
+ table.insert(f, doc)
+ end
end
table.sort(f, function(a, b) return a.applet < b.applet end)
diff --git a/src/hash.c b/src/hash.c
index 6835ddf..bf8041d 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -84,29 +84,17 @@ void apk_hash_delete_hashed(struct apk_hash *h, apk_blob_t key, unsigned long ha
ptrdiff_t offset = h->ops->node_offset;
apk_hash_node *pos;
apk_hash_item item;
- apk_blob_t itemkey;
+
+ assert(h->ops->compare_item != NULL);
hash %= h->buckets->num;
- if (h->ops->compare_item != NULL) {
- hlist_for_each(pos, &h->buckets->item[hash]) {
- item = ((void *) pos) - offset;
- if (h->ops->compare_item(item, key) == 0) {
- hlist_del(pos, &h->buckets->item[hash]);
- h->ops->delete_item(item);
- h->num_items--;
- break;
- }
- }
- } else {
- hlist_for_each(pos, &h->buckets->item[hash]) {
- item = ((void *) pos) - offset;
- itemkey = h->ops->get_key(item);
- if (h->ops->compare(key, itemkey) == 0) {
- hlist_del(pos, &h->buckets->item[hash]);
- h->ops->delete_item(item);
- h->num_items--;
- break;
- }
+ hlist_for_each(pos, &h->buckets->item[hash]) {
+ item = ((void *) pos) - offset;
+ if (h->ops->compare_item(item, key) == 0) {
+ hlist_del(pos, &h->buckets->item[hash]);
+ h->ops->delete_item(item);
+ h->num_items--;
+ break;
}
}
}
diff --git a/src/io.c b/src/io.c
index 9acf1e6..9afb4d6 100644
--- a/src/io.c
+++ b/src/io.c
@@ -12,19 +12,19 @@
#include <fcntl.h>
#include <endian.h>
#include <unistd.h>
-#include <malloc.h>
#include <dirent.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/stat.h>
-#include <sys/xattr.h>
#include <pwd.h>
#include <grp.h>
+#include <limits.h>
#include "apk_defines.h"
#include "apk_io.h"
#include "apk_crypto.h"
+#include "apk_xattr.h"
#if defined(__GLIBC__) || defined(__UCLIBC__)
#define HAVE_FGETPWENT_R
@@ -33,11 +33,27 @@
size_t apk_io_bufsize = 128*1024;
+
static inline int atfd_error(int atfd)
{
return atfd < -1 && atfd != AT_FDCWD;
}
+int apk_make_dirs(int root_fd, const char *dirname, mode_t dirmode, mode_t parentmode)
+{
+ char parentdir[PATH_MAX], *slash;
+
+ if (faccessat(root_fd, dirname, F_OK, 0) == 0) return 0;
+ if (mkdirat(root_fd, dirname, dirmode) == 0) return 0;
+ if (errno != ENOENT || !parentmode) return -1;
+
+ slash = strrchr(dirname, '/');
+ if (!slash || slash == dirname || slash-dirname+1 >= sizeof parentdir) return -1;
+ strlcpy(parentdir, dirname, slash-dirname+1);
+ if (apk_make_dirs(root_fd, parentdir, parentmode, parentmode) < 0) return -1;
+ return mkdirat(root_fd, dirname, dirmode);
+}
+
ssize_t apk_write_fully(int fd, const void *ptr, size_t size)
{
ssize_t i = 0, r;
@@ -77,6 +93,8 @@ ssize_t apk_istream_read_max(struct apk_istream *is, void *ptr, size_t size)
{
ssize_t left = size, r = 0;
+ if (is->err < 0) return is->err;
+
while (left) {
if (is->ptr != is->end) {
r = min(left, is->end - is->ptr);
@@ -140,6 +158,8 @@ void *apk_istream_peek(struct apk_istream *is, size_t len)
{
int r;
+ if (is->err < 0) return ERR_PTR(is->err);
+
do {
if (is->end - is->ptr >= len) {
void *ptr = is->ptr;
@@ -154,7 +174,7 @@ void *apk_istream_peek(struct apk_istream *is, size_t len)
void *apk_istream_get(struct apk_istream *is, size_t len)
{
void *p = apk_istream_peek(is, len);
- if (!IS_ERR_OR_NULL(p)) is->ptr += len;
+ if (!IS_ERR(p)) is->ptr += len;
else apk_istream_error(is, PTR_ERR(p));
return p;
}
@@ -174,7 +194,7 @@ int apk_istream_get_max(struct apk_istream *is, size_t max, apk_blob_t *data)
int apk_istream_get_delim(struct apk_istream *is, apk_blob_t token, apk_blob_t *data)
{
apk_blob_t ret = APK_BLOB_NULL, left = APK_BLOB_NULL;
- int r;
+ int r = 0;
do {
if (apk_blob_split(APK_BLOB_PTR_LEN((char*)is->ptr, is->end - is->ptr), token, &ret, &left))
@@ -302,6 +322,67 @@ struct apk_istream *apk_istream_segment(struct apk_segment_istream *sis, struct
return &sis->is;
}
+static void digest_get_meta(struct apk_istream *is, struct apk_file_meta *meta)
+{
+ struct apk_digest_istream *dis = container_of(is, struct apk_digest_istream, is);
+ return apk_istream_get_meta(dis->pis, meta);
+}
+
+static ssize_t digest_read(struct apk_istream *is, void *ptr, size_t size)
+{
+ struct apk_digest_istream *dis = container_of(is, struct apk_digest_istream, is);
+ ssize_t r;
+
+ r = dis->pis->ops->read(dis->pis, ptr, size);
+ if (r > 0) {
+ apk_digest_ctx_update(&dis->dctx, ptr, r);
+ dis->size_left -= r;
+ }
+ return r;
+}
+
+static int digest_close(struct apk_istream *is)
+{
+ struct apk_digest_istream *dis = container_of(is, struct apk_digest_istream, is);
+
+ if (dis->digest && dis->size_left == 0) {
+ struct apk_digest res;
+ apk_digest_ctx_final(&dis->dctx, &res);
+ if (apk_digest_cmp(&res, dis->digest) != 0)
+ apk_istream_error(is, -APKE_FILE_INTEGRITY);
+ dis->digest = 0;
+ }
+ apk_digest_ctx_free(&dis->dctx);
+
+ return is->err < 0 ? is->err : 0;
+}
+
+static const struct apk_istream_ops digest_istream_ops = {
+ .get_meta = digest_get_meta,
+ .read = digest_read,
+ .close = digest_close,
+};
+
+struct apk_istream *apk_istream_verify(struct apk_digest_istream *dis, struct apk_istream *is, off_t size, struct apk_digest *d)
+{
+ *dis = (struct apk_digest_istream) {
+ .is.ops = &digest_istream_ops,
+ .is.buf = is->buf,
+ .is.buf_size = is->buf_size,
+ .is.ptr = is->ptr,
+ .is.end = is->end,
+ .pis = is,
+ .digest = d,
+ .size_left = size,
+ };
+ apk_digest_ctx_init(&dis->dctx, d->alg);
+ if (dis->is.ptr != dis->is.end) {
+ apk_digest_ctx_update(&dis->dctx, dis->is.ptr, dis->is.end - dis->is.ptr);
+ dis->size_left -= dis->is.end - dis->is.ptr;
+ }
+ return &dis->is;
+}
+
struct apk_tee_istream {
struct apk_istream is;
struct apk_istream *inner_is;
@@ -401,9 +482,12 @@ struct apk_istream *apk_istream_tee(struct apk_istream *from, struct apk_ostream
err_free:
free(tee);
err:
- if (!IS_ERR(to)) apk_ostream_close(to);
- if (!IS_ERR(from) && (flags & APK_ISTREAM_TEE_OPTIONAL))
- return from;
+ if (!IS_ERR(to)) {
+ apk_ostream_cancel(to, r);
+ apk_ostream_close(to);
+ }
+ if (IS_ERR(from)) return ERR_CAST(from);
+ if (flags & APK_ISTREAM_TEE_OPTIONAL) return from;
return ERR_PTR(apk_istream_close_error(from, r));
}
@@ -529,6 +613,13 @@ struct apk_istream *apk_istream_from_fd(int fd)
return &fis->is;
}
+struct apk_istream *apk_istream_from_fd_url_if_modified(int atfd, const char *url, time_t since)
+{
+ const char *fn = apk_url_local_file(url);
+ if (fn != NULL) return apk_istream_from_file(atfd, fn);
+ return apk_io_url_istream(url, since);
+}
+
struct apk_istream *__apk_istream_from_file(int atfd, const char *file, int try_mmap)
{
int fd;
@@ -540,7 +631,7 @@ struct apk_istream *__apk_istream_from_file(int atfd, const char *file, int try_
if (try_mmap) {
struct apk_istream *is = apk_mmap_istream_from_fd(fd);
- if (!IS_ERR_OR_NULL(is)) return is;
+ if (!IS_ERR(is)) return is;
}
return apk_istream_from_fd(fd);
}
@@ -574,50 +665,59 @@ ssize_t apk_stream_copy(struct apk_istream *is, struct apk_ostream *os, size_t s
return done;
}
-apk_blob_t apk_blob_from_istream(struct apk_istream *is, size_t size)
+int apk_blob_from_istream(struct apk_istream *is, size_t size, apk_blob_t *b)
{
void *ptr;
+ int r;
+
+ *b = APK_BLOB_NULL;
ptr = malloc(size);
- if (ptr == NULL)
- return APK_BLOB_NULL;
+ if (!ptr) return -ENOMEM;
- if (apk_istream_read(is, ptr, size) < 0) {
+ r = apk_istream_read(is, ptr, size);
+ if (r < 0) {
free(ptr);
- return APK_BLOB_NULL;
+ return r;
}
- return APK_BLOB_PTR_LEN(ptr, size);
+ *b = APK_BLOB_PTR_LEN(ptr, size);
+ return r;
}
-apk_blob_t apk_blob_from_file(int atfd, const char *file)
+int apk_blob_from_file(int atfd, const char *file, apk_blob_t *b)
{
- int fd;
struct stat st;
char *buf;
+ ssize_t n;
+ int fd;
- if (atfd_error(atfd)) return APK_BLOB_NULL;
+ *b = APK_BLOB_NULL;
- fd = openat(atfd, file, O_RDONLY | O_CLOEXEC);
- if (fd < 0)
- return APK_BLOB_NULL;
+ if (atfd_error(atfd)) return atfd;
- if (fstat(fd, &st) < 0)
- goto err_fd;
+ fd = openat(atfd, file, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) goto err;
+ if (fstat(fd, &st) < 0) goto err_fd;
buf = malloc(st.st_size);
- if (buf == NULL)
- goto err_fd;
+ if (!buf) goto err_fd;
- if (read(fd, buf, st.st_size) != st.st_size)
+ n = read(fd, buf, st.st_size);
+ if (n != st.st_size) {
+ if (n >= 0) errno = EIO;
goto err_read;
+ }
close(fd);
- return APK_BLOB_PTR_LEN(buf, st.st_size);
+ *b = APK_BLOB_PTR_LEN(buf, st.st_size);
+ return 0;
+
err_read:
free(buf);
err_fd:
close(fd);
- return APK_BLOB_NULL;
+err:
+ return -errno;
}
int apk_blob_to_file(int atfd, const char *file, apk_blob_t b, unsigned int flags)
@@ -691,7 +791,7 @@ void apk_fileinfo_hash_xattr(struct apk_file_info *fi, uint8_t alg)
int apk_fileinfo_get(int atfd, const char *filename, unsigned int flags,
struct apk_file_info *fi, struct apk_atom_pool *atoms)
{
- struct stat64 st;
+ struct stat st;
unsigned int hash_alg = flags & 0xff;
unsigned int xattr_hash_alg = (flags >> 8) & 0xff;
int atflags = 0;
@@ -702,7 +802,7 @@ int apk_fileinfo_get(int atfd, const char *filename, unsigned int flags,
if (flags & APK_FI_NOFOLLOW)
atflags |= AT_SYMLINK_NOFOLLOW;
- if (fstatat64(atfd, filename, &st, atflags) != 0)
+ if (fstatat(atfd, filename, &st, atflags) != 0)
return -errno;
*fi = (struct apk_file_info) {
@@ -714,7 +814,7 @@ int apk_fileinfo_get(int atfd, const char *filename, unsigned int flags,
.device = st.st_rdev,
};
- if (xattr_hash_alg != APK_DIGEST_NONE) {
+ if (xattr_hash_alg != APK_DIGEST_NONE && !S_ISLNK(fi->mode)) {
ssize_t len, vlen;
int fd, i, r;
char val[1024], buf[1024];
@@ -722,12 +822,12 @@ int apk_fileinfo_get(int atfd, const char *filename, unsigned int flags,
r = 0;
fd = openat(atfd, filename, O_RDONLY);
if (fd >= 0) {
- len = flistxattr(fd, buf, sizeof(buf));
+ len = apk_flistxattr(fd, buf, sizeof(buf));
if (len > 0) {
struct apk_xattr_array *xattrs = NULL;
apk_xattr_array_init(&xattrs);
for (i = 0; i < len; i += strlen(&buf[i]) + 1) {
- vlen = fgetxattr(fd, &buf[i], val, sizeof(val));
+ vlen = apk_fgetxattr(fd, &buf[i], val, sizeof(val));
if (vlen < 0) {
r = errno;
if (r == ENODATA) continue;
@@ -752,16 +852,14 @@ int apk_fileinfo_get(int atfd, const char *filename, unsigned int flags,
/* Checksum file content */
if ((flags & APK_FI_NOFOLLOW) && S_ISLNK(st.st_mode)) {
- char *target = alloca(st.st_size);
- if (target == NULL)
- return -ENOMEM;
+ char target[PATH_MAX];
+ if (st.st_size > sizeof target) return -ENOMEM;
if (readlinkat(atfd, filename, target, st.st_size) < 0)
return -errno;
-
apk_digest_calc(&fi->digest, hash_alg, target, st.st_size);
} else {
struct apk_istream *is = apk_istream_from_file(atfd, filename);
- if (!IS_ERR_OR_NULL(is)) {
+ if (!IS_ERR(is)) {
struct apk_digest_ctx dctx;
apk_blob_t blob;
@@ -857,12 +955,11 @@ static int fdo_write(struct apk_ostream *os, const void *ptr, size_t size)
if (size + fos->bytes >= sizeof(fos->buffer)) {
r = fdo_flush(fos);
- if (r != 0)
- return r;
+ if (r != 0) return r;
if (size >= sizeof(fos->buffer) / 2) {
r = apk_write_fully(fos->fd, ptr, size);
- if (r != size) apk_ostream_cancel(&fos->os, r < 0 ? r : -ENOSPC);
- return r;
+ if (r == size) return 0;
+ return apk_ostream_cancel(&fos->os, r < 0 ? r : -ENOSPC);
}
}
@@ -940,7 +1037,7 @@ struct apk_ostream *apk_ostream_to_file(int atfd, const char *file, mode_t mode)
if (fd < 0) return ERR_PTR(-errno);
os = apk_ostream_to_fd(fd);
- if (IS_ERR_OR_NULL(os)) return ERR_CAST(os);
+ if (IS_ERR(os)) return ERR_CAST(os);
struct apk_fd_ostream *fos = container_of(os, struct apk_fd_ostream, os);
fos->file = file;
@@ -1073,6 +1170,19 @@ static struct cache_item *idcache_by_id(struct apk_id_hash *hash, unsigned long
return 0;
}
+const char *apk_url_local_file(const char *url)
+{
+ if (strncmp(url, "file:", 5) == 0)
+ return &url[5];
+
+ if (strncmp(url, "http:", 5) != 0 &&
+ strncmp(url, "https:", 6) != 0 &&
+ strncmp(url, "ftp:", 4) != 0)
+ return url;
+
+ return NULL;
+}
+
void apk_id_cache_init(struct apk_id_cache *idc, int root_fd)
{
idc->root_fd = root_fd;
@@ -1123,8 +1233,11 @@ static void idcache_load_users(int root_fd, struct apk_id_hash *idh)
do {
#ifdef HAVE_FGETPWENT_R
fgetpwent_r(in, &pwent, buf, sizeof(buf), &pwd);
-#else
+#elif !defined(__APPLE__)
pwd = fgetpwent(in);
+#else
+# warning macOS does not support nested /etc/passwd databases, using system one.
+ pwd = getpwent();
#endif
if (!pwd) break;
idcache_add(idh, APK_BLOB_STR(pwd->pw_name), pwd->pw_uid);
@@ -1153,8 +1266,11 @@ static void idcache_load_groups(int root_fd, struct apk_id_hash *idh)
do {
#ifdef HAVE_FGETGRENT_R
fgetgrent_r(in, &grent, buf, sizeof(buf), &grp);
-#else
+#elif !defined(__APPLE__)
grp = fgetgrent(in);
+#else
+# warning macOS does not support nested /etc/group databases, using system one.
+ grp = getgrent();
#endif
if (!grp) break;
idcache_add(idh, APK_BLOB_STR(grp->gr_name), grp->gr_gid);
@@ -1170,7 +1286,9 @@ uid_t apk_id_cache_resolve_uid(struct apk_id_cache *idc, apk_blob_t username, ui
struct cache_item *ci;
idcache_load_users(idc->root_fd, &idc->uid_cache);
ci = idcache_by_name(&idc->uid_cache, username);
- return ci ? ci->id : default_uid;
+ if (ci) return ci->id;
+ if (!apk_blob_compare(username, APK_BLOB_STRLIT("root"))) return 0;
+ return default_uid;
}
gid_t apk_id_cache_resolve_gid(struct apk_id_cache *idc, apk_blob_t groupname, gid_t default_gid)
@@ -1178,7 +1296,9 @@ gid_t apk_id_cache_resolve_gid(struct apk_id_cache *idc, apk_blob_t groupname, g
struct cache_item *ci;
idcache_load_groups(idc->root_fd, &idc->gid_cache);
ci = idcache_by_name(&idc->gid_cache, groupname);
- return ci ? ci->id : default_gid;
+ if (ci) return ci->id;
+ if (!apk_blob_compare(groupname, APK_BLOB_STRLIT("root"))) return 0;
+ return default_gid;
}
apk_blob_t apk_id_cache_resolve_user(struct apk_id_cache *idc, uid_t uid)
@@ -1186,7 +1306,9 @@ apk_blob_t apk_id_cache_resolve_user(struct apk_id_cache *idc, uid_t uid)
struct cache_item *ci;
idcache_load_users(idc->root_fd, &idc->uid_cache);
ci = idcache_by_id(&idc->uid_cache, uid);
- return ci ? APK_BLOB_PTR_LEN(ci->name, ci->len) : APK_BLOB_STRLIT("nobody");
+ if (ci) return APK_BLOB_PTR_LEN(ci->name, ci->len);
+ if (uid == 0) return APK_BLOB_STRLIT("root");
+ return APK_BLOB_STRLIT("nobody");
}
apk_blob_t apk_id_cache_resolve_group(struct apk_id_cache *idc, gid_t gid)
@@ -1194,5 +1316,7 @@ apk_blob_t apk_id_cache_resolve_group(struct apk_id_cache *idc, gid_t gid)
struct cache_item *ci;
idcache_load_groups(idc->root_fd, &idc->gid_cache);
ci = idcache_by_id(&idc->gid_cache, gid);
- return ci ? APK_BLOB_PTR_LEN(ci->name, ci->len) : APK_BLOB_STRLIT("nobody");
+ if (ci) return APK_BLOB_PTR_LEN(ci->name, ci->len);
+ if (gid == 0) return APK_BLOB_STRLIT("root");
+ return APK_BLOB_STRLIT("nobody");
}
diff --git a/src/io_gunzip.c b/src/io_gunzip.c
index 4ad0b6c..b2dbb34 100644
--- a/src/io_gunzip.c
+++ b/src/io_gunzip.c
@@ -10,7 +10,6 @@
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
-#include <malloc.h>
#include <zlib.h>
#include "apk_defines.h"
@@ -37,11 +36,32 @@ static int gzi_boundary_change(struct apk_gzip_istream *gis)
{
int r;
+ if (gis->cb && !APK_BLOB_IS_NULL(gis->cbarg)) {
+ r = gis->cb(gis->cbctx, APK_MPART_DATA, gis->cbarg);
+ if (r) return apk_istream_error(&gis->is, r);
+ }
+ gis->cbarg = APK_BLOB_NULL;
+ if (!gis->is.err && gis->zis->err && gis->zs.avail_in == 0) gis->is.err = gis->zis->err;
if (!gis->cb) return 0;
- r = gis->cb(gis->cbctx, gis->is.err ? APK_MPART_END : APK_MPART_BOUNDARY, gis->cbarg);
+ r = gis->cb(gis->cbctx, gis->is.err ? APK_MPART_END : APK_MPART_BOUNDARY, APK_BLOB_NULL);
if (r > 0) r = -ECANCELED;
- if (r != 0) gis->is.err = r;
- return r;
+ return apk_istream_error(&gis->is, r);
+}
+
+static int gzi_read_more(struct apk_gzip_istream *gis)
+{
+ apk_blob_t blob;
+ int r;
+
+ r = apk_istream_get_all(gis->zis, &blob);
+ if (r < 0) {
+ if (r != -APKE_EOF) return apk_istream_error(&gis->is, r);
+ return 0;
+ }
+ gis->zs.avail_in = blob.len;
+ gis->zs.next_in = (void *) blob.ptr;
+ gis->cbprev = blob.ptr;
+ return 0;
}
static ssize_t gzi_read(struct apk_istream *is, void *ptr, size_t size)
@@ -52,55 +72,42 @@ static ssize_t gzi_read(struct apk_istream *is, void *ptr, size_t size)
gis->zs.avail_out = size;
gis->zs.next_out = ptr;
- while (gis->zs.avail_out != 0 && gis->is.err == 0) {
+ while (gis->zs.avail_out != 0 && gis->is.err >= 0) {
if (!APK_BLOB_IS_NULL(gis->cbarg)) {
- if (gzi_boundary_change(gis))
- goto ret;
- gis->cbarg = APK_BLOB_NULL;
+ r = gzi_boundary_change(gis);
+ if (r) return r;
}
- if (gis->zs.avail_in == 0) {
- apk_blob_t blob;
-
- if (gis->cb != NULL && gis->cbprev != NULL &&
- gis->cbprev != gis->zs.next_in) {
- gis->cb(gis->cbctx, APK_MPART_DATA,
- APK_BLOB_PTR_LEN(gis->cbprev,
- (void *)gis->zs.next_in - gis->cbprev));
- }
- r = apk_istream_get_all(gis->zis, &blob);
- gis->cbprev = blob.ptr;
- gis->zs.avail_in = blob.len;
- gis->zs.next_in = (void *) gis->cbprev;
- if (r < 0) {
- if (r == -APKE_EOF) {
- gis->is.err = 1;
- gis->cbarg = APK_BLOB_NULL;
- gzi_boundary_change(gis);
- } else {
- gis->is.err = r;
- }
- goto ret;
+ if (gis->zs.avail_in == 0 && gis->is.err == 0) {
+ if (gis->cb != NULL && gis->cbprev != NULL && gis->cbprev != gis->zs.next_in) {
+ r = gis->cb(gis->cbctx, APK_MPART_DATA,
+ APK_BLOB_PTR_LEN(gis->cbprev, (void *)gis->zs.next_in - gis->cbprev));
+ if (r < 0) return apk_istream_error(&gis->is, r);
+ gis->cbprev = gis->zs.next_in;
}
+ r = gzi_read_more(gis);
+ if (r) return r;
}
r = inflate(&gis->zs, Z_NO_FLUSH);
switch (r) {
case Z_STREAM_END:
- /* Digest the inflated bytes */
- if (gis->zis->err && gis->zs.avail_in == 0)
- gis->is.err = gis->zis->err;
if (gis->cb != NULL) {
- gis->cbarg = APK_BLOB_PTR_LEN(gis->cbprev, (void *) gis->zs.next_in - gis->cbprev);
+ gis->cbarg = APK_BLOB_PTR_LEN(gis->cbprev, (void *) gis->zs.next_in - gis->cbprev);
gis->cbprev = gis->zs.next_in;
}
+ /* Digest the inflated bytes */
+ if (gis->zs.avail_in == 0) {
+ r = gzi_read_more(gis);
+ if (r) return r;
+ }
/* If we hit end of the bitstream (not end
* of just this gzip), we need to do the
* callback here, as we won't be called again.
* For boundaries it should be postponed to not
* be called until next gzip read is started. */
- if (gis->is.err) {
- gzi_boundary_change(gis);
- goto ret;
+ if (gis->zs.avail_in == 0 && gis->zs.avail_out == size) {
+ r = gzi_boundary_change(gis);
+ if (r) return r;
}
inflateEnd(&gis->zs);
if (inflateInit2(&gis->zs, 15+32) != Z_OK)
@@ -109,9 +116,12 @@ static ssize_t gzi_read(struct apk_istream *is, void *ptr, size_t size)
break;
case Z_OK:
break;
+ case Z_BUF_ERROR:
+ /* Happens when input stream is EOF, input buffer is empty,
+ * and we just tried reading a new header. */
+ goto ret;
default:
- gis->is.err = -EIO;
- break;
+ return apk_istream_error(&gis->is, -APKE_FORMAT_INVALID);
}
}
@@ -146,7 +156,7 @@ struct apk_istream *apk_istream_zlib(struct apk_istream *is, int raw, apk_multip
{
struct apk_gzip_istream *gis;
- if (IS_ERR_OR_NULL(is)) return ERR_CAST(is);
+ if (IS_ERR(is)) return ERR_CAST(is);
gis = malloc(sizeof(*gis) + apk_io_bufsize);
if (!gis) goto err;
@@ -227,11 +237,11 @@ static const struct apk_ostream_ops gzip_ostream_ops = {
.close = gzo_close,
};
-struct apk_ostream *apk_ostream_zlib(struct apk_ostream *output, int raw)
+struct apk_ostream *apk_ostream_zlib(struct apk_ostream *output, int raw, uint8_t level)
{
struct apk_gzip_ostream *gos;
- if (IS_ERR_OR_NULL(output)) return ERR_CAST(output);
+ if (IS_ERR(output)) return ERR_CAST(output);
gos = malloc(sizeof(struct apk_gzip_ostream));
if (gos == NULL) goto err;
@@ -241,7 +251,7 @@ struct apk_ostream *apk_ostream_zlib(struct apk_ostream *output, int raw)
.output = output,
};
- if (deflateInit2(&gos->zs, 9, Z_DEFLATED, window_bits(15, raw), 8,
+ if (deflateInit2(&gos->zs, level ?: 9, Z_DEFLATED, window_bits(15, raw), 8,
Z_DEFAULT_STRATEGY) != Z_OK) {
free(gos);
goto err;
diff --git a/src/io_url.c b/src/io_url_libfetch.c
index 3cfc27a..67f9664 100644
--- a/src/io_url.c
+++ b/src/io_url_libfetch.c
@@ -1,4 +1,4 @@
-/* io_url.c - Alpine Package Keeper (APK)
+/* io_url_libfetch.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
@@ -18,19 +18,6 @@
#include "apk_io.h"
-const char *apk_url_local_file(const char *url)
-{
- if (strncmp(url, "file:", 5) == 0)
- return &url[5];
-
- if (strncmp(url, "http:", 5) != 0 &&
- strncmp(url, "https:", 6) != 0 &&
- strncmp(url, "ftp:", 4) != 0)
- return url;
-
- return NULL;
-}
-
struct apk_fetch_istream {
struct apk_istream is;
fetchIO *fetchIO;
@@ -52,7 +39,7 @@ static int fetch_maperror(int ec)
/* [FETCH_OK] = , */
[FETCH_PROTO] = -EPROTO,
[FETCH_RESOLV] = -APKE_DNS,
- [FETCH_SERVER] = -EREMOTEIO,
+ [FETCH_SERVER] = -APKE_REMOTE_IO,
[FETCH_TEMP] = -EAGAIN,
[FETCH_TIMEOUT] = -ETIMEDOUT,
[FETCH_UNAVAIL] = -ENOENT,
@@ -101,7 +88,7 @@ static const struct apk_istream_ops fetch_istream_ops = {
.close = fetch_close,
};
-static struct apk_istream *apk_istream_fetch(const char *url, time_t since)
+struct apk_istream *apk_io_url_istream(const char *url, time_t since)
{
struct apk_fetch_istream *fis = NULL;
struct url *u;
@@ -148,9 +135,45 @@ err:
return ERR_PTR(rc);
}
-struct apk_istream *apk_istream_from_fd_url_if_modified(int atfd, const char *url, time_t since)
+static void (*io_url_redirect_callback)(int, const char *);
+
+static void fetch_redirect(int code, const struct url *cur, const struct url *next)
+{
+ char *url;
+
+ switch (code) {
+ case 301: // Moved Permanently
+ case 308: // Permanent Redirect
+ url = fetchStringifyURL(next);
+ io_url_redirect_callback(code, url);
+ free(url);
+ break;
+ }
+}
+
+void apk_io_url_no_check_certificate(void)
+{
+ fetch_no_check_certificate();
+}
+
+void apk_io_url_set_timeout(int timeout)
+{
+ fetchTimeout = timeout;
+}
+
+void apk_io_url_set_redirect_callback(void (*cb)(int, const char *))
+{
+ fetchRedirectMethod = cb ? fetch_redirect : NULL;
+ io_url_redirect_callback = cb;
+}
+
+static void apk_io_url_fini(void)
+{
+ fetchConnectionCacheClose();
+}
+
+void apk_io_url_init(void)
{
- if (apk_url_local_file(url) != NULL)
- return apk_istream_from_file(atfd, apk_url_local_file(url));
- return apk_istream_fetch(url, since);
+ fetchConnectionCacheInit(32, 4);
+ atexit(apk_io_url_fini);
}
diff --git a/src/io_zstd.c b/src/io_zstd.c
new file mode 100644
index 0000000..ea3969e
--- /dev/null
+++ b/src/io_zstd.c
@@ -0,0 +1,258 @@
+/* io_zstd.c - Alpine Package Keeper (APK)
+ *
+ * Copyright (C) 2008-2023 Timo Teräs <timo.teras@iki.fi>
+ * Copyright (C) 2023 q66 <q66@chimera-linux.org>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <zstd.h>
+
+#include "apk_defines.h"
+#include "apk_io.h"
+#include "apk_nproc.h"
+
+struct apk_zstd_istream {
+ struct apk_istream is;
+ struct apk_istream *input;
+ ZSTD_DCtx *ctx;
+ void *buf_in;
+ size_t buf_insize;
+ ZSTD_inBuffer inp;
+ int flush;
+};
+
+static void zi_get_meta(struct apk_istream *input, struct apk_file_meta *meta)
+{
+ struct apk_zstd_istream *is = container_of(input, struct apk_zstd_istream, is);
+ apk_istream_get_meta(is->input, meta);
+}
+
+static ssize_t zi_read(struct apk_istream *input, void *ptr, size_t size)
+{
+ struct apk_zstd_istream *is = container_of(input, struct apk_zstd_istream, is);
+ ZSTD_outBuffer outp;
+
+ outp.dst = ptr;
+ outp.pos = 0;
+ outp.size = size;
+
+ while (outp.pos < outp.size) {
+ size_t zr;
+ if (is->inp.pos >= is->inp.size) {
+ ssize_t rs = apk_istream_read_max(is->input, is->buf_in, is->buf_insize);
+ if (rs < 0) {
+ is->is.err = rs;
+ return outp.pos;
+ } else if (rs == 0 && is->flush == 0) {
+ /* eof but only if we haven't read anything */
+ if (outp.pos == 0) is->is.err = 1;
+ return outp.pos;
+ } else if (rs) {
+ /* got proper input, disregard flush case */
+ is->flush = 0;
+ }
+ is->inp.size = rs;
+ is->inp.pos = 0;
+ }
+ zr = ZSTD_decompressStream(is->ctx, &outp, &is->inp);
+ if (ZSTD_isError(zr)) {
+ is->is.err = -EIO;
+ return outp.pos;
+ }
+ if (is->flush != 0) {
+ is->flush = 0;
+ /* set EOF if there wasn't antyhing left */
+ if (outp.pos == 0) is->is.err = 1;
+ break;
+ }
+ }
+
+ /* if set, next run should try decompress again, even on eof; this
+ * is because there might still be data in the internal buffers as
+ * mentioned in the zstd documentation
+ */
+ if (outp.pos == outp.size) is->flush = 1;
+ return outp.pos;
+}
+
+static int zi_close(struct apk_istream *input)
+{
+ int r;
+ struct apk_zstd_istream *is = container_of(input, struct apk_zstd_istream, is);
+
+ ZSTD_freeDCtx(is->ctx);
+ r = apk_istream_close_error(is->input, is->is.err);
+ free(is);
+ return r;
+}
+
+static const struct apk_istream_ops zstd_istream_ops = {
+ .get_meta = zi_get_meta,
+ .read = zi_read,
+ .close = zi_close,
+};
+
+struct apk_istream *apk_istream_zstd(struct apk_istream *input)
+{
+ struct apk_zstd_istream *is;
+ size_t buf_insize;
+
+ if (IS_ERR(input)) return ERR_CAST(input);
+
+ buf_insize = ZSTD_DStreamInSize();
+
+ is = malloc(sizeof(struct apk_zstd_istream) + apk_io_bufsize + buf_insize);
+ if (is == NULL) goto err;
+
+ is->buf_in = (uint8_t*)(is + 1) + apk_io_bufsize;
+ is->buf_insize = buf_insize;
+ is->inp.size = is->inp.pos = 0;
+ is->inp.src = is->buf_in;
+ is->flush = 0;
+
+ if ((is->ctx = ZSTD_createDCtx()) == NULL) {
+ free(is);
+ goto err;
+ }
+
+ memset(&is->is, 0, sizeof(is->is));
+
+ is->is.ops = &zstd_istream_ops;
+ is->is.buf = (uint8_t*)(is + 1);
+ is->is.buf_size = apk_io_bufsize;
+ is->input = input;
+
+ return &is->is;
+err:
+ return ERR_PTR(apk_istream_close_error(input, -ENOMEM));
+}
+
+struct apk_zstd_ostream {
+ struct apk_ostream os;
+ struct apk_ostream *output;
+ ZSTD_CCtx *ctx;
+ void *buf_out;
+ size_t buf_outsize;
+};
+
+static int zo_write(struct apk_ostream *output, const void *ptr, size_t size)
+{
+ struct apk_zstd_ostream *os = container_of(output, struct apk_zstd_ostream, os);
+ ssize_t r;
+ ZSTD_inBuffer inp = {ptr, size, 0};
+
+ do {
+ ZSTD_outBuffer outp = {os->buf_out, os->buf_outsize, 0};
+ size_t rem = ZSTD_compressStream2(os->ctx, &outp, &inp, ZSTD_e_continue);
+
+ if (ZSTD_isError(rem))
+ return apk_ostream_cancel(os->output, -EIO);
+
+ if (outp.pos != 0) {
+ r = apk_ostream_write(os->output, os->buf_out, outp.pos);
+ if (r < 0) return r;
+ }
+ } while (inp.pos != inp.size);
+
+ return 0;
+}
+
+static int zo_close(struct apk_ostream *output)
+{
+ struct apk_zstd_ostream *os = container_of(output, struct apk_zstd_ostream, os);
+ ZSTD_inBuffer inp = {NULL, 0, 0};
+ size_t rem;
+ int r, rc = output->rc;
+
+ do {
+ ZSTD_outBuffer outp = {os->buf_out, os->buf_outsize, 0};
+ rem = ZSTD_compressStream2(os->ctx, &outp, &inp, ZSTD_e_end);
+
+ if (ZSTD_isError(rem)) break;
+
+ if (outp.pos && apk_ostream_write(os->output, os->buf_out, outp.pos) < 0)
+ break;
+ } while (rem != 0);
+
+ r = apk_ostream_close(os->output);
+ ZSTD_freeCCtx(os->ctx);
+ free(os);
+
+ if (rc) return rc;
+ if (ZSTD_isError(rem)) return 1;
+
+ return r;
+}
+
+static const struct apk_ostream_ops zstd_ostream_ops = {
+ .write = zo_write,
+ .close = zo_close,
+};
+
+struct apk_ostream *apk_ostream_zstd(struct apk_ostream *output, uint8_t level)
+{
+ struct apk_zstd_ostream *os;
+ size_t errc, buf_outsize;
+ int threads;
+ ZSTD_bounds bounds;
+
+ if (IS_ERR(output)) return ERR_CAST(output);
+
+ buf_outsize = ZSTD_CStreamOutSize();
+
+ os = malloc(sizeof(struct apk_zstd_ostream) + buf_outsize);
+ if (os == NULL) goto err;
+
+ os->buf_outsize = buf_outsize;
+ os->buf_out = (uint8_t*)(os + 1);
+
+ if ((os->ctx = ZSTD_createCCtx()) == NULL) {
+ free(os);
+ goto err;
+ }
+
+ threads = apk_get_nproc();
+
+ /* above 6 threads, zstd does not actually seem to perform much or at all
+ * better; it uses the cpu, it uses a disproportionate amount of memory,
+ * but time improvements are marginal at best
+ */
+ if (threads > 6) threads = 6;
+
+ /* constrain the thread count; e.g. static zstd does not support threads
+ * and will return 0 for both bounds, and setting compression level to
+ * any other number would actually fail, so avoid doing that
+ */
+ bounds = ZSTD_cParam_getBounds(ZSTD_c_nbWorkers);
+ if (threads < bounds.lowerBound) threads = bounds.lowerBound;
+ if (threads > bounds.upperBound) threads = bounds.upperBound;
+
+ /* default level is 3 and that's not that useful here */
+ errc = ZSTD_CCtx_setParameter(os->ctx, ZSTD_c_compressionLevel, level ?: 9);
+ if (ZSTD_isError(errc)) {
+ free(os);
+ goto err;
+ }
+
+ errc = ZSTD_CCtx_setParameter(os->ctx, ZSTD_c_nbWorkers, threads);
+ if (ZSTD_isError(errc)) {
+ free(os);
+ goto err;
+ }
+
+ memset(&os->os, 0, sizeof(os->os));
+
+ os->os.ops = &zstd_ostream_ops;
+ os->output = output;
+
+ return &os->os;
+err:
+ apk_ostream_close(output);
+ return ERR_PTR(-ENOMEM);
+}
diff --git a/src/lua-apk.c b/src/lua-apk.c
index 10d34eb..981a63f 100644
--- a/src/lua-apk.c
+++ b/src/lua-apk.c
@@ -7,7 +7,6 @@
* SPDX-License-Identifier: GPL-2.0-only
*/
-#include <features.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
@@ -79,7 +78,7 @@ static int Pversion_compare(lua_State *L)
apk_blob_t a, b;
a = check_blob(L, 1);
b = check_blob(L, 2);
- lua_pushstring(L, apk_version_op_string(apk_version_compare_blob(a, b)));
+ lua_pushstring(L, apk_version_op_string(apk_version_compare(a, b)));
return 1;
}
@@ -91,7 +90,7 @@ static int Pversion_is_less(lua_State *L)
apk_blob_t a, b;
a = check_blob(L, 1);
b = check_blob(L, 2);
- lua_pushboolean(L, apk_version_compare_blob(a, b) == APK_VERSION_LESS);
+ lua_pushboolean(L, apk_version_match(a, APK_VERSION_LESS, b));
return 1;
}
@@ -207,7 +206,6 @@ static int push_package(lua_State *L, struct apk_package *pkg)
set_string_field(L, -3, "url", pkg->url);
set_string_field(L, -3, "license", apk_blob_cstr(*pkg->license));
set_string_field(L, -3, "description", pkg->description);
- set_string_field(L, -3, "filename", pkg->filename);
set_int_field(L, -3, "size", pkg->size);
return 1;
}
diff --git a/src/meson.build b/src/meson.build
index 98b2461..c1aae55 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -12,17 +12,23 @@ libapk_src = [
'commit.c',
'common.c',
'context.c',
+ 'crypto.c',
'crypto_openssl.c',
+ 'ctype.c',
'database.c',
+ 'extract_v2.c',
+ 'extract_v3.c',
+ 'fs_fsys.c',
+ 'fs_uvol.c',
'hash.c',
'io.c',
- 'io_archive.c',
- 'io_url.c',
+ 'io_url_libfetch.c',
'io_gunzip.c',
'package.c',
'pathbuilder.c',
'print.c',
'solver.c',
+ 'tar.c',
'trust.c',
'version.c',
]
@@ -30,20 +36,23 @@ libapk_src = [
libapk_headers = [
'apk_applet.h',
'apk_atom.h',
- 'apk_archive.h',
'apk_blob.h',
'apk_crypto.h',
+ 'apk_crypto_openssl.h',
+ 'apk_ctype.h',
'apk_database.h',
'apk_defines.h',
+ 'apk_extract.h',
+ 'apk_fs.h',
'apk_hash.h',
'apk_io.h',
- 'apk_openssl.h',
'apk_package.h',
'apk_pathbuilder.h',
'apk_print.h',
'apk_provider_data.h',
'apk_solver_data.h',
'apk_solver.h',
+ 'apk_tar.h',
'apk_version.h',
]
@@ -80,13 +89,18 @@ apk_src = [
if lua_bin.found()
genhelp_script = files('genhelp.lua')
+ genhelp_args = [lua_bin, genhelp_script, '@INPUT@']
+
+ if not get_option('compressed-help')
+ genhelp_args += ['--no-zlib']
+ endif
generated_help = custom_target(
'help.h',
capture: true,
output: 'help.h',
input: man_files,
- command: [lua_bin, genhelp_script, '@INPUT@'],
+ command: genhelp_args,
)
else
generated_help = custom_target(
@@ -104,6 +118,21 @@ apk_cargs = [
'-D_ATFILE_SOURCE',
]
+apk_arch_prefix = get_option('arch_prefix')
+if apk_arch_prefix != ''
+ apk_cargs += ['-DAPK_ARCH_PREFIX="@0@"'.format(apk_arch_prefix)]
+endif
+
+apk_uvol_db_target = get_option('uvol_db_target').strip('/')
+if apk_uvol_db_target != ''
+ apk_cargs += ['-DAPK_UVOL_DB_TARGET="@0@"'.format(apk_uvol_db_target)]
+endif
+
+if get_option('zstd')
+ libapk_src += [ 'io_zstd.c' ]
+ apk_cargs += [ '-DHAVE_ZSTD' ]
+endif
+
libapk_shared = shared_library(
'apk',
libapk_src,
@@ -111,6 +140,7 @@ libapk_shared = shared_library(
install: not subproject,
dependencies: [
libfetch_dep,
+ libportability_dep,
shared_deps,
],
c_args: apk_cargs,
@@ -122,6 +152,7 @@ libapk_static = static_library(
install: not subproject,
dependencies: [
libfetch_dep,
+ libportability_dep,
static_deps,
],
c_args: [apk_cargs, '-DOPENSSL_NO_ENGINE'],
@@ -152,9 +183,14 @@ if(lua_dep.found())
libluaapk = library(
'luaapk',
luaapk_src,
- dependencies: [lua_dep, libapk_dep],
+ dependencies: [
+ lua_dep,
+ libapk_dep,
+ shared_deps,
+ libportability_dep.partial_dependency(includes: true),
+ ],
install: true,
- install_dir: lua_dep.get_pkgconfig_variable('libdir'),
+ install_dir: lua_dep.get_variable(pkgconfig: 'libdir'),
c_args: apk_cargs,
)
endif
@@ -167,6 +203,7 @@ apk_exe = executable(
libapk_dep,
shared_deps,
libfetch_dep.partial_dependency(includes: true),
+ libportability_dep.partial_dependency(includes: true),
],
c_args: apk_cargs,
)
@@ -179,6 +216,7 @@ if get_option('static_apk')
dependencies: [
static_deps,
libfetch_dep.partial_dependency(includes: true),
+ libportability_dep.partial_dependency(includes: true),
],
link_with: libapk_static,
c_args: [apk_cargs, '-DOPENSSL_NO_ENGINE'],
diff --git a/src/package.c b/src/package.c
index fd0104a..68d2e60 100644
--- a/src/package.c
+++ b/src/package.c
@@ -12,35 +12,20 @@
#include <ctype.h>
#include <stdio.h>
#include <limits.h>
-#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
-
-#include "apk_openssl.h"
-#include <openssl/pem.h>
+#include <sys/sysmacros.h>
#include "apk_defines.h"
-#include "apk_archive.h"
#include "apk_package.h"
#include "apk_database.h"
+#include "apk_ctype.h"
#include "apk_print.h"
-
-const apk_spn_match_def apk_spn_dependency_comparer = {
- [7] = (1<<4) /*<*/ | (1<<5) /*=*/ | (1<<6) /*<*/,
- [15] = (1<<6) /*~*/
-};
-
-const apk_spn_match_def apk_spn_dependency_separator = {
- [1] = (1<<2) /*\n*/,
- [4] = (1<<0) /* */,
-};
-
-const apk_spn_match_def apk_spn_repotag_separator = {
- [8] = (1<<0) /*@*/
-};
+#include "apk_extract.h"
+#include "apk_adb.h"
struct apk_package *apk_pkg_get_installed(struct apk_name *name)
{
@@ -83,6 +68,7 @@ struct apk_installed_package *apk_pkg_install(struct apk_database *db,
/* Overlay override information resides in a nameless package */
if (pkg->name != NULL) {
+ db->sorted_installed_packages = 0;
db->installed.stats.packages++;
db->installed.stats.bytes += pkg->installed_size;
list_add_tail(&ipkg->installed_pkgs_list,
@@ -102,6 +88,7 @@ void apk_pkg_uninstall(struct apk_database *db, struct apk_package *pkg)
return;
if (db != NULL) {
+ db->sorted_installed_packages = 0;
db->installed.stats.packages--;
db->installed.stats.bytes -= pkg->installed_size;
}
@@ -154,6 +141,33 @@ int apk_pkg_parse_name(apk_blob_t apkname,
return 0;
}
+int apk_dep_parse(apk_blob_t spec, apk_blob_t *name, int *rop, apk_blob_t *version)
+{
+ apk_blob_t bop;
+ int op = 0;
+
+ /* [!]name[[op]ver] */
+ if (APK_BLOB_IS_NULL(spec)) goto fail;
+ if (apk_blob_pull_blob_match(&spec, APK_BLOB_STRLIT("!")))
+ op |= APK_VERSION_CONFLICT;
+ if (apk_blob_cspn(spec, APK_CTYPE_DEPENDENCY_COMPARER, name, &bop)) {
+ if (!apk_blob_spn(bop, APK_CTYPE_DEPENDENCY_COMPARER, &bop, version)) goto fail;
+ op |= apk_version_result_mask_blob(bop);
+ if ((op & ~APK_VERSION_CONFLICT) == 0) goto fail;
+ } else {
+ *name = spec;
+ op |= APK_DEPMASK_ANY;
+ *version = APK_BLOB_NULL;
+ }
+ *rop = op;
+ return 0;
+fail:
+ *name = APK_BLOB_NULL;
+ *version = APK_BLOB_NULL;
+ *rop = APK_DEPMASK_ANY;
+ return -APKE_DEPENDENCY_FORMAT;
+}
+
void apk_deps_add(struct apk_dependency_array **depends, struct apk_dependency *dep)
{
struct apk_dependency *d0;
@@ -189,81 +203,30 @@ void apk_deps_del(struct apk_dependency_array **pdeps, struct apk_name *name)
void apk_blob_pull_dep(apk_blob_t *b, struct apk_database *db, struct apk_dependency *dep)
{
struct apk_name *name;
- apk_blob_t bdep, bname, bop, bver = APK_BLOB_NULL, btag;
- int mask = APK_DEPMASK_ANY, conflict = 0, tag = 0, fuzzy = 0;
-
- /* [!]name[<,<=,<~,=,~,>~,>=,>,><]ver */
- if (APK_BLOB_IS_NULL(*b))
- goto fail;
-
- /* grap one token */
- if (!apk_blob_cspn(*b, apk_spn_dependency_separator, &bdep, NULL))
- bdep = *b;
- b->ptr += bdep.len;
- b->len -= bdep.len;
-
- /* skip also all separator chars */
- if (!apk_blob_spn(*b, apk_spn_dependency_separator, NULL, b)) {
- b->ptr += b->len;
- b->len = 0;
- }
-
- /* parse the version */
- if (bdep.ptr[0] == '!') {
- bdep.ptr++;
- bdep.len--;
- conflict = 1;
- }
-
- if (apk_blob_cspn(bdep, apk_spn_dependency_comparer, &bname, &bop)) {
- int i;
-
- if (mask == 0)
- goto fail;
- if (!apk_blob_spn(bop, apk_spn_dependency_comparer, &bop, &bver))
- goto fail;
- mask = 0;
- for (i = 0; i < bop.len; i++) {
- switch (bop.ptr[i]) {
- case '<':
- mask |= APK_VERSION_LESS;
- break;
- case '>':
- mask |= APK_VERSION_GREATER;
- break;
- case '~':
- mask |= APK_VERSION_FUZZY|APK_VERSION_EQUAL;
- fuzzy = TRUE;
- break;
- case '=':
- mask |= APK_VERSION_EQUAL;
- break;
- }
- }
- if ((mask & APK_DEPMASK_CHECKSUM) != APK_DEPMASK_CHECKSUM &&
- !apk_version_validate(bver))
- goto fail;
- } else {
- bname = bdep;
- bop = APK_BLOB_NULL;
- bver = APK_BLOB_NULL;
- }
-
- if (apk_blob_cspn(bname, apk_spn_repotag_separator, &bname, &btag))
+ apk_blob_t bdep, bname, bver, btag;
+ int op, tag = 0, broken = 0;
+
+ /* grap one token, and skip all separators */
+ if (APK_BLOB_IS_NULL(*b)) goto fail;
+ apk_blob_cspn(*b, APK_CTYPE_DEPENDENCY_SEPARATOR, &bdep, b);
+ apk_blob_spn(*b, APK_CTYPE_DEPENDENCY_SEPARATOR, NULL, b);
+
+ if (apk_dep_parse(bdep, &bname, &op, &bver) != 0) goto fail;
+ if ((op & APK_DEPMASK_CHECKSUM) != APK_DEPMASK_CHECKSUM &&
+ !apk_version_validate(bver)) broken = 1;
+ if (apk_blob_split(bname, APK_BLOB_STRLIT("@"), &bname, &btag))
tag = apk_db_get_tag_id(db, btag);
/* convert to apk_dependency */
name = apk_db_get_name(db, bname);
- if (name == NULL)
- goto fail;
+ if (name == NULL) goto fail;
*dep = (struct apk_dependency){
.name = name,
.version = apk_atomize_dup(&db->atoms, bver),
.repository_tag = tag,
- .result_mask = mask,
- .conflict = conflict,
- .fuzzy = fuzzy,
+ .op = op,
+ .broken = broken,
};
return;
fail:
@@ -271,17 +234,21 @@ fail:
*b = APK_BLOB_NULL;
}
-void apk_blob_pull_deps(apk_blob_t *b, struct apk_database *db, struct apk_dependency_array **deps)
+int apk_blob_pull_deps(apk_blob_t *b, struct apk_database *db, struct apk_dependency_array **deps)
{
struct apk_dependency dep;
+ int rc = 0;
while (b->len > 0) {
apk_blob_pull_dep(b, db, &dep);
- if (APK_BLOB_IS_NULL(*b) || dep.name == NULL)
- break;
-
+ if (APK_BLOB_IS_NULL(*b) || dep.name == NULL) {
+ rc = -APKE_DEPENDENCY_FORMAT;
+ continue;
+ }
+ if (dep.broken) rc = -APKE_PKGVERSION_FORMAT;
*apk_dependency_array_add(deps) = dep;
}
+ return rc;
}
void apk_dep_from_pkg(struct apk_dependency *dep, struct apk_database *db,
@@ -296,7 +263,7 @@ void apk_dep_from_pkg(struct apk_dependency *dep, struct apk_database *db,
*dep = (struct apk_dependency) {
.name = pkg->name,
.version = apk_atomize_dup(&db->atoms, b),
- .result_mask = APK_DEPMASK_CHECKSUM,
+ .op = APK_DEPMASK_CHECKSUM,
};
}
@@ -306,58 +273,27 @@ static const int apk_checksum_compare(const struct apk_checksum *a, const struct
APK_BLOB_PTR_LEN((char *) b->data, b->type));
}
-static int apk_dep_match_checksum(struct apk_dependency *dep, struct apk_package *pkg)
+static int apk_dep_match_checksum(const struct apk_dependency *dep, const struct apk_package *pkg)
{
struct apk_checksum csum;
apk_blob_t b = *dep->version;
apk_blob_pull_csum(&b, &csum);
- if (apk_checksum_compare(&csum, &pkg->csum) == 0)
- return 1;
-
- return 0;
+ return apk_checksum_compare(&csum, &pkg->csum) == 0;
}
-int apk_dep_is_provided(struct apk_dependency *dep, struct apk_provider *p)
+int apk_dep_is_provided(const struct apk_dependency *dep, const struct apk_provider *p)
{
- if (p == NULL || p->pkg == NULL)
- return dep->conflict;
-
- switch (dep->result_mask) {
- case APK_DEPMASK_CHECKSUM:
- return apk_dep_match_checksum(dep, p->pkg);
- case APK_DEPMASK_ANY:
- return !dep->conflict;
- default:
- if (p->version == &apk_atom_null)
- return dep->conflict;
- if (apk_version_compare_blob_fuzzy(*p->version, *dep->version, dep->fuzzy)
- & dep->result_mask)
- return !dep->conflict;
- return dep->conflict;
- }
- return dep->conflict;
+ if (p == NULL || p->pkg == NULL) return apk_dep_conflict(dep);
+ if (dep->op == APK_DEPMASK_CHECKSUM) return apk_dep_match_checksum(dep, p->pkg);
+ return apk_version_match(*p->version, dep->op, *dep->version);
}
-int apk_dep_is_materialized(struct apk_dependency *dep, struct apk_package *pkg)
+int apk_dep_is_materialized(const struct apk_dependency *dep, const struct apk_package *pkg)
{
- if (pkg == NULL)
- return dep->conflict;
- if (dep->name != pkg->name)
- return dep->conflict;
-
- switch (dep->result_mask) {
- case APK_DEPMASK_CHECKSUM:
- return apk_dep_match_checksum(dep, pkg);
- case APK_DEPMASK_ANY:
- return !dep->conflict;
- default:
- if (apk_version_compare_blob_fuzzy(*pkg->version, *dep->version, dep->fuzzy)
- & dep->result_mask)
- return !dep->conflict;
- return dep->conflict;
- }
- return dep->conflict;
+ if (pkg == NULL || dep->name != pkg->name) return apk_dep_conflict(dep);
+ if (dep->op == APK_DEPMASK_CHECKSUM) return apk_dep_match_checksum(dep, pkg);
+ return apk_version_match(*pkg->version, dep->op, *dep->version);
}
int apk_dep_analyze(struct apk_dependency *dep, struct apk_package *pkg)
@@ -394,16 +330,14 @@ char *apk_dep_snprintf(char *buf, size_t n, struct apk_dependency *dep)
void apk_blob_push_dep(apk_blob_t *to, struct apk_database *db, struct apk_dependency *dep)
{
- int result_mask = dep->result_mask;
-
- if (dep->conflict)
+ if (apk_dep_conflict(dep))
apk_blob_push_blob(to, APK_BLOB_PTR_LEN("!", 1));
apk_blob_push_blob(to, APK_BLOB_STR(dep->name->name));
if (dep->repository_tag && db != NULL)
apk_blob_push_blob(to, db->repo_tags[dep->repository_tag].tag);
if (!APK_BLOB_IS_NULL(*dep->version)) {
- apk_blob_push_blob(to, APK_BLOB_STR(apk_version_op_string(result_mask)));
+ apk_blob_push_blob(to, APK_BLOB_STR(apk_version_op_string(dep->op)));
apk_blob_push_blob(to, *dep->version);
}
}
@@ -422,7 +356,7 @@ void apk_blob_push_deps(apk_blob_t *to, struct apk_database *db, struct apk_depe
}
}
-int apk_deps_write(struct apk_database *db, struct apk_dependency_array *deps, struct apk_ostream *os, apk_blob_t separator)
+int apk_deps_write_layer(struct apk_database *db, struct apk_dependency_array *deps, struct apk_ostream *os, apk_blob_t separator, unsigned layer)
{
apk_blob_t blob;
char tmp[256];
@@ -432,9 +366,10 @@ int apk_deps_write(struct apk_database *db, struct apk_dependency_array *deps, s
return 0;
for (i = 0; i < deps->num; i++) {
+ if (layer != -1 && deps->item[i].layer != layer) continue;
+
blob = APK_BLOB_BUF(tmp);
- if (i)
- apk_blob_push_blob(&blob, separator);
+ if (n) apk_blob_push_blob(&blob, separator);
apk_blob_push_dep(&blob, db, &deps->item[i]);
blob = apk_blob_pushed(APK_BLOB_BUF(tmp), blob);
@@ -448,6 +383,38 @@ int apk_deps_write(struct apk_database *db, struct apk_dependency_array *deps, s
return n;
}
+int apk_deps_write(struct apk_database *db, struct apk_dependency_array *deps, struct apk_ostream *os, apk_blob_t separator)
+{
+ return apk_deps_write_layer(db, deps, os, separator, -1);
+}
+
+void apk_dep_from_adb(struct apk_dependency *dep, struct apk_database *db, struct adb_obj *d)
+{
+ int op = adb_ro_int(d, ADBI_DEP_MATCH);
+ apk_blob_t ver = adb_ro_blob(d, ADBI_DEP_VERSION);
+
+ if (APK_BLOB_IS_NULL(ver)) op |= APK_DEPMASK_ANY;
+ else if (op == 0) op = APK_VERSION_EQUAL;
+
+ *dep = (struct apk_dependency) {
+ .name = apk_db_get_name(db, adb_ro_blob(d, ADBI_DEP_NAME)),
+ .version = apk_atomize_dup(&db->atoms, ver),
+ .op = op,
+ };
+}
+
+void apk_deps_from_adb(struct apk_dependency_array **deps, struct apk_database *db, struct adb_obj *da)
+{
+ struct adb_obj obj;
+ int i;
+
+ for (i = ADBI_FIRST; i <= adb_ra_num(da); i++) {
+ struct apk_dependency *d = apk_dependency_array_add(deps);
+ adb_ro_obj(da, i, &obj);
+ apk_dep_from_adb(d, db, &obj);
+ }
+}
+
const char *apk_script_types[] = {
[APK_SCRIPT_PRE_INSTALL] = "pre-install",
[APK_SCRIPT_POST_INSTALL] = "post-install",
@@ -470,301 +437,11 @@ int apk_script_type(const char *name)
return APK_SCRIPT_INVALID;
}
-void apk_sign_ctx_init(struct apk_sign_ctx *ctx, int action,
- struct apk_checksum *identity, struct apk_trust *trust)
-{
- memset(ctx, 0, sizeof(struct apk_sign_ctx));
- ctx->trust = trust;
- ctx->action = action;
- ctx->allow_untrusted = trust->allow_untrusted;
- switch (action) {
- case APK_SIGN_VERIFY:
- /* If we're only verifing, we're going to start with a
- * signature section, which we don't need a hash of */
- ctx->md = EVP_md_null();
- break;
- case APK_SIGN_VERIFY_IDENTITY:
- /* If we're checking the package against a particular hash,
- * we need to start with that hash, because there may not
- * be a signature section to deduce it from */
- ctx->md = EVP_sha1();
- memcpy(&ctx->identity, identity, sizeof(ctx->identity));
- break;
- case APK_SIGN_GENERATE:
- case APK_SIGN_VERIFY_AND_GENERATE:
- ctx->md = EVP_sha1();
- break;
- default:
- ctx->action = APK_SIGN_NONE;
- ctx->md = EVP_md_null();
- ctx->control_started = 1;
- ctx->data_started = 1;
- break;
- }
- ctx->mdctx = EVP_MD_CTX_new();
- EVP_DigestInit_ex(ctx->mdctx, ctx->md, NULL);
- EVP_MD_CTX_set_flags(ctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT);
-}
-
-void apk_sign_ctx_free(struct apk_sign_ctx *ctx)
-{
- if (ctx->signature.data.ptr != NULL)
- free(ctx->signature.data.ptr);
- EVP_MD_CTX_free(ctx->mdctx);
-}
-
-static int check_signing_key_trust(struct apk_sign_ctx *sctx)
-{
- switch (sctx->action) {
- case APK_SIGN_VERIFY:
- case APK_SIGN_VERIFY_AND_GENERATE:
- if (sctx->signature.pkey == NULL) {
- if (sctx->allow_untrusted)
- break;
- return -APKE_SIGNATURE_UNTRUSTED;
- }
- }
- return 0;
-}
-
-int apk_sign_ctx_process_file(struct apk_sign_ctx *ctx,
- const struct apk_file_info *fi,
- struct apk_istream *is)
-{
- static struct {
- char type[8];
- unsigned int nid;
- } signature_type[] = {
- { "RSA512", NID_sha512 },
- { "RSA256", NID_sha256 },
- { "RSA", NID_sha1 },
- { "DSA", NID_dsa },
- };
- const EVP_MD *md = NULL;
- const char *name = NULL;
- struct apk_pkey *pkey;
- int r, i;
-
- if (ctx->data_started)
- return 1;
-
- if (fi->name[0] != '.' || strchr(fi->name, '/') != NULL) {
- /* APKv1.0 compatibility - first non-hidden file is
- * considered to start the data section of the file.
- * This does not make any sense if the file has v2.0
- * style .PKGINFO */
- if (ctx->has_data_checksum)
- return -APKE_V2PKG_FORMAT;
- /* Error out early if identity part is missing */
- if (ctx->action == APK_SIGN_VERIFY_IDENTITY)
- return -APKE_V2PKG_FORMAT;
- ctx->data_started = 1;
- ctx->control_started = 1;
- r = check_signing_key_trust(ctx);
- if (r < 0)
- return r;
- return 1;
- }
-
- if (ctx->control_started)
- return 1;
-
- if (strncmp(fi->name, ".SIGN.", 6) != 0) {
- ctx->control_started = 1;
- return 1;
- }
-
- /* By this point, we must be handling a signature file */
- ctx->num_signatures++;
-
- /* Already found a signature by a trusted key; no need to keep searching */
- if ((ctx->action != APK_SIGN_VERIFY &&
- ctx->action != APK_SIGN_VERIFY_AND_GENERATE) ||
- ctx->signature.pkey != NULL)
- return 0;
-
- for (i = 0; i < ARRAY_SIZE(signature_type); i++) {
- size_t slen = strlen(signature_type[i].type);
- if (strncmp(&fi->name[6], signature_type[i].type, slen) == 0 &&
- fi->name[6+slen] == '.') {
- md = EVP_get_digestbynid(signature_type[i].nid);
- name = &fi->name[6+slen+1];
- break;
- }
- }
- if (!md) return 0;
-
- pkey = apk_trust_key_by_name(ctx->trust, name);
- if (pkey) {
- ctx->md = md;
- ctx->signature.pkey = pkey->key;
- ctx->signature.data = apk_blob_from_istream(is, fi->size);
- }
- return 0;
-}
-
-int apk_sign_ctx_parse_pkginfo_line(void *ctx, apk_blob_t line)
-{
- struct apk_sign_ctx *sctx = (struct apk_sign_ctx *) ctx;
- apk_blob_t l, r;
-
- if (!sctx->control_started || sctx->data_started)
- return 0;
-
- if (line.ptr == NULL || line.len < 1 || line.ptr[0] == '#')
- return 0;
-
- if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
- return 0;
-
- if (apk_blob_compare(APK_BLOB_STR("datahash"), l) == 0) {
- sctx->has_data_checksum = 1;
- sctx->md = EVP_sha256();
- apk_blob_pull_hexdump(
- &r, APK_BLOB_PTR_LEN(sctx->data_checksum,
- EVP_MD_size(sctx->md)));
- }
-
- return 0;
-}
-
-int apk_sign_ctx_verify_tar(void *sctx, const struct apk_file_info *fi,
- struct apk_istream *is)
-{
- struct apk_sign_ctx *ctx = (struct apk_sign_ctx *) sctx;
- int r;
-
- r = apk_sign_ctx_process_file(ctx, fi, is);
- if (r <= 0)
- return r;
-
- if (!ctx->control_started || ctx->data_started)
- return 0;
-
- if (strcmp(fi->name, ".PKGINFO") == 0) {
- apk_blob_t l, token = APK_BLOB_STR("\n");
- while (apk_istream_get_delim(is, token, &l) == 0)
- apk_sign_ctx_parse_pkginfo_line(ctx, l);
- }
-
- return 0;
-}
-
-/* apk_sign_ctx_mpart_cb() handles hashing archives and checking signatures, but
- it can't do it alone. apk_sign_ctx_process_file() must be in the loop to
- actually select which signature is to be verified and load the corresponding
- public key into the context object, and apk_sign_ctx_parse_pkginfo_line()
- needs to be called when handling the .PKGINFO file to find any applicable
- datahash and load it into the context for this function to check against. */
-int apk_sign_ctx_mpart_cb(void *ctx, int part, apk_blob_t data)
-{
- struct apk_sign_ctx *sctx = (struct apk_sign_ctx *) ctx;
- unsigned char calculated[EVP_MAX_MD_SIZE];
- int r, end_of_control;
-
- if ((part == APK_MPART_DATA) ||
- (part == APK_MPART_BOUNDARY && sctx->data_started))
- goto update_digest;
-
- /* Still in signature blocks? */
- if (!sctx->control_started) {
- if (part == APK_MPART_END)
- return -APKE_V2PKG_FORMAT;
- goto reset_digest;
- }
-
- /* Grab state and mark all remaining block as data */
- end_of_control = (sctx->data_started == 0);
- sctx->data_started = 1;
-
- /* End of control-block and control does not have data checksum? */
- if (sctx->has_data_checksum == 0 && end_of_control &&
- part != APK_MPART_END)
- goto update_digest;
-
- /* Drool in the remainder of the digest block now, we will finish
- * hashing it in all cases */
- EVP_DigestUpdate(sctx->mdctx, data.ptr, data.len);
-
- if (sctx->has_data_checksum && !end_of_control) {
- /* End of data-block with a checksum read from the control block */
- EVP_DigestFinal_ex(sctx->mdctx, calculated, NULL);
- if (EVP_MD_CTX_size(sctx->mdctx) == 0 ||
- memcmp(calculated, sctx->data_checksum,
- EVP_MD_CTX_size(sctx->mdctx)) != 0)
- return -APKE_V2PKG_INTEGRITY;
- sctx->data_verified = 1;
- if (!sctx->allow_untrusted && !sctx->control_verified)
- return -APKE_SIGNATURE_UNTRUSTED;
- return 0;
- }
-
- /* Either end of control block with a data checksum or end
- * of the data block following a control block without a data
- * checksum. In either case, we're checking a signature. */
- r = check_signing_key_trust(sctx);
- if (r < 0)
- return r;
-
- switch (sctx->action) {
- case APK_SIGN_VERIFY:
- case APK_SIGN_VERIFY_AND_GENERATE:
- if (sctx->signature.pkey != NULL) {
- r = EVP_VerifyFinal(sctx->mdctx,
- (unsigned char *) sctx->signature.data.ptr,
- sctx->signature.data.len,
- sctx->signature.pkey);
- if (r != 1 && !sctx->allow_untrusted)
- return -APKE_SIGNATURE_INVALID;
- } else {
- r = 0;
- if (!sctx->allow_untrusted)
- return -APKE_SIGNATURE_UNTRUSTED;
- }
- if (r == 1) {
- sctx->control_verified = 1;
- if (!sctx->has_data_checksum && part == APK_MPART_END)
- sctx->data_verified = 1;
- }
- if (sctx->action == APK_SIGN_VERIFY_AND_GENERATE) {
- sctx->identity.type = EVP_MD_CTX_size(sctx->mdctx);
- EVP_DigestFinal_ex(sctx->mdctx, sctx->identity.data, NULL);
- }
- break;
- case APK_SIGN_VERIFY_IDENTITY:
- /* Reset digest for hashing data */
- EVP_DigestFinal_ex(sctx->mdctx, calculated, NULL);
- if (memcmp(calculated, sctx->identity.data,
- sctx->identity.type) != 0)
- return -APKE_V2PKG_INTEGRITY;
- sctx->control_verified = 1;
- if (!sctx->has_data_checksum && part == APK_MPART_END)
- sctx->data_verified = 1;
- break;
- case APK_SIGN_GENERATE:
- /* Package identity is the checksum */
- sctx->identity.type = EVP_MD_CTX_size(sctx->mdctx);
- EVP_DigestFinal_ex(sctx->mdctx, sctx->identity.data, NULL);
- if (sctx->action == APK_SIGN_GENERATE &&
- sctx->has_data_checksum)
- return -ECANCELED;
- break;
- }
-reset_digest:
- EVP_DigestInit_ex(sctx->mdctx, sctx->md, NULL);
- EVP_MD_CTX_set_flags(sctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT);
- return 0;
-
-update_digest:
- EVP_MD_CTX_clear_flags(sctx->mdctx, EVP_MD_CTX_FLAG_ONESHOT);
- EVP_DigestUpdate(sctx->mdctx, data.ptr, data.len);
- return 0;
-}
-
struct read_info_ctx {
struct apk_database *db;
struct apk_package *pkg;
- struct apk_sign_ctx *sctx;
+ struct apk_extract_ctx ectx;
+ int v3ok;
};
int apk_pkg_add_info(struct apk_database *db, struct apk_package *pkg,
@@ -790,7 +467,11 @@ int apk_pkg_add_info(struct apk_database *db, struct apk_package *pkg,
pkg->arch = apk_atomize_dup(&db->atoms, value);
break;
case 'D':
- apk_blob_pull_deps(&value, db, &pkg->depends);
+ if (apk_blob_pull_deps(&value, db, &pkg->depends)) {
+ db->compat_depversions = 1;
+ db->compat_notinstallable = pkg->uninstallable = 1;
+ return 2;
+ }
break;
case 'C':
apk_blob_pull_csum(&value, &pkg->csum);
@@ -802,10 +483,18 @@ int apk_pkg_add_info(struct apk_database *db, struct apk_package *pkg,
pkg->installed_size = apk_blob_pull_uint(&value, 10);
break;
case 'p':
- apk_blob_pull_deps(&value, db, &pkg->provides);
+ if (apk_blob_pull_deps(&value, db, &pkg->provides)) {
+ db->compat_depversions = 1;
+ return 2;
+ }
break;
case 'i':
- apk_blob_pull_deps(&value, db, &pkg->install_if);
+ if (apk_blob_pull_deps(&value, db, &pkg->install_if)) {
+ // Disable partial install_if rules
+ apk_dependency_array_free(&pkg->install_if);
+ db->compat_depversions = 1;
+ return 2;
+ }
break;
case 'o':
pkg->origin = apk_atomize_dup(&db->atoms, value);
@@ -828,19 +517,58 @@ int apk_pkg_add_info(struct apk_database *db, struct apk_package *pkg,
return 1;
default:
/* lower case index entries are safe to be ignored */
- if (!islower(field)) {
- pkg->uninstallable = 1;
- db->compat_notinstallable = 1;
- }
+ if (!islower(field)) db->compat_notinstallable = pkg->uninstallable = 1;
db->compat_newfeatures = 1;
return 2;
}
if (APK_BLOB_IS_NULL(value))
- return -1;
+ return -APKE_V2PKG_FORMAT;
return 0;
}
-static int read_info_line(void *ctx, apk_blob_t line)
+static char *commit_id(apk_blob_t b)
+{
+ char buf[80];
+ apk_blob_t to = APK_BLOB_BUF(buf);
+
+ apk_blob_push_hexdump(&to, b);
+ to = apk_blob_pushed(APK_BLOB_BUF(buf), to);
+ if (APK_BLOB_IS_NULL(to)) return NULL;
+ return apk_blob_cstr(to);
+}
+
+void apk_pkg_from_adb(struct apk_database *db, struct apk_package *pkg, struct adb_obj *pkginfo)
+{
+ struct adb_obj obj;
+ apk_blob_t uid;
+
+ uid = adb_ro_blob(pkginfo, ADBI_PI_UNIQUE_ID);
+ if (uid.len >= APK_CHECKSUM_SHA1) {
+ pkg->csum.type = APK_CHECKSUM_SHA1;
+ memcpy(pkg->csum.data, uid.ptr, uid.len);
+ }
+
+ pkg->name = apk_db_get_name(db, adb_ro_blob(pkginfo, ADBI_PI_NAME));
+ pkg->version = apk_atomize_dup(&db->atoms, adb_ro_blob(pkginfo, ADBI_PI_VERSION));
+ pkg->description = apk_blob_cstr(adb_ro_blob(pkginfo, ADBI_PI_DESCRIPTION));
+ pkg->url = apk_blob_cstr(adb_ro_blob(pkginfo, ADBI_PI_URL));
+ pkg->license = apk_atomize_dup(&db->atoms, adb_ro_blob(pkginfo, ADBI_PI_LICENSE));
+ pkg->arch = apk_atomize_dup(&db->atoms, adb_ro_blob(pkginfo, ADBI_PI_ARCH));
+ pkg->installed_size = adb_ro_int(pkginfo, ADBI_PI_INSTALLED_SIZE);
+ pkg->size = adb_ro_int(pkginfo, ADBI_PI_FILE_SIZE);
+ pkg->provider_priority = adb_ro_int(pkginfo, ADBI_PI_PROVIDER_PRIORITY);
+ pkg->origin = apk_atomize_dup(&db->atoms, adb_ro_blob(pkginfo, ADBI_PI_ORIGIN));
+ pkg->maintainer = apk_atomize_dup(&db->atoms, adb_ro_blob(pkginfo, ADBI_PI_MAINTAINER));
+ pkg->build_time = adb_ro_int(pkginfo, ADBI_PI_BUILD_TIME);
+ pkg->commit = commit_id(adb_ro_blob(pkginfo, ADBI_PI_REPO_COMMIT));
+ pkg->layer = adb_ro_int(pkginfo, ADBI_PI_LAYER);
+
+ apk_deps_from_adb(&pkg->depends, db, adb_ro_obj(pkginfo, ADBI_PI_DEPENDS, &obj));
+ apk_deps_from_adb(&pkg->provides, db, adb_ro_obj(pkginfo, ADBI_PI_PROVIDES, &obj));
+ apk_deps_from_adb(&pkg->install_if, db, adb_ro_obj(pkginfo, ADBI_PI_INSTALL_IF, &obj));
+}
+
+static int read_info_line(struct read_info_ctx *ri, apk_blob_t line)
{
static struct {
const char *str;
@@ -862,7 +590,6 @@ static int read_info_line(void *ctx, apk_blob_t line)
{ "commit", 'c' },
{ "provider_priority", 'k' },
};
- struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
apk_blob_t l, r;
int i;
@@ -872,49 +599,53 @@ static int read_info_line(void *ctx, apk_blob_t line)
if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
return 0;
- for (i = 0; i < ARRAY_SIZE(fields); i++) {
- if (apk_blob_compare(APK_BLOB_STR(fields[i].str), l) == 0) {
- apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r);
- return 0;
- }
- }
- apk_sign_ctx_parse_pkginfo_line(ri->sctx, line);
+ apk_extract_v2_control(&ri->ectx, l, r);
+
+ for (i = 0; i < ARRAY_SIZE(fields); i++)
+ if (apk_blob_compare(APK_BLOB_STR(fields[i].str), l) == 0)
+ return apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r);
return 0;
}
-static int read_info_entry(void *ctx, const struct apk_file_info *ae,
- struct apk_istream *is)
+static int apk_pkg_v2meta(struct apk_extract_ctx *ectx, struct apk_istream *is)
{
- struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
- struct apk_package *pkg = ri->pkg;
+ struct read_info_ctx *ri = container_of(ectx, struct read_info_ctx, ectx);
+ apk_blob_t l, token = APK_BLOB_STR("\n");
int r;
- r = apk_sign_ctx_process_file(ri->sctx, ae, is);
- if (r <= 0)
- return r;
-
- if (!ri->sctx->control_started || ri->sctx->data_started)
- return 0;
-
- if (strcmp(ae->name, ".PKGINFO") == 0) {
- /* APK 2.0 format */
- apk_blob_t l, token = APK_BLOB_STR("\n");
- while (apk_istream_get_delim(is, token, &l) == 0)
- read_info_line(ctx, l);
- } else if (strcmp(ae->name, ".INSTALL") == 0) {
- apk_warn(&ri->db->ctx->out,
- "Package '%s-%s' contains deprecated .INSTALL",
- pkg->name->name, pkg->version);
+ while (apk_istream_get_delim(is, token, &l) == 0) {
+ r = read_info_line(ri, l);
+ if (r < 0) return r;
}
return 0;
}
-int apk_pkg_read(struct apk_database *db, const char *file,
- struct apk_sign_ctx *sctx, struct apk_package **pkg)
+static int apk_pkg_v3meta(struct apk_extract_ctx *ectx, struct adb_obj *pkg)
{
- struct read_info_ctx ctx;
+ struct read_info_ctx *ri = container_of(ectx, struct read_info_ctx, ectx);
+ struct adb_obj pkginfo;
+
+ if (!ri->v3ok) return -APKE_FORMAT_NOT_SUPPORTED;
+
+ adb_ro_obj(pkg, ADBI_PKG_PKGINFO, &pkginfo);
+ apk_pkg_from_adb(ri->db, ri->pkg, &pkginfo);
+
+ return -ECANCELED;
+}
+
+static const struct apk_extract_ops extract_pkgmeta_ops = {
+ .v2meta = apk_pkg_v2meta,
+ .v3meta = apk_pkg_v3meta,
+};
+
+int apk_pkg_read(struct apk_database *db, const char *file, struct apk_package **pkg, int v3ok)
+{
+ struct read_info_ctx ctx = {
+ .db = db,
+ .v3ok = v3ok,
+ };
struct apk_file_info fi;
int r;
@@ -922,28 +653,25 @@ int apk_pkg_read(struct apk_database *db, const char *file,
if (r != 0)
return r;
- memset(&ctx, 0, sizeof(ctx));
- ctx.db = db;
- ctx.sctx = sctx;
ctx.pkg = apk_pkg_new();
r = -ENOMEM;
if (ctx.pkg == NULL)
goto err;
ctx.pkg->size = fi.size;
-
- r = apk_tar_parse(
- apk_istream_gunzip_mpart(apk_istream_from_file(AT_FDCWD, file), apk_sign_ctx_mpart_cb, sctx),
- read_info_entry, &ctx, db->id_cache);
- if (r < 0 && r != -ECANCELED)
- goto err;
- if (ctx.pkg->name == NULL || ctx.pkg->uninstallable) {
- r = -ENOTSUP;
+ apk_extract_init(&ctx.ectx, db->ctx, &extract_pkgmeta_ops);
+ apk_extract_generate_identity(&ctx.ectx, &ctx.pkg->csum);
+
+ r = apk_extract(&ctx.ectx, apk_istream_from_file(AT_FDCWD, file));
+ if (r < 0 && r != -ECANCELED) goto err;
+ if (ctx.pkg->csum.type == APK_CHECKSUM_NONE ||
+ ctx.pkg->name == NULL ||
+ ctx.pkg->uninstallable) {
+ r = -APKE_V2PKG_FORMAT;
goto err;
}
- if (sctx->action != APK_SIGN_VERIFY)
- ctx.pkg->csum = sctx->identity;
- ctx.pkg->filename = strdup(file);
+ *apk_string_array_add(&db->filename_array) = strdup(file);
+ ctx.pkg->filename_ndx = db->filename_array->num;
ctx.pkg = apk_db_pkg_add(db, ctx.pkg);
if (pkg != NULL)
@@ -956,62 +684,106 @@ err:
void apk_pkg_free(struct apk_package *pkg)
{
- if (pkg == NULL)
- return;
+ if (!pkg) return;
apk_pkg_uninstall(NULL, pkg);
apk_dependency_array_free(&pkg->depends);
apk_dependency_array_free(&pkg->provides);
apk_dependency_array_free(&pkg->install_if);
- if (pkg->url)
- free(pkg->url);
- if (pkg->description)
- free(pkg->description);
- if (pkg->commit)
- free(pkg->commit);
+ if (pkg->url) free(pkg->url);
+ if (pkg->description) free(pkg->description);
+ if (pkg->commit) free(pkg->commit);
free(pkg);
}
+int apk_ipkg_assign_script(struct apk_installed_package *ipkg, unsigned int type, apk_blob_t b)
+{
+ if (APK_BLOB_IS_NULL(b)) return -1;
+ if (type >= APK_SCRIPT_MAX) {
+ free(b.ptr);
+ return -1;
+ }
+ if (ipkg->script[type].ptr) free(ipkg->script[type].ptr);
+ ipkg->script[type] = b;
+ return 0;
+}
+
int apk_ipkg_add_script(struct apk_installed_package *ipkg,
struct apk_istream *is,
unsigned int type, unsigned int size)
{
apk_blob_t b;
+ apk_blob_from_istream(is, size, &b);
+ return apk_ipkg_assign_script(ipkg, type, b);
+}
- if (type >= APK_SCRIPT_MAX) return -1;
- b = apk_blob_from_istream(is, size);
- if (APK_BLOB_IS_NULL(b)) return -1;
- if (ipkg->script[type].ptr) free(ipkg->script[type].ptr);
- ipkg->script[type] = b;
+#ifdef __linux__
+static inline int make_device_tree(struct apk_database *db)
+{
+ if (faccessat(db->root_fd, "dev", F_OK, 0) == 0) return 0;
+ if (mkdirat(db->root_fd, "dev", 0755) < 0 ||
+ mknodat(db->root_fd, "dev/null", S_IFCHR | 0666, makedev(1, 3)) < 0 ||
+ mknodat(db->root_fd, "dev/zero", S_IFCHR | 0666, makedev(1, 5)) < 0 ||
+ mknodat(db->root_fd, "dev/random", S_IFCHR | 0666, makedev(1, 8)) < 0 ||
+ mknodat(db->root_fd, "dev/urandom", S_IFCHR | 0666, makedev(1, 9)) < 0 ||
+ mknodat(db->root_fd, "dev/console", S_IFCHR | 0600, makedev(5, 1)) < 0)
+ return -1;
+ return 0;
+}
+#else
+static inline int make_device_tree(struct apk_database *db)
+{
+ (void) db;
return 0;
}
+#endif
-void apk_ipkg_run_script(struct apk_installed_package *ipkg,
- struct apk_database *db,
- unsigned int type, char **argv)
+int apk_ipkg_run_script(struct apk_installed_package *ipkg,
+ struct apk_database *db,
+ unsigned int type, char **argv)
{
+ // script_exec_dir is the directory to which the script is extracted,
+ // executed from, and removed. It needs to not be 'noexec' mounted, and
+ // preferably a tmpfs disk, or something that could be wiped in boot.
+ // Originally this was /tmp, but it is often suggested to be 'noexec'.
+ // Then changed ro /var/cache/misc, but that is also often 'noexec'.
+ // /run was consider as it's tmpfs, but it also might be changing to 'noexec'.
+ // So use for now /lib/apk/exec even if it is not of temporary nature.
+ static const char script_exec_dir[] = "lib/apk/exec";
struct apk_out *out = &db->ctx->out;
struct apk_package *pkg = ipkg->pkg;
char fn[PATH_MAX];
- int fd, root_fd = db->root_fd;
+ int fd, root_fd = db->root_fd, ret = 0;
if (type >= APK_SCRIPT_MAX || ipkg->script[type].ptr == NULL)
- return;
+ return 0;
argv[0] = (char *) apk_script_types[type];
- /* Avoid /tmp as it can be mounted noexec */
- snprintf(fn, sizeof(fn), "var/cache/misc/" PKG_VER_FMT ".%s",
- PKG_VER_PRINTF(pkg),
+ snprintf(fn, sizeof(fn), "%s/" PKG_VER_FMT ".%s",
+ script_exec_dir, PKG_VER_PRINTF(pkg),
apk_script_types[type]);
if ((db->ctx->flags & (APK_NO_SCRIPTS | APK_SIMULATE)) != 0)
- return;
+ return 0;
- apk_msg(out, "Executing %s", &fn[15]);
+ if (!db->script_dirs_checked) {
+ if (apk_make_dirs(root_fd, "tmp", 01777, 0) <0 ||
+ apk_make_dirs(root_fd, script_exec_dir, 0700, 0755) < 0) {
+ apk_err(out, "failed to prepare dirs for hook scripts: %s",
+ apk_error_str(errno));
+ goto err;
+ }
+ if (make_device_tree(db) < 0) {
+ apk_warn(out, "failed to create initial device nodes for scripts: %s",
+ apk_error_str(errno));
+ }
+ db->script_dirs_checked = 1;
+ }
+
+ apk_msg(out, "Executing %s", &fn[strlen(script_exec_dir)+1]);
fd = openat(root_fd, fn, O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC, 0755);
if (fd < 0) {
- mkdirat(root_fd, "var/cache/misc", 0755);
fd = openat(root_fd, fn, O_CREAT|O_RDWR|O_TRUNC|O_CLOEXEC, 0755);
if (fd < 0) goto err_log;
}
@@ -1030,11 +802,13 @@ void apk_ipkg_run_script(struct apk_installed_package *ipkg,
goto cleanup;
err_log:
- apk_err(out, "%s: failed to execute: %s", &fn[15], apk_error_str(errno));
+ apk_err(out, "%s: failed to execute: %s", &fn[strlen(script_exec_dir)+1], apk_error_str(errno));
err:
ipkg->broken_script = 1;
+ ret = 1;
cleanup:
unlinkat(root_fd, fn, 0);
+ return ret;
}
static int parse_index_line(void *ctx, apk_blob_t line)
@@ -1082,10 +856,9 @@ static int write_depends(struct apk_ostream *os, const char *field,
return 0;
}
-int apk_pkg_write_index_entry(struct apk_package *info,
- struct apk_ostream *os)
+int apk_pkg_write_index_header(struct apk_package *info, struct apk_ostream *os)
{
- char buf[512];
+ char buf[2048];
apk_blob_t bbuf = APK_BLOB_BUF(buf);
apk_blob_push_blob(&bbuf, APK_BLOB_STR("C:"));
@@ -1134,7 +907,7 @@ int apk_pkg_write_index_entry(struct apk_package *info,
return apk_ostream_cancel(os, -ENOBUFS);
bbuf = apk_blob_pushed(APK_BLOB_BUF(buf), bbuf);
- if (apk_ostream_write(os, bbuf.ptr, bbuf.len) ||
+ if (apk_ostream_write(os, bbuf.ptr, bbuf.len) < 0 ||
write_depends(os, "D:", info->depends) ||
write_depends(os, "p:", info->provides) ||
write_depends(os, "i:", info->install_if))
@@ -1143,12 +916,103 @@ int apk_pkg_write_index_entry(struct apk_package *info,
return 0;
}
-int apk_pkg_version_compare(struct apk_package *a, struct apk_package *b)
+int apk_pkg_write_index_entry(struct apk_package *pkg, struct apk_ostream *os)
+{
+ int r = apk_pkg_write_index_header(pkg, os);
+ if (r < 0) return r;
+ return apk_ostream_write(os, "\n", 1);
+}
+
+int apk_pkg_version_compare(const struct apk_package *a, const struct apk_package *b)
{
if (a->version == b->version)
return APK_VERSION_EQUAL;
- return apk_version_compare_blob(*a->version, *b->version);
+ return apk_version_compare(*a->version, *b->version);
+}
+
+int apk_pkg_cmp_display(const struct apk_package *a, const struct apk_package *b)
+{
+ if (a->name != b->name)
+ return apk_name_cmp_display(a->name, b->name);
+ switch (apk_pkg_version_compare(a, b)) {
+ case APK_VERSION_LESS:
+ return -1;
+ case APK_VERSION_GREATER:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int apk_pkg_replaces_dir(const struct apk_package *a, const struct apk_package *b)
+{
+ struct apk_installed_package *ai = a->ipkg, *bi = b->ipkg;
+
+ /* Prefer overlay */
+ if (a->name == NULL) return APK_PKG_REPLACES_NO;
+ if (b->name == NULL) return APK_PKG_REPLACES_YES;
+
+ /* Upgrading package? */
+ if (a->name == b->name) return APK_PKG_REPLACES_YES;
+
+ /* Highest replaces_priority wins */
+ if (ai->replaces_priority > bi->replaces_priority) return APK_PKG_REPLACES_NO;
+ if (ai->replaces_priority < bi->replaces_priority) return APK_PKG_REPLACES_YES;
+
+ /* If both have the same origin... */
+ if (a->origin && a->origin == b->origin) {
+ /* .. and either has origin equal to package name, prefer it. */
+ if (apk_blob_compare(*a->origin, APK_BLOB_STR(a->name->name)) == 0)
+ return APK_PKG_REPLACES_NO;
+ if (apk_blob_compare(*b->origin, APK_BLOB_STR(b->name->name)) == 0)
+ return APK_PKG_REPLACES_YES;
+ }
+
+ /* Fall back to package name to have stable sort */
+ if (strcmp(a->name->name, b->name->name) <= 0) return APK_PKG_REPLACES_NO;
+ return APK_PKG_REPLACES_YES;
+}
+
+int apk_pkg_replaces_file(const struct apk_package *a, const struct apk_package *b)
+{
+ struct apk_dependency *dep;
+ int a_prio = -1, b_prio = -1;
+
+ /* Overlay file? Replace the ownership, but extraction will keep the overlay file. */
+ if (a->name == NULL) return APK_PKG_REPLACES_YES;
+
+ /* Upgrading package? */
+ if (a->name == b->name) return APK_PKG_REPLACES_YES;
+
+ /* Or same source package? */
+ if (a->origin && a->origin == b->origin) return APK_PKG_REPLACES_YES;
+
+ /* Does the original package replace the new one? */
+ foreach_array_item(dep, a->ipkg->replaces) {
+ if (apk_dep_is_materialized(dep, b)) {
+ a_prio = a->ipkg->replaces_priority;
+ break;
+ }
+ }
+
+ /* Does the new package replace the original one? */
+ foreach_array_item(dep, b->ipkg->replaces) {
+ if (apk_dep_is_materialized(dep, a)) {
+ b_prio = b->ipkg->replaces_priority;
+ break;
+ }
+ }
+
+ /* If the original package is more important, skip this file */
+ if (a_prio > b_prio) return APK_PKG_REPLACES_NO;
+
+ /* If the new package has valid 'replaces', we will overwrite
+ * the file without warnings. */
+ if (b_prio >= 0) return APK_PKG_REPLACES_YES;
+
+ /* Both ship same file, but metadata is inconclusive. */
+ return APK_PKG_REPLACES_CONFLICT;
}
unsigned int apk_foreach_genid(void)
diff --git a/src/pathbuilder.c b/src/pathbuilder.c
index 166aa22..3f25cf8 100644
--- a/src/pathbuilder.c
+++ b/src/pathbuilder.c
@@ -11,19 +11,18 @@
int apk_pathbuilder_pushb(struct apk_pathbuilder *pb, apk_blob_t b)
{
- size_t i = pb->namelen;
+ size_t oldlen = pb->namelen, i = pb->namelen;
if (i + b.len + 2 >= ARRAY_SIZE(pb->name)) return -ENAMETOOLONG;
if (i) pb->name[i++] = '/';
memcpy(&pb->name[i], b.ptr, b.len);
pb->namelen = i + b.len;
pb->name[pb->namelen] = 0;
- return 0;
+ return oldlen;
}
-void apk_pathbuilder_pop(struct apk_pathbuilder *pb)
+void apk_pathbuilder_pop(struct apk_pathbuilder *pb, int pos)
{
- char *slash = memrchr(pb->name, '/', pb->namelen);
- if (slash) pb->namelen = slash - pb->name;
- else pb->namelen = 0;
+ if (pos < 0) return;
+ pb->namelen = pos;
pb->name[pb->namelen] = 0;
}
diff --git a/src/print.c b/src/print.c
index 1248995..808d74f 100644
--- a/src/print.c
+++ b/src/print.c
@@ -7,14 +7,14 @@
* SPDX-License-Identifier: GPL-2.0-only
*/
-#include <assert.h>
#include <stdio.h>
+#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
-#include <malloc.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include "apk_defines.h"
#include "apk_print.h"
@@ -28,7 +28,6 @@ const char *apk_error_str(int error)
case ECONNABORTED: return "network connection aborted";
case ECONNREFUSED: return "could not connect to server (check repositories file)";
case ENETUNREACH: return "network error (check Internet connection and firewall)";
- case EREMOTEIO: return "remote server returned error (try 'apk update')";
case EAGAIN: return "temporary error (try again later)";
case APKE_EOF: return "unexpected end of file";
case APKE_DNS: return "DNS error (try again later)";
@@ -36,9 +35,14 @@ const char *apk_error_str(int error)
case APKE_CRYPTO_ERROR: return "crypto error";
case APKE_CRYPTO_NOT_SUPPORTED: return "cryptographic algorithm not supported";
case APKE_CRYPTO_KEY_FORMAT: return "cryptographic key format not recognized";
- case APKE_SIGNATURE_FAIL: return "signing failure";
+ case APKE_SIGNATURE_GEN_FAILURE: return "signing failure";
case APKE_SIGNATURE_UNTRUSTED: return "UNTRUSTED signature";
case APKE_SIGNATURE_INVALID: return "BAD signature";
+ case APKE_FORMAT_INVALID: return "file format is invalid or inconsistent";
+ case APKE_FORMAT_NOT_SUPPORTED: return "file format not supported (in this applet)";
+ case APKE_PKGNAME_FORMAT: return "package name is invalid";
+ case APKE_PKGVERSION_FORMAT: return "package version is invalid";
+ case APKE_DEPENDENCY_FORMAT: return "dependency format is invalid";
case APKE_ADB_COMPRESSION: return "ADB compression not supported";
case APKE_ADB_HEADER: return "ADB header error";
case APKE_ADB_VERSION: return "incompatible ADB version";
@@ -47,7 +51,6 @@ const char *apk_error_str(int error)
case APKE_ADB_SIGNATURE: return "ADB signature block error";
case APKE_ADB_NO_FROMSTRING: return "ADB schema error (no fromstring)";
case APKE_ADB_LIMIT: return "ADB schema limit reached";
- case APKE_ADB_DEPENDENCY_FORMAT: return "ADB dependency format";
case APKE_ADB_PACKAGE_FORMAT: return "ADB package format";
case APKE_V2DB_FORMAT: return "v2 database format error";
case APKE_V2PKG_FORMAT: return "v2 package format error";
@@ -57,12 +60,30 @@ const char *apk_error_str(int error)
case APKE_INDEX_STALE: return "package mentioned in index not found (try 'apk update')";
case APKE_FILE_INTEGRITY: return "file integrity error";
case APKE_CACHE_NOT_AVAILABLE: return "cache not available";
- case APKE_UVOL: return "uvol error";
+ case APKE_UVOL_NOT_AVAILABLE: return "uvol manager not available";
+ case APKE_UVOL_ERROR: return "uvol error";
+ case APKE_UVOL_ROOT: return "uvol not supported with --root";
+ case APKE_REMOTE_IO: return "remote server returned error (try 'apk update')";
default:
return strerror(error);
}
}
+int apk_exit_status_str(int status, char *buf, size_t sz)
+{
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+ return 0;
+ if (WIFEXITED(status))
+ return snprintf(buf, sz, "exited with error %d", WEXITSTATUS(status));
+ if (WIFSIGNALED(status))
+ return snprintf(buf, sz, "killed by signal %d", WTERMSIG(status));
+ if (WIFSTOPPED(status))
+ return snprintf(buf, sz, "stopped by signal %d", WSTOPSIG(status));
+ if (WIFCONTINUED(status))
+ return snprintf(buf, sz, "continued");
+ return snprintf(buf, sz, "status unknown %x", status);
+}
+
static const char *size_units[] = {"B", "KiB", "MiB", "GiB", "TiB"};
int apk_get_human_size_unit(apk_blob_t b)
@@ -220,16 +241,52 @@ void apk_print_progress(struct apk_progress *p, size_t done, size_t total)
fputs("\e8\e[0K", out);
}
+void apk_print_indented_init(struct apk_indent *i, struct apk_out *out, int err)
+{
+ *i = (struct apk_indent) {
+ .f = err ? out->err : out->out,
+ .width = apk_out_get_width(out),
+ };
+ out->last_change++;
+}
+
+void apk_print_indented_line(struct apk_indent *i, const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ vfprintf(i->f, fmt, va);
+ va_end(va);
+ i->x = i->indent = 0;
+}
+
+void apk_print_indented_group(struct apk_indent *i, int indent, const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ i->x = vfprintf(i->f, fmt, va);
+ i->indent = indent ?: (i->x + 1);
+ if (fmt[strlen(fmt)-1] == '\n') i->x = 0;
+ va_end(va);
+}
+
+void apk_print_indented_end(struct apk_indent *i)
+{
+ if (i->x) {
+ fprintf(i->f, "\n");
+ i->x = i->indent = 0;
+ }
+}
+
int apk_print_indented(struct apk_indent *i, apk_blob_t blob)
{
- FILE *out = i->out->out;
if (i->x <= i->indent)
- i->x += fprintf(out, "%*s" BLOB_FMT, i->indent - i->x, "", BLOB_PRINTF(blob));
- else if (i->x + blob.len + 1 >= apk_out_get_width(i->out))
- i->x = fprintf(out, "\n%*s" BLOB_FMT, i->indent, "", BLOB_PRINTF(blob)) - 1;
+ i->x += fprintf(i->f, "%*s" BLOB_FMT, i->indent - i->x, "", BLOB_PRINTF(blob));
+ else if (i->x + blob.len + 1 >= i->width)
+ i->x = fprintf(i->f, "\n%*s" BLOB_FMT, i->indent, "", BLOB_PRINTF(blob)) - 1;
else
- i->x += fprintf(out, " " BLOB_FMT, BLOB_PRINTF(blob));
- i->out->last_change++;
+ i->x += fprintf(i->f, " " BLOB_FMT, BLOB_PRINTF(blob));
return 0;
}
diff --git a/src/solver.c b/src/solver.c
index 2ea7f3f..79ec554 100644
--- a/src/solver.c
+++ b/src/solver.c
@@ -51,6 +51,7 @@ void apk_solver_set_name_flags(struct apk_name *name,
{
struct apk_provider *p;
+ name->solver_flags_set = 1;
foreach_array_item(p, name->providers) {
struct apk_package *pkg = p->pkg;
dbg_printf("marking '" PKG_VER_FMT "' = 0x%04x / 0x%04x\n",
@@ -118,8 +119,7 @@ static void reevaluate_reverse_deps(struct apk_solver_state *ss, struct apk_name
foreach_array_item(pname0, name->rdepends) {
name0 = *pname0;
- if (!name0->ss.seen)
- continue;
+ if (!name0->ss.seen) continue;
name0->ss.reevaluate_deps = 1;
queue_dirty(ss, name0);
}
@@ -131,15 +131,21 @@ static void reevaluate_reverse_installif(struct apk_solver_state *ss, struct apk
foreach_array_item(pname0, name->rinstall_if) {
name0 = *pname0;
- if (!name0->ss.seen)
- continue;
- if (name0->ss.no_iif)
- continue;
+ if (!name0->ss.seen) continue;
+ if (name0->ss.no_iif) continue;
name0->ss.reevaluate_iif = 1;
queue_dirty(ss, name0);
}
}
+static void reevaluate_reverse_installif_pkg(struct apk_solver_state *ss, struct apk_package *pkg)
+{
+ struct apk_dependency *d;
+ reevaluate_reverse_installif(ss, pkg->name);
+ foreach_array_item(d, pkg->provides)
+ reevaluate_reverse_installif(ss, d->name);
+}
+
static void disqualify_package(struct apk_solver_state *ss, struct apk_package *pkg, const char *reason)
{
struct apk_dependency *p;
@@ -149,7 +155,7 @@ static void disqualify_package(struct apk_solver_state *ss, struct apk_package *
reevaluate_reverse_deps(ss, pkg->name);
foreach_array_item(p, pkg->provides)
reevaluate_reverse_deps(ss, p->name);
- reevaluate_reverse_installif(ss, pkg->name);
+ reevaluate_reverse_installif_pkg(ss, pkg);
}
static int dependency_satisfiable(struct apk_solver_state *ss, struct apk_dependency *dep)
@@ -157,7 +163,7 @@ static int dependency_satisfiable(struct apk_solver_state *ss, struct apk_depend
struct apk_name *name = dep->name;
struct apk_provider *p;
- if (dep->conflict && ss->ignore_conflict)
+ if (apk_dep_conflict(dep) && ss->ignore_conflict)
return TRUE;
if (name->ss.locked)
@@ -192,15 +198,15 @@ static void discover_name(struct apk_solver_state *ss, struct apk_name *name)
pkg->ss.seen = 1;
pkg->ss.pinning_allowed = APK_DEFAULT_PINNING_MASK;
pkg->ss.pinning_preferred = APK_DEFAULT_PINNING_MASK;
- pkg->ss.pkg_available =
- (pkg->filename != NULL) ||
- (pkg->repos & db->available_repos & ~BIT(APK_REPOSITORY_CACHED));
+ pkg->ss.pkg_available = pkg->filename_ndx ||
+ (pkg->repos & db->available_repos & ~BIT(APK_REPOSITORY_CACHED));
/* Package is in 'cached' repository if filename is provided,
* or it's a 'virtual' package with install_size zero */
- pkg->ss.pkg_selectable =
- (pkg->repos & db->available_repos) ||
- pkg->cached_non_repository ||
- pkg->ipkg;
+ pkg->ss.pkg_selectable = !pkg->uninstallable &&
+ (BIT(pkg->layer) & db->active_layers) &&
+ ((pkg->repos & db->available_repos) ||
+ pkg->cached_non_repository ||
+ pkg->ipkg);
/* Prune install_if packages that are no longer available,
* currently works only if SOLVERF_AVAILABLE is set in the
@@ -211,8 +217,7 @@ static void discover_name(struct apk_solver_state *ss, struct apk_name *name)
!pkg->ss.pkg_available);
repos = get_pkg_repos(db, pkg);
- pkg->ss.tag_preferred =
- (pkg->filename != NULL) ||
+ pkg->ss.tag_preferred = pkg->filename_ndx ||
(pkg->installed_size == 0) ||
(repos & ss->default_repos);
pkg->ss.tag_ok =
@@ -240,8 +245,16 @@ static void discover_name(struct apk_solver_state *ss, struct apk_name *name)
dbg_printf("discover %s: max_dep_chain=%d no_iif=%d\n",
name->name, name->ss.max_dep_chain, name->ss.no_iif);
}
- foreach_array_item(pname0, name->rinstall_if)
- discover_name(ss, *pname0);
+ foreach_array_item(p, name->providers) {
+ struct apk_package *pkg = p->pkg;
+ foreach_array_item(pname0, pkg->name->rinstall_if)
+ discover_name(ss, *pname0);
+ foreach_array_item(dep, pkg->provides) {
+ if (dep->name->ss.seen) continue;
+ foreach_array_item(pname0, dep->name->rinstall_if)
+ discover_name(ss, *pname0);
+ }
+ }
}
static void name_requirers_changed(struct apk_solver_state *ss, struct apk_name *name)
@@ -283,16 +296,16 @@ static void apply_constraint(struct apk_solver_state *ss, struct apk_package *pp
int is_provided;
dbg_printf(" apply_constraint: %s%s%s" BLOB_FMT "\n",
- dep->conflict ? "!" : "",
+ apk_dep_conflict(dep) ? "!" : "",
name->name,
- apk_version_op_string(dep->result_mask),
+ apk_version_op_string(dep->op),
BLOB_PRINTF(*dep->version));
- if (dep->conflict && ss->ignore_conflict)
+ if (apk_dep_conflict(dep) && ss->ignore_conflict)
return;
- name->ss.requirers += !dep->conflict;
- if (name->ss.requirers == 1 && !dep->conflict)
+ name->ss.requirers += !apk_dep_conflict(dep);
+ if (name->ss.requirers == 1 && !apk_dep_conflict(dep))
name_requirers_changed(ss, name);
foreach_array_item(p0, name->providers) {
@@ -423,7 +436,7 @@ static void reconsider_name(struct apk_solver_state *ss, struct apk_name *name)
/* FIXME: can merge also conflicts */
foreach_array_item(dep, pkg->depends)
- if (!dep->conflict)
+ if (!apk_dep_conflict(dep))
merge_index(&dep->name->ss.merge_depends, num_options);
if (merge_index(&pkg->name->ss.merge_provides, num_options))
@@ -597,7 +610,7 @@ static int compare_providers(struct apk_solver_state *ss,
}
/* Select latest by requested name */
- switch (apk_version_compare_blob(*pA->version, *pB->version)) {
+ switch (apk_version_compare(*pA->version, *pB->version)) {
case APK_VERSION_LESS:
dbg_printf(" select latest by requested name (less)\n");
return -1;
@@ -608,7 +621,7 @@ static int compare_providers(struct apk_solver_state *ss,
/* Select latest by principal name */
if (pkgA->name == pkgB->name) {
- switch (apk_version_compare_blob(*pkgA->version, *pkgB->version)) {
+ switch (apk_version_compare(*pkgA->version, *pkgB->version)) {
case APK_VERSION_LESS:
dbg_printf(" select latest by principal name (less)\n");
return -1;
@@ -683,7 +696,10 @@ static void assign_name(struct apk_solver_state *ss, struct apk_name *name, stru
}
}
reevaluate_reverse_deps(ss, name);
- reevaluate_reverse_installif(ss, name);
+ if (p.pkg)
+ reevaluate_reverse_installif_pkg(ss, p.pkg);
+ else
+ reevaluate_reverse_installif(ss, name);
}
static void select_package(struct apk_solver_state *ss, struct apk_name *name)
@@ -714,8 +730,8 @@ static void select_package(struct apk_solver_state *ss, struct apk_name *name)
if (p->version == &apk_atom_null &&
p->pkg->name->auto_select_virtual == 0 &&
p->pkg->name->ss.requirers == 0 &&
- (p->pkg->provider_priority == 0 && name->providers->num > 1)) {
- dbg_printf(" ignore: virtual package without provider_priority with >1 provider\n");
+ p->pkg->provider_priority == 0) {
+ dbg_printf(" ignore: virtual package without provider_priority\n");
continue;
}
if (compare_providers(ss, p, &chosen) > 0) {
@@ -779,7 +795,7 @@ static void cset_track_deps_added(struct apk_package *pkg)
struct apk_dependency *d;
foreach_array_item(d, pkg->depends) {
- if (d->conflict || !d->name->ss.installed_name)
+ if (apk_dep_conflict(d) || !d->name->ss.installed_name)
continue;
d->name->ss.installed_name->ss.requirers++;
}
@@ -791,7 +807,7 @@ static void cset_track_deps_removed(struct apk_solver_state *ss, struct apk_pack
struct apk_package *pkg0;
foreach_array_item(d, pkg->depends) {
- if (d->conflict || !d->name->ss.installed_name)
+ if (apk_dep_conflict(d) || !d->name->ss.installed_name)
continue;
if (--d->name->ss.installed_name->ss.requirers > 0)
continue;
@@ -822,10 +838,8 @@ static void cset_check_install_by_iif(struct apk_solver_state *ss, struct apk_na
foreach_array_item(dep0, pkg->install_if) {
struct apk_name *name0 = dep0->name;
- if (!name0->ss.in_changeset)
- return;
- if (!apk_dep_is_provided(dep0, &name0->ss.chosen))
- return;
+ if (!name0->ss.in_changeset) return;
+ if (!apk_dep_is_provided(dep0, &name0->ss.chosen)) return;
}
cset_gen_name_change(ss, name);
}
@@ -847,25 +861,50 @@ static void cset_check_removal_by_iif(struct apk_solver_state *ss, struct apk_na
}
}
+static void cset_check_by_reverse_iif(struct apk_solver_state *ss, struct apk_package *pkg, void (*cb)(struct apk_solver_state *ss, struct apk_name *))
+{
+ struct apk_name **pname;
+ struct apk_dependency *d;
+
+ if (!pkg) return;
+ foreach_array_item(pname, pkg->name->rinstall_if)
+ cb(ss, *pname);
+ foreach_array_item(d, pkg->provides)
+ foreach_array_item(pname, d->name->rinstall_if)
+ cb(ss, *pname);
+}
+
static void cset_gen_name_remove_orphan(struct apk_solver_state *ss, struct apk_name *name)
{
- struct apk_package *pkg = name->ss.chosen.pkg;
+ struct apk_provider *p;
if (name->ss.in_changeset) return;
name->ss.in_changeset = 1;
- if ((!pkg || pkg->name != name) && name->ss.installed_pkg)
+ dbg_printf("cset_gen_name_remove_orphans: %s\n", name->name);
+
+ /* Remove the package providing this name previously if it was provided
+ * by a package with different name. */
+ if (name->ss.installed_pkg && (!name->ss.chosen.pkg || name->ss.chosen.pkg->name != name))
cset_gen_name_remove(ss, name->ss.installed_pkg);
+
+ /* Remove any package that provides this name and is due to be deleted */
+ foreach_array_item(p, name->providers) {
+ struct apk_package *pkg0 = p->pkg;
+ struct apk_name *name0 = pkg0->name;
+ if (name0->ss.installed_pkg == pkg0 && name0->ss.chosen.pkg == NULL)
+ cset_gen_name_remove(ss, pkg0);
+ }
}
static void cset_gen_name_change(struct apk_solver_state *ss, struct apk_name *name)
{
- struct apk_name **pname;
struct apk_package *pkg, *opkg;
struct apk_dependency *d;
if (name->ss.in_changeset) return;
+ dbg_printf("cset_gen: processing: %s\n", name->name);
cset_gen_name_remove_orphan(ss, name);
pkg = name->ss.chosen.pkg;
@@ -877,19 +916,15 @@ static void cset_gen_name_change(struct apk_solver_state *ss, struct apk_name *n
cset_gen_name_remove_orphan(ss, d->name);
opkg = pkg->name->ss.installed_pkg;
- if (opkg) {
- foreach_array_item(pname, opkg->name->rinstall_if)
- cset_check_removal_by_iif(ss, *pname);
- }
+ cset_check_by_reverse_iif(ss, opkg, cset_check_removal_by_iif);
foreach_array_item(d, pkg->depends)
cset_gen_dep(ss, pkg, d);
- dbg_printf("Selecting: "PKG_VER_FMT"%s\n", PKG_VER_PRINTF(pkg), pkg->ss.pkg_selectable ? "" : " [NOT SELECTABLE]");
+ dbg_printf("cset_gen: selecting: "PKG_VER_FMT"%s\n", PKG_VER_PRINTF(pkg), pkg->ss.pkg_selectable ? "" : " [NOT SELECTABLE]");
record_change(ss, opkg, pkg);
- foreach_array_item(pname, pkg->name->rinstall_if)
- cset_check_install_by_iif(ss, *pname);
+ cset_check_by_reverse_iif(ss, pkg, cset_check_install_by_iif);
cset_track_deps_added(pkg);
if (opkg)
@@ -903,7 +938,7 @@ static void cset_gen_name_remove0(struct apk_package *pkg0, struct apk_dependenc
static void cset_gen_name_remove(struct apk_solver_state *ss, struct apk_package *pkg)
{
- struct apk_name *name = pkg->name, **pname;
+ struct apk_name *name = pkg->name;
if (pkg->ss.in_changeset ||
(name->ss.chosen.pkg != NULL &&
@@ -913,8 +948,8 @@ static void cset_gen_name_remove(struct apk_solver_state *ss, struct apk_package
name->ss.in_changeset = 1;
pkg->ss.in_changeset = 1;
apk_pkg_foreach_reverse_dependency(pkg, APK_FOREACH_INSTALLED|APK_DEP_SATISFIES, cset_gen_name_remove0, ss);
- foreach_array_item(pname, pkg->name->rinstall_if)
- cset_check_removal_by_iif(ss, *pname);
+ cset_check_by_reverse_iif(ss, pkg, cset_check_removal_by_iif);
+
record_change(ss, pkg, NULL);
cset_track_deps_removed(ss, pkg);
}
@@ -924,7 +959,7 @@ static void cset_gen_dep(struct apk_solver_state *ss, struct apk_package *ppkg,
struct apk_name *name = dep->name;
struct apk_package *pkg = name->ss.chosen.pkg;
- if (dep->conflict && ss->ignore_conflict)
+ if (apk_dep_conflict(dep) && ss->ignore_conflict)
return;
if (!apk_dep_is_provided(dep, &name->ss.chosen))
@@ -1001,20 +1036,23 @@ static int free_package(apk_hash_item item, void *ctx)
static int cmp_pkgname(const void *p1, const void *p2)
{
const struct apk_dependency *d1 = p1, *d2 = p2;
- return strcmp(d1->name->name, d2->name->name);
+ return apk_name_cmp_display(d1->name, d2->name);
}
static int compare_name_dequeue(const struct apk_name *a, const struct apk_name *b)
{
int r;
- r = (!!a->ss.requirers) - (!!b->ss.requirers);
+ r = !!(a->solver_flags_set) - !!(b->solver_flags_set);
if (r) return -r;
r = (int)a->priority - (int)b->priority;
if (r) return r;
r = a->ss.max_dep_chain - b->ss.max_dep_chain;
+ if (r) return -r;
+
+ r = (!!a->ss.requirers) - (!!b->ss.requirers);
return -r;
}
@@ -1093,6 +1131,11 @@ restart:
goto restart;
}
+ foreach_array_item(d, world) {
+ if (!d->name->ss.chosen.pkg) continue;
+ d->layer = d->name->ss.chosen.pkg->layer;
+ }
+
apk_hash_foreach(&db->available.names, free_name, NULL);
apk_hash_foreach(&db->available.packages, free_package, NULL);
dbg_printf("solver done, errors=%d\n", ss->errors);
diff --git a/src/io_archive.c b/src/tar.c
index ccd512a..55ad1ed 100644
--- a/src/io_archive.c
+++ b/src/tar.c
@@ -1,4 +1,4 @@
-/* io_archive.c - Alpine Package Keeper (APK)
+/* tar.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
@@ -7,27 +7,12 @@
* SPDX-License-Identifier: GPL-2.0-only
*/
-#include <stdio.h>
-
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <utime.h>
-#include <string.h>
-#include <unistd.h>
-#include <sysexits.h>
-#include <sys/wait.h>
#include <sys/stat.h>
-#include <sys/xattr.h>
#include <sys/sysmacros.h>
#include <limits.h>
-#include <stdint.h>
-#include <stdlib.h>
#include "apk_defines.h"
-#include "apk_print.h"
-#include "apk_archive.h"
-#include "apk_openssl.h"
+#include "apk_tar.h"
struct tar_header {
/* ustar header, Posix 1003.1 */
@@ -49,21 +34,24 @@ struct tar_header {
char padding[12]; /* 500-511 */
};
-#define TAR_BLOB(s) APK_BLOB_PTR_LEN(s, strnlen(s, sizeof(s)))
-#define GET_OCTAL(s) get_octal(s, sizeof(s))
-#define PUT_OCTAL(s,v) put_octal(s, sizeof(s), v)
+#define TAR_BLOB(s) APK_BLOB_PTR_LEN(s, strnlen(s, sizeof(s)))
+#define GET_OCTAL(s,r) get_octal(s, sizeof(s), r)
+#define PUT_OCTAL(s,v,hz) put_octal(s, sizeof(s), v, hz)
-static unsigned int get_octal(char *s, size_t l)
+static unsigned int get_octal(char *s, size_t l, int *r)
{
apk_blob_t b = APK_BLOB_PTR_LEN(s, l);
- return apk_blob_pull_uint(&b, 8);
+ unsigned int val = apk_blob_pull_uint(&b, 8);
+ while (b.len >= 1 && (b.ptr[0] == 0 || b.ptr[0] == 0x20)) b.ptr++, b.len--;
+ if (b.len != 0) *r = -APKE_V2PKG_FORMAT;
+ return val;
}
-static void put_octal(char *s, size_t l, size_t value)
+static void put_octal(char *s, size_t l, size_t value, int has_zero)
{
char *ptr = &s[l - 1];
- *(ptr--) = '\0';
+ if (has_zero) *(ptr--) = '\0';
while (value != 0 && ptr >= s) {
*(ptr--) = '0' + (value % 8);
value /= 8;
@@ -137,7 +125,7 @@ int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser,
apk_blob_t pax = APK_BLOB_NULL, longname = APK_BLOB_NULL;
char filename[sizeof buf.name + sizeof buf.prefix + 2];
- if (IS_ERR_OR_NULL(is)) return PTR_ERR(is) ?: -EINVAL;
+ if (IS_ERR(is)) return PTR_ERR(is);
memset(&entry, 0, sizeof(entry));
entry.name = buf.name;
@@ -147,20 +135,27 @@ int apk_tar_parse(struct apk_istream *is, apk_archive_entry_parser parser,
end++;
continue;
}
+ if (memcmp(buf.magic, "ustar", 5) != 0) {
+ r = -APKE_V2PKG_FORMAT;
+ goto err;
+ }
+ r = 0;
entry = (struct apk_file_info){
- .size = GET_OCTAL(buf.size),
- .uid = apk_id_cache_resolve_uid(idc, TAR_BLOB(buf.uname), GET_OCTAL(buf.uid)),
- .gid = apk_id_cache_resolve_gid(idc, TAR_BLOB(buf.gname), GET_OCTAL(buf.gid)),
- .mode = GET_OCTAL(buf.mode) & 07777,
- .mtime = GET_OCTAL(buf.mtime),
+ .size = GET_OCTAL(buf.size, &r),
+ .uid = apk_id_cache_resolve_uid(idc, TAR_BLOB(buf.uname), GET_OCTAL(buf.uid, &r)),
+ .gid = apk_id_cache_resolve_gid(idc, TAR_BLOB(buf.gname), GET_OCTAL(buf.gid, &r)),
+ .mode = GET_OCTAL(buf.mode, &r) & 07777,
+ .mtime = GET_OCTAL(buf.mtime, &r),
.name = entry.name,
.uname = buf.uname,
.gname = buf.gname,
- .device = makedev(GET_OCTAL(buf.devmajor),
- GET_OCTAL(buf.devminor)),
+ .device = makedev(GET_OCTAL(buf.devmajor, &r),
+ GET_OCTAL(buf.devminor, &r)),
.xattrs = entry.xattrs,
};
+ if (r != 0) goto err;
+
if (buf.prefix[0] && buf.typeflag != 'x' && buf.typeflag != 'g') {
snprintf(filename, sizeof filename, "%.*s/%.*s",
(int) sizeof buf.prefix, buf.prefix,
@@ -287,11 +282,11 @@ int apk_tar_write_entry(struct apk_ostream *os, const struct apk_file_info *ae,
strlcpy(buf.uname, ae->uname ?: "root", sizeof buf.uname);
strlcpy(buf.gname, ae->gname ?: "root", sizeof buf.gname);
- PUT_OCTAL(buf.size, ae->size);
- PUT_OCTAL(buf.uid, ae->uid);
- PUT_OCTAL(buf.gid, ae->gid);
- PUT_OCTAL(buf.mode, ae->mode & 07777);
- PUT_OCTAL(buf.mtime, ae->mtime ?: time(NULL));
+ PUT_OCTAL(buf.size, ae->size, 0);
+ PUT_OCTAL(buf.uid, ae->uid, 1);
+ PUT_OCTAL(buf.gid, ae->gid, 1);
+ PUT_OCTAL(buf.mode, ae->mode & 07777, 1);
+ PUT_OCTAL(buf.mtime, ae->mtime, 0);
/* Checksum */
strcpy(buf.magic, "ustar ");
@@ -299,7 +294,7 @@ int apk_tar_write_entry(struct apk_ostream *os, const struct apk_file_info *ae,
src = (const unsigned char *) &buf;
for (i = chksum = 0; i < sizeof(buf); i++)
chksum += src[i];
- put_octal(buf.chksum, sizeof(buf.chksum)-1, chksum);
+ put_octal(buf.chksum, sizeof(buf.chksum)-1, chksum, 1);
}
if (apk_ostream_write(os, &buf, sizeof(buf)) < 0)
@@ -331,111 +326,3 @@ int apk_tar_write_padding(struct apk_ostream *os, const struct apk_file_info *ae
return 0;
}
-
-int apk_archive_entry_extract(int atfd, const struct apk_file_info *ae,
- const char *extract_name, const char *link_target,
- struct apk_istream *is,
- apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx,
- unsigned int extract_flags, struct apk_out *out)
-{
- struct apk_xattr *xattr;
- const char *fn = extract_name ?: ae->name;
- int fd, r = -1, atflags = 0, ret = 0;
-
- if (!(extract_flags & APK_EXTRACTF_NO_OVERWRITE)) {
- if (unlinkat(atfd, fn, 0) != 0 && errno != ENOENT) return -errno;
- }
-
- switch (ae->mode & S_IFMT) {
- case S_IFDIR:
- r = mkdirat(atfd, fn, ae->mode & 07777);
- if (r < 0 && errno != EEXIST)
- ret = -errno;
- break;
- case S_IFREG:
- if (ae->link_target == NULL) {
- int flags = O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC | O_EXCL;
- struct apk_ostream *os = apk_ostream_to_fd(openat(atfd, fn, flags, ae->mode & 07777));
- apk_stream_copy(is, os, ae->size, cb, cb_ctx, dctx);
- r = apk_ostream_close(os);
- if (r < 0) ret = r;
- } else {
- r = linkat(atfd, link_target ?: ae->link_target, atfd, fn, 0);
- if (r < 0) ret = -errno;
- }
- break;
- case S_IFLNK:
- r = symlinkat(link_target ?: ae->link_target, atfd, fn);
- if (r < 0) ret = -errno;
- atflags |= AT_SYMLINK_NOFOLLOW;
- break;
- case S_IFBLK:
- case S_IFCHR:
- case S_IFIFO:
- r = mknodat(atfd, fn, ae->mode, ae->device);
- if (r < 0) ret = -errno;
- break;
- }
- if (ret) {
- apk_err(out, "Failed to create %s: %s", ae->name, strerror(-ret));
- return ret;
- }
-
- if (!(extract_flags & APK_EXTRACTF_NO_CHOWN)) {
- r = fchownat(atfd, fn, ae->uid, ae->gid, atflags);
- if (r < 0) {
- apk_err(out, "Failed to set ownership on %s: %s",
- fn, strerror(errno));
- if (!ret) ret = -errno;
- }
-
- /* chown resets suid bit so we need set it again */
- if (ae->mode & 07000) {
- r = fchmodat(atfd, fn, ae->mode & 07777, atflags);
- if (r < 0) {
- apk_err(out, "Failed to set file permissions on %s: %s",
- fn, strerror(errno));
- if (!ret) ret = -errno;
- }
- }
- }
-
- /* extract xattrs */
- if (!S_ISLNK(ae->mode) && ae->xattrs && ae->xattrs->num) {
- r = 0;
- fd = openat(atfd, fn, O_RDWR);
- if (fd >= 0) {
- foreach_array_item(xattr, ae->xattrs) {
- if (fsetxattr(fd, xattr->name, xattr->value.ptr, xattr->value.len, 0) < 0) {
- r = -errno;
- if (r != -ENOTSUP) break;
- }
- }
- close(fd);
- } else {
- r = -errno;
- }
- if (r) {
- if (r != -ENOTSUP)
- apk_err(out, "Failed to set xattrs on %s: %s",
- fn, strerror(-r));
- if (!ret) ret = r;
- }
- }
-
- if (!S_ISLNK(ae->mode)) {
- /* preserve modification time */
- struct timespec times[2];
-
- times[0].tv_sec = times[1].tv_sec = ae->mtime;
- times[0].tv_nsec = times[1].tv_nsec = 0;
- r = utimensat(atfd, fn, times, atflags);
- if (r < 0) {
- apk_err(out, "Failed to preserve modification time on %s: %s",
- fn, strerror(errno));
- if (!ret || ret == -ENOTSUP) ret = -errno;
- }
- }
-
- return ret;
-}
diff --git a/src/trust.c b/src/trust.c
index 5e2a956..c65377d 100644
--- a/src/trust.c
+++ b/src/trust.c
@@ -32,21 +32,19 @@ static int __apk_trust_load_pubkey(void *pctx, int dirfd, const char *filename)
return 0;
}
-int apk_trust_init(struct apk_trust *trust, int dirfd, struct apk_string_array *pkey_files)
+void apk_trust_init(struct apk_trust *trust)
{
- char **fn;
-
*trust = (struct apk_trust){};
apk_digest_ctx_init(&trust->dctx, APK_DIGEST_NONE);
list_init(&trust->trusted_key_list);
list_init(&trust->private_key_list);
- trust->initialized = 1;
- apk_dir_foreach_file(dirfd, __apk_trust_load_pubkey, trust);
+}
- foreach_array_item(fn, pkey_files) {
- struct apk_trust_key *key = apk_trust_load_key(AT_FDCWD, *fn);
- if (IS_ERR(key)) return PTR_ERR(key);
- list_add_tail(&key->key_node, &trust->private_key_list);
+int apk_trust_load_keys(struct apk_trust *trust, int dirfd)
+{
+ if (!trust->keys_loaded) {
+ trust->keys_loaded = 1;
+ apk_dir_foreach_file(dirfd, __apk_trust_load_pubkey, trust);
}
return 0;
@@ -66,8 +64,6 @@ static void __apk_trust_free_keys(struct list_head *h)
void apk_trust_free(struct apk_trust *trust)
{
- if (!trust->initialized) return;
- trust->initialized = 0;
__apk_trust_free_keys(&trust->trusted_key_list);
__apk_trust_free_keys(&trust->private_key_list);
apk_digest_ctx_free(&trust->dctx);
@@ -95,9 +91,19 @@ APK_OPT_GROUP(options_signing, "Signing", SIGNING_OPTIONS);
static int option_parse_signing(void *ctx, struct apk_ctx *ac, int optch, const char *optarg)
{
+ struct apk_trust *trust = &ac->trust;
+ struct apk_out *out = &ac->out;
+ struct apk_trust_key *key;
+
switch (optch) {
case OPT_SIGN_sign_key:
- *apk_string_array_add(&ac->private_keys) = (char*) optarg;
+ key = apk_trust_load_key(AT_FDCWD, optarg);
+ if (IS_ERR(key)) {
+ apk_err(out, "Failed to load signing key: %s: %s",
+ optarg, apk_error_str(PTR_ERR(key)));
+ return PTR_ERR(key);
+ }
+ list_add_tail(&key->key_node, &trust->private_key_list);
break;
default:
return -ENOTSUP;
diff --git a/src/version.c b/src/version.c
index 7c1c0b4..f496f75 100644
--- a/src/version.c
+++ b/src/version.c
@@ -6,143 +6,215 @@
*
* SPDX-License-Identifier: GPL-2.0-only
*/
-#include <stdio.h>
+#include <stdio.h>
#include <ctype.h>
+
#include "apk_defines.h"
#include "apk_version.h"
+#include "apk_ctype.h"
+
+#define DEBUG 0
-/* Gentoo version: {digit}{.digit}...{letter}{_suf{#}}...{-r#} */
+/* Alpine version: digit{.digit}...{letter}{_suf{#}}...{~hash}{-r#} */
enum PARTS {
- TOKEN_INVALID = -1,
- TOKEN_DIGIT_OR_ZERO,
+ TOKEN_INITIAL_DIGIT,
TOKEN_DIGIT,
TOKEN_LETTER,
TOKEN_SUFFIX,
TOKEN_SUFFIX_NO,
+ TOKEN_COMMIT_HASH,
TOKEN_REVISION_NO,
TOKEN_END,
+ TOKEN_INVALID,
};
-static void next_token(int *type, apk_blob_t *blob)
-{
- int n = TOKEN_INVALID;
+#define DECLARE_SUFFIXES(func) \
+ func(INVALID, "") \
+ func(ALPHA, "alpha") \
+ func(BETA, "beta") \
+ func(PRE, "pre") \
+ func(RC, "rc") \
+ func(NONE, "") \
+ func(CVS, "cvs") \
+ func(SVN, "svn") \
+ func(GIT, "git") \
+ func(HG, "hg") \
+ func(P, "p")
- if (blob->len == 0 || blob->ptr[0] == 0) {
- n = TOKEN_END;
- } else if ((*type == TOKEN_DIGIT || *type == TOKEN_DIGIT_OR_ZERO) &&
- islower(blob->ptr[0])) {
- n = TOKEN_LETTER;
- } else if (*type == TOKEN_LETTER && isdigit(blob->ptr[0])) {
- n = TOKEN_DIGIT;
- } else if (*type == TOKEN_SUFFIX && isdigit(blob->ptr[0])) {
- n = TOKEN_SUFFIX_NO;
- } else {
- switch (blob->ptr[0]) {
- case '.':
- n = TOKEN_DIGIT_OR_ZERO;
- break;
- case '_':
- n = TOKEN_SUFFIX;
- break;
- case '-':
- if (blob->len > 1 && blob->ptr[1] == 'r') {
- n = TOKEN_REVISION_NO;
- blob->ptr++;
- blob->len--;
- } else
- n = TOKEN_INVALID;
- break;
- }
- blob->ptr++;
- blob->len--;
- }
+#define SUFFIX_ENUM(n, str) SUFFIX_##n,
+enum {
+ SUFFIX_ENUM_START=-1,
+ DECLARE_SUFFIXES(SUFFIX_ENUM)
+};
- if (n < *type) {
- if (! ((n == TOKEN_DIGIT_OR_ZERO && *type == TOKEN_DIGIT) ||
- (n == TOKEN_SUFFIX && *type == TOKEN_SUFFIX_NO) ||
- (n == TOKEN_DIGIT && *type == TOKEN_LETTER)))
- n = TOKEN_INVALID;
- }
- *type = n;
-}
+struct token_state {
+ unsigned int token;
+ unsigned int suffix;
+ uint64_t number;
+ apk_blob_t value;
+};
-static int get_token(int *type, apk_blob_t *blob)
+static int suffix_value(apk_blob_t suf)
{
- static const char *pre_suffixes[] = { "alpha", "beta", "pre", "rc" };
- static const char *post_suffixes[] = { "cvs", "svn", "git", "hg", "p" };
- int v = 0, i = 0, nt = TOKEN_INVALID;
+#define SUFFIX_DEFINE(n, str) char suffix_##n[sizeof(str)];
+#define SUFFIX_ASSIGN(n, str) str,
+#define SUFFIX_INDEX(n, str) [SUFFIX_##n] = offsetof(struct suffix_literals, suffix_##n),
+ static const struct suffix_literals {
+ DECLARE_SUFFIXES(SUFFIX_DEFINE)
+ } suffixes = {
+ DECLARE_SUFFIXES(SUFFIX_ASSIGN)
+ };
+ static const unsigned short suffix_indexes[] = {
+ DECLARE_SUFFIXES(SUFFIX_INDEX)
+ sizeof(suffixes)
+ };
+ int val;
- if (blob->len <= 0) {
- *type = TOKEN_END;
- return 0;
+ if (suf.len == 0) return SUFFIX_NONE;
+ switch (suf.ptr[0]) {
+ case 'a': val = SUFFIX_ALPHA; break;
+ case 'b': val = SUFFIX_BETA; break;
+ case 'c': val = SUFFIX_CVS; break;
+ case 'g': val = SUFFIX_GIT; break;
+ case 'h': val = SUFFIX_HG; break;
+ case 'p': val = suf.len > 1 ? SUFFIX_PRE : SUFFIX_P; break;
+ case 'r': val = SUFFIX_RC; break;
+ case 's': val = SUFFIX_SVN; break;
+ default: return SUFFIX_INVALID;
}
+ char *ptr = (char *)&suffixes + suffix_indexes[val];
+ unsigned short len = suffix_indexes[val+1] - suffix_indexes[val] - 1;
+ if (apk_blob_compare(suf, APK_BLOB_PTR_LEN(ptr, len)) != 0)
+ return SUFFIX_INVALID;
+ return val;
+}
- switch (*type) {
- case TOKEN_DIGIT_OR_ZERO:
- /* Leading zero digits get a special treatment */
- if (blob->ptr[i] == '0') {
- while (i < blob->len && blob->ptr[i] == '0')
- i++;
- nt = TOKEN_DIGIT;
- v = -i;
- break;
- }
+static int token_cmp(struct token_state *ta, struct token_state *tb)
+{
+ uint64_t a, b;
+ int r;
+
+ switch (ta->token) {
case TOKEN_DIGIT:
+ if (ta->value.ptr[0] == '0' || tb->value.ptr[0] == '0') {
+ // if either of the digits have a leading zero, use
+ // raw string comparison similar to Gentoo spec
+ goto use_string_sort;
+ }
+ // fall throught to numeric comparison
+ case TOKEN_INITIAL_DIGIT:
case TOKEN_SUFFIX_NO:
case TOKEN_REVISION_NO:
- while (i < blob->len && isdigit(blob->ptr[i])) {
- v *= 10;
- v += blob->ptr[i++] - '0';
- }
+ a = ta->number;
+ b = tb->number;
break;
case TOKEN_LETTER:
- v = blob->ptr[i++];
+ a = ta->value.ptr[0];
+ b = tb->value.ptr[0];
break;
case TOKEN_SUFFIX:
- for (v = 0; v < ARRAY_SIZE(pre_suffixes); v++) {
- i = strlen(pre_suffixes[v]);
- if (i <= blob->len &&
- strncmp(pre_suffixes[v], blob->ptr, i) == 0)
- break;
- }
- if (v < ARRAY_SIZE(pre_suffixes)) {
- v = v - ARRAY_SIZE(pre_suffixes);
+ a = ta->suffix;
+ b = tb->suffix;
+ break;
+ use_string_sort:
+ default:
+ r = apk_blob_sort(ta->value, tb->value);
+ if (r < 0) return APK_VERSION_LESS;
+ if (r > 0) return APK_VERSION_GREATER;
+ return APK_VERSION_EQUAL;
+ }
+ if (a < b) return APK_VERSION_LESS;
+ if (a > b) return APK_VERSION_GREATER;
+ return APK_VERSION_EQUAL;
+}
+
+static void token_parse_digits(struct token_state *t, apk_blob_t *b)
+{
+ char *start = b->ptr;
+ t->number = apk_blob_pull_uint(b, 10);
+ t->value = APK_BLOB_PTR_LEN(start, b->ptr - start);
+ if (t->value.len == 0) t->token = TOKEN_INVALID;
+}
+
+static void token_first(struct token_state *t, apk_blob_t *b)
+{
+ t->token = TOKEN_INITIAL_DIGIT;
+ token_parse_digits(t, b);
+}
+
+static void token_next(struct token_state *t, apk_blob_t *b)
+{
+ if (b->len == 0) {
+ t->token = TOKEN_END;
+ return;
+ }
+ // determine the token type from the first letter and parse
+ // the content just as a blob. validate also that the previous
+ // token allows the subsequent token.
+ switch (b->ptr[0]) {
+ case 'a' ... 'z':
+ if (t->token > TOKEN_DIGIT) goto invalid;
+ t->value = APK_BLOB_PTR_LEN(b->ptr, 1);
+ t->token = TOKEN_LETTER;
+ b->ptr++, b->len--;
+ break;
+ case '.':
+ if (t->token > TOKEN_DIGIT) goto invalid;
+ b->ptr++, b->len--;
+ // fallthrough to parse number
+ case '0' ... '9':
+ switch (t->token) {
+ case TOKEN_INITIAL_DIGIT:
+ case TOKEN_DIGIT:
+ t->token = TOKEN_DIGIT;
break;
- }
- for (v = 0; v < ARRAY_SIZE(post_suffixes); v++) {
- i = strlen(post_suffixes[v]);
- if (i <= blob->len &&
- strncmp(post_suffixes[v], blob->ptr, i) == 0)
- break;
- }
- if (v < ARRAY_SIZE(post_suffixes))
+ case TOKEN_SUFFIX:
+ t->token = TOKEN_SUFFIX_NO;
break;
- /* fallthrough: invalid suffix */
+ default:
+ goto invalid;
+ }
+ token_parse_digits(t, b);
+ break;
+ case '_':
+ if (t->token > TOKEN_SUFFIX_NO) goto invalid;
+ b->ptr++, b->len--;
+ apk_blob_spn(*b, APK_CTYPE_VERSION_SUFFIX, &t->value, b);
+ t->suffix = suffix_value(t->value);
+ if (t->suffix == SUFFIX_INVALID) goto invalid;
+ t->token = TOKEN_SUFFIX;
+ break;
+ case '~':
+ if (t->token >= TOKEN_COMMIT_HASH) goto invalid;
+ b->ptr++, b->len--;
+ apk_blob_spn(*b, APK_CTYPE_HEXDIGIT, &t->value, b);
+ if (t->value.len == 0) goto invalid;
+ t->token = TOKEN_COMMIT_HASH;
+ break;
+ case '-':
+ if (t->token >= TOKEN_REVISION_NO) goto invalid;
+ if (!apk_blob_pull_blob_match(b, APK_BLOB_STRLIT("-r"))) goto invalid;
+ t->token = TOKEN_REVISION_NO;
+ token_parse_digits(t, b);
+ break;
+ invalid:
default:
- *type = TOKEN_INVALID;
- return -1;
+ t->token = TOKEN_INVALID;
+ break;
}
- blob->ptr += i;
- blob->len -= i;
- if (blob->len == 0)
- *type = TOKEN_END;
- else if (nt != TOKEN_INVALID)
- *type = nt;
- else
- next_token(type, blob);
-
- return v;
}
-const char *apk_version_op_string(int mask)
+const char *apk_version_op_string(int op)
{
- switch (mask) {
+ switch (op & ~APK_VERSION_CONFLICT) {
case APK_VERSION_LESS:
return "<";
case APK_VERSION_LESS|APK_VERSION_EQUAL:
return "<=";
+ case APK_VERSION_LESS|APK_VERSION_EQUAL|APK_VERSION_FUZZY:
+ return "<~";
case APK_VERSION_EQUAL|APK_VERSION_FUZZY:
case APK_VERSION_FUZZY:
return "~";
@@ -150,6 +222,8 @@ const char *apk_version_op_string(int mask)
return "=";
case APK_VERSION_GREATER|APK_VERSION_EQUAL:
return ">=";
+ case APK_VERSION_GREATER|APK_VERSION_EQUAL|APK_VERSION_FUZZY:
+ return ">~";
case APK_VERSION_GREATER:
return ">";
case APK_DEPMASK_CHECKSUM:
@@ -175,6 +249,9 @@ int apk_version_result_mask_blob(apk_blob_t op)
case '=':
r |= APK_VERSION_EQUAL;
break;
+ case '~':
+ r |= APK_VERSION_FUZZY|APK_VERSION_EQUAL;
+ break;
default:
return 0;
}
@@ -189,18 +266,15 @@ int apk_version_result_mask(const char *op)
int apk_version_validate(apk_blob_t ver)
{
- int t = TOKEN_DIGIT;
-
- while (t != TOKEN_END && t != TOKEN_INVALID)
- get_token(&t, &ver);
-
- return t == TOKEN_END;
+ struct token_state t;
+ for (token_first(&t, &ver); t.token < TOKEN_END; token_next(&t, &ver))
+ ;
+ return t.token == TOKEN_END;
}
-int apk_version_compare_blob_fuzzy(apk_blob_t a, apk_blob_t b, int fuzzy)
+static int apk_version_compare_fuzzy(apk_blob_t a, apk_blob_t b, int fuzzy)
{
- int at = TOKEN_DIGIT, bt = TOKEN_DIGIT, tt;
- int av = 0, bv = 0;
+ struct token_state ta, tb;
if (APK_BLOB_IS_NULL(a) || APK_BLOB_IS_NULL(b)) {
if (APK_BLOB_IS_NULL(a) && APK_BLOB_IS_NULL(b))
@@ -208,50 +282,48 @@ int apk_version_compare_blob_fuzzy(apk_blob_t a, apk_blob_t b, int fuzzy)
return APK_VERSION_EQUAL | APK_VERSION_GREATER | APK_VERSION_LESS;
}
- while (at == bt && at != TOKEN_END && at != TOKEN_INVALID && av == bv) {
- av = get_token(&at, &a);
- bv = get_token(&bt, &b);
-#if 0
- fprintf(stderr,
- "av=%d, at=%d, a.len=%d\n"
- "bv=%d, bt=%d, b.len=%d\n",
- av, at, a.len, bv, bt, b.len);
+ for (token_first(&ta, &a), token_first(&tb, &b);
+ ta.token == tb.token && ta.token < TOKEN_END;
+ token_next(&ta, &a), token_next(&tb, &b)) {
+ int r = token_cmp(&ta, &tb);
+#if DEBUG
+ fprintf(stderr, "at=%d <" BLOB_FMT "> bt=%d <" BLOB_FMT "> -> %d\n",
+ ta.token, BLOB_PRINTF(ta.value),
+ tb.token, BLOB_PRINTF(tb.value), r);
#endif
+ if (r != APK_VERSION_EQUAL) return r;
}
- /* value of this token differs? */
- if (av < bv)
- return APK_VERSION_LESS;
- if (av > bv)
- return APK_VERSION_GREATER;
+#if DEBUG
+ fprintf(stderr, "at=%d <" BLOB_FMT "> bt=%d <" BLOB_FMT ">\n",
+ ta.token, BLOB_PRINTF(ta.value),
+ tb.token, BLOB_PRINTF(tb.value));
+#endif
- /* both have TOKEN_END or TOKEN_INVALID next? */
- if (at == bt || fuzzy)
- return APK_VERSION_EQUAL;
+ /* both have TOKEN_END or TOKEN_INVALID next? or fuzzy matching the prefix*/
+ if (ta.token == tb.token) return APK_VERSION_EQUAL;
+ if (tb.token == TOKEN_END && fuzzy) return APK_VERSION_EQUAL;
/* leading version components and their values are equal,
* now the non-terminating version is greater unless it's a suffix
* indicating pre-release */
- tt = at;
- if (at == TOKEN_SUFFIX && get_token(&tt, &a) < 0)
- return APK_VERSION_LESS;
- tt = bt;
- if (bt == TOKEN_SUFFIX && get_token(&tt, &b) < 0)
- return APK_VERSION_GREATER;
- if (at > bt)
- return APK_VERSION_LESS;
- if (bt > at)
- return APK_VERSION_GREATER;
-
+ if (ta.token == TOKEN_SUFFIX && ta.suffix < SUFFIX_NONE) return APK_VERSION_LESS;
+ if (tb.token == TOKEN_SUFFIX && tb.suffix < SUFFIX_NONE) return APK_VERSION_GREATER;
+ if (ta.token > tb.token) return APK_VERSION_LESS;
+ if (tb.token > ta.token) return APK_VERSION_GREATER;
return APK_VERSION_EQUAL;
}
-int apk_version_compare_blob(apk_blob_t a, apk_blob_t b)
+int apk_version_compare(apk_blob_t a, apk_blob_t b)
{
- return apk_version_compare_blob_fuzzy(a, b, FALSE);
+ return apk_version_compare_fuzzy(a, b, FALSE);
}
-int apk_version_compare(const char *str1, const char *str2)
+int apk_version_match(apk_blob_t a, int op, apk_blob_t b)
{
- return apk_version_compare_blob(APK_BLOB_STR(str1), APK_BLOB_STR(str2));
+ int ok = 0;
+ if ((op & APK_DEPMASK_ANY) == APK_DEPMASK_ANY ||
+ apk_version_compare_fuzzy(a, b, op & APK_VERSION_FUZZY) & op) ok = 1;
+ if (op & APK_VERSION_CONFLICT) ok = !ok;
+ return ok;
}
diff --git a/test/Makefile b/test/Makefile
index 9834668..a48387d 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -50,9 +50,9 @@ repos.stamp: $(repos)
fi; \
if ! APK="$(APK)" ROOT="$(testroot)" SYSREPO="$(SYSREPO)" sh -ex ./$< > $(basename $@).out 2>&1; then\
echo " FAIL";\
- printf "------------- output -------------\n" >&2;\
+ printf '%s\n' "------------- output -------------" >&2;\
cat $(basename $@).out >&2;\
- printf "----------------------------------\n" >&2;\
+ printf '%s\n' "----------------------------------" >&2;\
exit 1;\
fi ;\
echo " OK" ;\
diff --git a/test/basic17.installed b/test/basic17.installed
new file mode 100644
index 0000000..ce66f47
--- /dev/null
+++ b/test/basic17.installed
@@ -0,0 +1,13 @@
+C:Q1EyN5AdpAOBJWKMR89pp/C66o+OE=
+P:a
+V:1
+S:1
+I:1
+D:b=1
+
+C:Q1C4uoV7SdMdDhYg4OCVmI71D8HIA=
+P:b
+V:1
+S:1
+I:1
+
diff --git a/test/basic17.repo b/test/basic17.repo
new file mode 100644
index 0000000..cac8d9e
--- /dev/null
+++ b/test/basic17.repo
@@ -0,0 +1,26 @@
+C:Q1EyN5AdpAOBJWKMR89pp/C66o+OE=
+P:a
+V:1
+S:1
+I:1
+D:b=1
+
+C:Q1eVpkasfqZAukAXFYbgwt4xAMZWU=
+P:a
+V:2
+S:1
+I:1
+D:b=2
+
+C:Q1C4uoV7SdMdDhYg4OCVmI71D8HIA=
+P:b
+V:1
+S:1
+I:1
+
+C:Q1hdUpqRv5mYgJEqW52UmVsvmyysE=
+P:b
+V:2
+S:1
+I:1
+
diff --git a/test/basic17.test b/test/basic17.test
new file mode 100644
index 0000000..3d0b7fc
--- /dev/null
+++ b/test/basic17.test
@@ -0,0 +1,9 @@
+@ARGS
+--test-repo basic17.repo
+--test-instdb basic17.installed
+--test-world b
+add --upgrade a
+@EXPECT
+(1/2) Upgrading b (1 -> 2)
+(2/2) Upgrading a (1 -> 2)
+OK: 0 MiB in 2 packages
diff --git a/test/basic18.test b/test/basic18.test
new file mode 100644
index 0000000..ff8d6b8
--- /dev/null
+++ b/test/basic18.test
@@ -0,0 +1,9 @@
+@ARGS
+--test-repo basic17.repo
+--test-instdb basic17.installed
+--test-world a
+add --latest b
+@EXPECT
+(1/2) Upgrading b (1 -> 2)
+(2/2) Upgrading a (1 -> 2)
+OK: 0 MiB in 2 packages
diff --git a/test/basic8.test b/test/basic8.test
index 19d3964..0042371 100644
--- a/test/basic8.test
+++ b/test/basic8.test
@@ -2,5 +2,6 @@
--no-network
add -t .virtual
@EXPECT
+WARNING: creating empty virtual package
(1/1) Installing .virtual (20190603.131426)
OK: 0 MiB in 0 packages
diff --git a/test/conflict.installed b/test/conflict.installed
new file mode 100644
index 0000000..60cdd3b
--- /dev/null
+++ b/test/conflict.installed
@@ -0,0 +1,14 @@
+C:Q1hdUpqRv5mYgJEqW52UmVsvmyysE=
+P:foo
+V:1
+S:1
+I:1
+D:cmd:b
+
+C:Q1hdOpqRv6mYgJEqW52UmVsvmyysE=
+P:bar
+V:1
+S:1
+I:1
+p:cmd:b=2
+
diff --git a/test/conflict2.repo b/test/conflict2.repo
new file mode 100644
index 0000000..99cb0df
--- /dev/null
+++ b/test/conflict2.repo
@@ -0,0 +1,20 @@
+C:Q1hdUpqRv5mYgJEqW52UmVsvmyysE=
+P:foo
+V:1
+S:1
+I:1
+D:cmd:b
+
+C:Q1hdOpqRv6mYgJEqW52UmVsvmyysE=
+P:bar
+V:1
+S:1
+I:1
+p:cmd:b=2
+
+C:Q1hdOpqRv7mYgJEqW52UmVsvmyysE=
+P:baz
+V:1
+S:1
+I:1
+p:cmd:b=1
diff --git a/test/conflict3.test b/test/conflict3.test
new file mode 100644
index 0000000..c39aa35
--- /dev/null
+++ b/test/conflict3.test
@@ -0,0 +1,9 @@
+@ARGS
+--test-repo conflict2.repo
+--test-instdb conflict.installed
+--test-world foo
+add baz
+@EXPECT
+(1/2) Purging bar (1)
+(2/2) Installing baz (1)
+OK: 0 MiB in 2 packages
diff --git a/test/fuzzy.repo b/test/fuzzy.repo
new file mode 100644
index 0000000..8569edd
--- /dev/null
+++ b/test/fuzzy.repo
@@ -0,0 +1,11 @@
+C:Q1EyN5AdpAOBJWKMR89pp/C66o+OE=
+P:a
+V:2.2
+S:1
+I:1
+
+C:Q1EyN5AdpAOBJWKMR89pprC66o+OE=
+P:a
+V:2.10
+S:1
+I:1
diff --git a/test/fuzzy1.test b/test/fuzzy1.test
new file mode 100644
index 0000000..3582bb1
--- /dev/null
+++ b/test/fuzzy1.test
@@ -0,0 +1,6 @@
+@ARGS
+--test-repo fuzzy.repo
+add a~2.2
+@EXPECT
+(1/1) Installing a (2.2)
+OK: 0 MiB in 0 packages
diff --git a/test/fuzzy2.test b/test/fuzzy2.test
new file mode 100644
index 0000000..6ef3a3f
--- /dev/null
+++ b/test/fuzzy2.test
@@ -0,0 +1,7 @@
+@ARGS
+--test-repo fuzzy.repo
+add a~2.4
+@EXPECT
+ERROR: unable to select packages:
+ a-2.10:
+ breaks: world[a~2.4]
diff --git a/test/fuzzy3.test b/test/fuzzy3.test
new file mode 100644
index 0000000..91fca5e
--- /dev/null
+++ b/test/fuzzy3.test
@@ -0,0 +1,6 @@
+@ARGS
+--test-repo fuzzy.repo
+add a>~2.5
+@EXPECT
+(1/1) Installing a (2.10)
+OK: 0 MiB in 0 packages
diff --git a/test/installif6.repo b/test/installif6.repo
new file mode 100644
index 0000000..02a126f
--- /dev/null
+++ b/test/installif6.repo
@@ -0,0 +1,34 @@
+C:Q1C4uoV7SdMdDhYg4OCVmI71D8HIA=
+P:qt5-qtbase
+V:1
+S:1
+I:1
+p:so:libQt5Core.so.5=1
+
+C:Q1hdUpqRv5mYgJEqW52UmVsvmyysF=
+P:wayland-libs-client
+V:1
+S:1
+I:1
+p:so:libwayland-client.so.0=1
+
+C:Q1EyN5AdpAOBJWKMR89pp/C66o+OE=
+P:peruse
+V:1
+S:1
+I:1
+D:so:libQt5Core.so.5
+
+C:Q1eVpkasfqZAukAXFYbgwt4xAMZWU=
+P:sway
+V:1
+S:1
+I:1
+D:so:libwayland-client.so.0
+
+C:Q1/hQ3eH2AguTwJVGOz+keypXhXKY=
+P:qt5-qtwayland
+V:1
+S:1
+I:1
+i:wayland-libs-client qt5-qtbase
diff --git a/test/installif6.test b/test/installif6.test
new file mode 100644
index 0000000..37297e9
--- /dev/null
+++ b/test/installif6.test
@@ -0,0 +1,10 @@
+@ARGS
+--test-repo installif6.repo
+add sway peruse
+@EXPECT
+(1/5) Installing qt5-qtbase (1)
+(2/5) Installing peruse (1)
+(3/5) Installing wayland-libs-client (1)
+(4/5) Installing qt5-qtwayland (1)
+(5/5) Installing sway (1)
+OK: 0 MiB in 0 packages
diff --git a/test/provides.repo b/test/provides.repo
index ed72f72..25a0d4e 100644
--- a/test/provides.repo
+++ b/test/provides.repo
@@ -133,3 +133,38 @@ S:1
I:1
k:1
p:pulseaudio=1 pulseaudio-alsa=1
+
+C:Q1FAFBeAhWPV1oiuybW+TSjrUghxU=
+P:sane-backends
+V:1
+S:1
+I:1
+
+C:Q1FAFBeAhWPV1asdfoW+TSjrUghxU=
+P:sane-backend-hpaio
+V:1
+S:1
+I:1
+i:sane-backends
+p:hplip-sane
+
+C:Q1FFN5AdpAOBJWKMR89pp/C66o+OE=
+P:testp
+V:0.1-r0
+S:1
+I:1
+p:foopkg
+
+C:Q1FFN5AdpAOBJWKMR89pp/C66o+FE=
+P:testq
+V:0.1-r0
+S:1
+I:1
+p:foopkg
+
+C:Q1FFpkasfqZAukAXFYbgwt4xAMZWU=
+P:testr
+V:0.1-r0
+S:1
+I:1
+D:foopkg
diff --git a/test/provides10.test b/test/provides10.test
index c832b75..91bf295 100644
--- a/test/provides10.test
+++ b/test/provides10.test
@@ -5,6 +5,8 @@ add conflicted-dep
@EXPECT
ERROR: unable to select packages:
conflicted-provider (virtual):
+ note: please select one of the 'provided by'
+ packages explicitly
provided by: conflicted-provider-a
conflicted-provider-b
required by: conflicted-dep-0.1[conflicted-provider]
diff --git a/test/provides11.test b/test/provides11.test
index 54c6b34..c48c33c 100644
--- a/test/provides11.test
+++ b/test/provides11.test
@@ -5,6 +5,8 @@ add conflicted-parent
@EXPECT
ERROR: unable to select packages:
conflicted-provider (virtual):
+ note: please select one of the 'provided by'
+ packages explicitly
provided by: conflicted-provider-a
conflicted-provider-b
required by: conflicted-dep-0.1[conflicted-provider]
diff --git a/test/provides14.test b/test/provides14.test
new file mode 100644
index 0000000..37cc4a4
--- /dev/null
+++ b/test/provides14.test
@@ -0,0 +1,10 @@
+@ARGS
+--test-repo provides.repo
+add hplip-sane sane-backends
+@EXPECT
+ERROR: unable to select packages:
+ hplip-sane (virtual):
+ note: please select one of the 'provided by'
+ packages explicitly
+ provided by: sane-backend-hpaio
+ required by: world[hplip-sane]
diff --git a/test/provides15.test b/test/provides15.test
new file mode 100644
index 0000000..62f8534
--- /dev/null
+++ b/test/provides15.test
@@ -0,0 +1,7 @@
+@ARGS
+--test-repo provides.repo
+add hplip-sane sane-backend-hpaio sane-backends nonexistent
+@EXPECT
+ERROR: unable to select packages:
+ nonexistent (no such package):
+ required by: world[nonexistent]
diff --git a/test/provides16.test b/test/provides16.test
new file mode 100644
index 0000000..cb63cf8
--- /dev/null
+++ b/test/provides16.test
@@ -0,0 +1,10 @@
+@ARGS
+--test-repo provides.repo
+add testr
+@EXPECT
+ERROR: unable to select packages:
+ foopkg (virtual):
+ note: please select one of the 'provided by'
+ packages explicitly
+ provided by: testp testq
+ required by: testr-0.1-r0[foopkg]
diff --git a/test/provides17.test b/test/provides17.test
new file mode 100644
index 0000000..30dfc1b
--- /dev/null
+++ b/test/provides17.test
@@ -0,0 +1,7 @@
+@ARGS
+--test-repo provides.repo
+add testp testr
+@EXPECT
+(1/2) Installing testp (0.1-r0)
+(2/2) Installing testr (0.1-r0)
+OK: 0 MiB in 0 packages
diff --git a/test/provides9.test b/test/provides9.test
index ce45a15..8df1bc8 100644
--- a/test/provides9.test
+++ b/test/provides9.test
@@ -4,6 +4,8 @@ add conflicted-provider
@EXPECT
ERROR: unable to select packages:
conflicted-provider (virtual):
+ note: please select one of the 'provided by'
+ packages explicitly
provided by: conflicted-provider-a
conflicted-provider-b
required by: world[conflicted-provider]
diff --git a/test/solver.sh b/test/solver.sh
index d14d89d..4c9f50d 100755
--- a/test/solver.sh
+++ b/test/solver.sh
@@ -10,9 +10,9 @@ TEST_TO_RUN="$@"
fail=0
pass=0
for test in ${TEST_TO_RUN:-*.test}; do
- get_block ARGS < $test | xargs $APK_TEST &> .$test.got
+ get_block ARGS < $test | xargs $APK_TEST > .$test.got 2>&1
- if ! get_block EXPECT < $test | cmp .$test.got &> /dev/null; then
+ if ! get_block EXPECT < $test | cmp .$test.got > /dev/null 2>&1; then
fail=$((fail+1))
echo "FAIL: $test"
get_block EXPECT < $test | diff -ru - .$test.got
diff --git a/test/version.data b/test/version.data
index 59d93cf..b6567db 100644
--- a/test/version.data
+++ b/test/version.data
@@ -726,3 +726,59 @@
1.3_pre1-r1 < 1.3.2
1.0_p10-r0 > 1.0_p9-r0
0.1.0_alpha_pre2 < 0.1.0_alpha
+1.0.0_pre20191002222144-r0 < 1.0.0_pre20210530193627-r0
+6.0_pre1 < 6.0
+6.1_pre1 < 6.1
+6.0_p1 > 6.0
+6.1_p1 > 6.1
+8.2.0 < 8.2.001
+8.2.0015 < 8.2.002
+
+1.0~1234 < 1.0~2345
+1.0~1234-r1 < 1.0~2345-r0
+1.0~1234-r1 > 1.0~1234-r0
+
+3.6.0 ~ 3.6
+3.6.9 ~ 3.6
+3.6_pre1 ~ 3.6
+3.6.0_pre1 ~ 3.6
+3.6.9_post1 ~ 3.6
+3.6.0 ~ 3.6.0
+3.5 <~ 3.6
+3.7 >~ 3.6
+3.6.0 !~ 3.8
+3.6.9 !~ 3.8
+3.8 !~ 3.8.1
+3.8.0 !~ 3.8.1
+3.8.0.1 !~ 3.8.1
+3.6_pre1 !~ 3.8
+3.6.0_pre1 !~ 3.8
+3.6.9_post1 !~ 3.8
+
+1.2
+0.1_pre2
+0.1_pre2~1234abcd
+0.1_p1_pre2
+0.1_alpha1_pre2
+0.1_git20240101_pre1
+!
+!0.1bc
+!0.1bc1
+!0.1a1
+!0.1a.1
+!0.1_pre2~
+!0.1_pre2~1234xbcd
+!0.1_pre2~1234abcd_pre1
+!0.1_pre2-r1~1234xbcd
+!0.1_foobar
+!0.1_foobar1
+!0.1-pre1.1
+!0.1-r
+!0.1-r2_pre1
+!0.1-r2_p3_pre1
+!0.1-r2-r3
+!0.1-r2.1
+!.1
+!a
+!_pre1
+!-r1
diff --git a/test/version.sh b/test/version.sh
index 18199a6..cbd3a4f 100755
--- a/test/version.sh
+++ b/test/version.sh
@@ -1,17 +1,12 @@
#!/bin/sh
-fail=0
-cat version.data | while read a result b rest ; do
- output="$(../src/apk version -t "$a" "$b")"
- if [ "$output" != "$result" ] ; then
- echo "$a $result $b, but got $output"
- fail=$(($fail+1))
- fi
-done
+../src/apk vertest < version.data
+fail=$?
-if [ "$fail" == "0" ]; then
+if [ "$fail" = "0" ]; then
echo "OK: version checking works"
+ exit 0
fi
-exit $fail
-
+echo "FAIL: $fail version checks failed"
+exit 1
diff --git a/tests/meson.build b/tests/meson.build
index a96a6cc..e6ae0c2 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -3,7 +3,7 @@ env.set('APK', apk_exe.full_path())
env.set('SRC', meson.current_source_dir())
test_program = find_program('test-basic.sh')
-test_list = run_command(test_program, '--list').stdout().split('\n')
+test_list = run_command(test_program, '--list', check: true).stdout().split('\n')
foreach test : test_list
test = test.strip()
diff --git a/tests/test-basic.sh b/tests/test-basic.sh
index 3d4fffe..5a09428 100644..100755
--- a/tests/test-basic.sh
+++ b/tests/test-basic.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-source ${SRC:-.}/test-lib.sh
+. ${SRC:-.}/test-lib.sh
t_case "help" && {
help_normal=$($APK version --help 2>/dev/null) || true
diff --git a/tests/version.data b/tests/version.data
index 59d93cf..bb1aa9e 100644
--- a/tests/version.data
+++ b/tests/version.data
@@ -726,3 +726,4 @@
1.3_pre1-r1 < 1.3.2
1.0_p10-r0 > 1.0_p9-r0
0.1.0_alpha_pre2 < 0.1.0_alpha
+1.0.0_pre20191002222144-r0 < 1.0.0_pre20210530193627-r0