aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Groffen <grobian@gentoo.org>2017-10-29 15:51:23 +0100
committerFabian Groffen <grobian@gentoo.org>2017-10-29 15:51:23 +0100
commitd598b947524aaba3bda162fc0d2cc97a9e0dcef6 (patch)
tree13de6e9697f196bb4a06e93dbad1be8ca45b167c
parentMerge remote-tracking branch 'overlays-gentoo-org/master' into prefix (diff)
parentUpdates for portage-2.3.13 release (diff)
downloadportage-d598b947.tar.gz
portage-d598b947.tar.bz2
portage-d598b947.zip
Merge remote-tracking branch 'overlays-gentoo-org/master' into prefix
-rw-r--r--.travis.yml2
-rw-r--r--NEWS7
-rw-r--r--RELEASE-NOTES21
-rwxr-xr-xbin/ebuild-helpers/prepstrip20
-rw-r--r--bin/install-qa-check.d/10ignored-flags5
-rwxr-xr-xbin/misc-functions.sh18
-rw-r--r--bin/postinst-qa-check.d/50gnome2-utils6
-rw-r--r--bin/postinst-qa-check.d/50xdg-utils12
l---------bin/preinst-qa-check.d/50gnome2-utils1
l---------bin/preinst-qa-check.d/50xdg-utils1
-rw-r--r--doc/config/bashrc.docbook15
-rw-r--r--man/ebuild.578
-rw-r--r--man/emerge.12
-rw-r--r--man/make.conf.54
-rw-r--r--man/portage.514
-rw-r--r--pym/_emerge/BinpkgExtractorAsync.py26
-rw-r--r--pym/_emerge/depgraph.py45
-rw-r--r--pym/_emerge/resolver/package_tracker.py87
-rw-r--r--pym/portage/checksum.py17
-rw-r--r--pym/portage/dbapi/porttree.py123
-rw-r--r--pym/portage/elog/mod_echo.py4
-rw-r--r--pym/portage/package/ebuild/doebuild.py1
-rw-r--r--pym/portage/sync/modules/rsync/rsync.py6
-rw-r--r--repoman/RELEASE-NOTES5
-rwxr-xr-xrepoman/setup.py2
-rwxr-xr-xsetup.py2
-rw-r--r--src/portage_util_file_copy_reflink_linux.c99
27 files changed, 435 insertions, 188 deletions
diff --git a/.travis.yml b/.travis.yml
index 2ae85b1d3..b77c92ee1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,6 +12,8 @@ install:
# python3.6+ has sha3 built-in, for older versions install pysha3
# (except for pypy where pysha3 is broken)
- "[[ ${TRAVIS_PYTHON_VERSION} == 3.[6789] || ${TRAVIS_PYTHON_VERSION} == pypy ]] || pip install pysha3"
+ # python3.6+ has blake2 built-in, for older versions install pyblake2
+ - "[[ ${TRAVIS_PYTHON_VERSION} == 3.[6789] ]] || pip install pyblake2"
# always install pygost for Streebog
- pip install pygost
diff --git a/NEWS b/NEWS
index 60a436522..c773530e3 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,12 @@
News (mainly features/major bug fixes)
+portage-2.3.12
+----------------
+* better_cache implemented to use less expensive os.listdir() instead of
+ os.stat() operations to scan for ebuilds. Avoids exhaustively scanning
+ overlays for all ebuilds which allows Portage to not slow down significantly
+ with lots of overlays enabled. (Daniel Robbins)
+
portage-2.3.7
-----------------
* eapply_user combines patch basenames from all matched directories into a
diff --git a/RELEASE-NOTES b/RELEASE-NOTES
index 81c54e550..e457919f3 100644
--- a/RELEASE-NOTES
+++ b/RELEASE-NOTES
@@ -1,6 +1,27 @@
Release Notes; upgrade information mainly.
Features/major bugfixes are listed in NEWS
+portage-2.3.13
+==================================
+* Bug Fixes:
+ - Bug 497596 fix PORTAGE_RSYNC_RETRIES
+ - Bug 635116 is_prelinkable_elf: fix for python3
+ - Bug 635126 file_copy: use sendfile return value to measure bytes copied
+ - Bug 635474 postinst_qa_check: initialize preinst state
+
+
+portage-2.3.12
+==================================
+* Bug Fixes:
+ - Bug 455232 fix ignored LDFLAGS check, enabled by adding
+ "-Wl,--defsym=__gentoo_check_ldflags__=0" to LDFLAGS
+ - Bug 630132 remove trailer when decompressing binary packages
+ - Bug 633842 PORTAGE_LOG_FILE KeyError
+ - Bug 634210 optimize portdbapi performance to handle large numbers of
+ repositories (Daniel Robbins)
+ - Bug 634378 use debugedit from rpm if necessary
+
+
portage-2.3.11
==================================
* Bug Fixes:
diff --git a/bin/ebuild-helpers/prepstrip b/bin/ebuild-helpers/prepstrip
index 768620e3c..62daf817e 100755
--- a/bin/ebuild-helpers/prepstrip
+++ b/bin/ebuild-helpers/prepstrip
@@ -84,7 +84,19 @@ esac
prepstrip_sources_dir=${EPREFIX}/usr/src/debug/${CATEGORY}/${PF}
-type -P debugedit >/dev/null && debugedit_found=true || debugedit_found=false
+debugedit=$(type -P debugedit)
+if [[ -z ${debugedit} ]]; then
+ debugedit_paths=(
+ "${EPREFIX}/usr/libexec/rpm/debugedit"
+ )
+ for x in "${debugedit_paths[@]}"; do
+ if [[ -x ${x} ]]; then
+ debugedit=${x}
+ break
+ fi
+ done
+fi
+[[ ${debugedit} ]] && debugedit_found=true || debugedit_found=false
debugedit_warned=false
__multijob_init
@@ -101,8 +113,8 @@ save_elf_sources() {
if ! ${debugedit_found} ; then
if ! ${debugedit_warned} ; then
debugedit_warned=true
- ewarn "FEATURES=installsources is enabled but the debugedit binary could not"
- ewarn "be found. This feature will not work unless debugedit is installed!"
+ ewarn "FEATURES=installsources is enabled but the debugedit binary could not be"
+ ewarn "found. This feature will not work unless debugedit or rpm is installed!"
fi
return 0
fi
@@ -112,7 +124,7 @@ save_elf_sources() {
# since we're editing the ELF here, we should recompute the build-id
# (the -i flag below). save that output so we don't need to recompute
# it later on in the save_elf_debug step.
- buildid=$(debugedit -i \
+ buildid=$("${debugedit}" -i \
-b "${WORKDIR}" \
-d "${prepstrip_sources_dir}" \
-l "${tmpdir}/sources/${x##*/}.${BASHPID:-$(__bashpid)}" \
diff --git a/bin/install-qa-check.d/10ignored-flags b/bin/install-qa-check.d/10ignored-flags
index 7aa9eb695..28aec6787 100644
--- a/bin/install-qa-check.d/10ignored-flags
+++ b/bin/install-qa-check.d/10ignored-flags
@@ -64,9 +64,10 @@ ignored_flag_check() {
fi
# Check for files built without respecting LDFLAGS
- if [[ "${LDFLAGS}" == *,--hash-style=gnu* ]] && \
+ if [[ "${LDFLAGS}" == *,--defsym=__gentoo_check_ldflags__* ]] && \
! has binchecks ${RESTRICT} ; then
- f=$(scanelf -qyRF '#k%p' -k .hash "${ED}")
+ f=$(LC_ALL=C comm -3 <(scanelf -qyRF '#k%p' -k .dynsym "${ED}" | LC_ALL=C sort) \
+ <(scanelf -qyRF '#s%p' -s __gentoo_check_ldflags__ "${ED}" | LC_ALL=C sort))
if [[ -n ${f} ]] ; then
echo "${f}" > "${T}"/scanelf-ignored-LDFLAGS.log
if [ "${QA_STRICT_FLAGS_IGNORED-unset}" = unset ] ; then
diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh
index 20fd4eef8..702f1ff4a 100755
--- a/bin/misc-functions.sh
+++ b/bin/misc-functions.sh
@@ -680,8 +680,12 @@ install_qa_check_xcoff() {
fi
}
+preinst_qa_check() {
+ postinst_qa_check preinst
+}
+
postinst_qa_check() {
- local d f paths qa_checks=()
+ local d f paths qa_checks=() PORTAGE_QA_PHASE=${1:-postinst}
if ! ___eapi_has_prefix_variables; then
local EPREFIX= EROOT=${ROOT}
fi
@@ -691,23 +695,23 @@ postinst_qa_check() {
# Collect the paths for QA checks, highest prio first.
paths=(
# sysadmin overrides
- "${PORTAGE_OVERRIDE_EPREFIX}"/usr/local/lib/postinst-qa-check.d
+ "${PORTAGE_OVERRIDE_EPREFIX}"/usr/local/lib/${PORTAGE_QA_PHASE}-qa-check.d
# system-wide package installs
- "${PORTAGE_OVERRIDE_EPREFIX}"/usr/lib/postinst-qa-check.d
+ "${PORTAGE_OVERRIDE_EPREFIX}"/usr/lib/${PORTAGE_QA_PHASE}-qa-check.d
)
# Now repo-specific checks.
# (yes, PORTAGE_ECLASS_LOCATIONS contains repo paths...)
for d in "${PORTAGE_ECLASS_LOCATIONS[@]}"; do
paths+=(
- "${d}"/metadata/postinst-qa-check.d
+ "${d}"/metadata/${PORTAGE_QA_PHASE}-qa-check.d
)
done
paths+=(
# Portage built-in checks
- "${PORTAGE_OVERRIDE_EPREFIX}"/usr/lib/portage/postinst-qa-check.d
- "${PORTAGE_BIN_PATH}"/postinst-qa-check.d
+ "${PORTAGE_OVERRIDE_EPREFIX}"/usr/lib/portage/${PORTAGE_QA_PHASE}-qa-check.d
+ "${PORTAGE_BIN_PATH}"/${PORTAGE_QA_PHASE}-qa-check.d
)
# Collect file names of QA checks. We need them early to support
@@ -732,7 +736,7 @@ postinst_qa_check() {
# Allow inheriting eclasses.
# XXX: we want this only in repository-wide checks.
_IN_INSTALL_QA_CHECK=1
- source "${d}/${f}" || eerror "Post-postinst QA check ${f} failed to run"
+ source "${d}/${f}" || eerror "Post-${PORTAGE_QA_PHASE} QA check ${f} failed to run"
)
done < <(printf "%s\0" "${qa_checks[@]}" | LC_ALL=C sort -u -z)
}
diff --git a/bin/postinst-qa-check.d/50gnome2-utils b/bin/postinst-qa-check.d/50gnome2-utils
index 7f1b0b847..dacc19a43 100644
--- a/bin/postinst-qa-check.d/50gnome2-utils
+++ b/bin/postinst-qa-check.d/50gnome2-utils
@@ -33,6 +33,12 @@ gnome2_icon_cache_check() {
fi
done
+ # preinst initializes the baseline state for the posinst check
+ [[ ${PORTAGE_QA_PHASE} == preinst ]] && return
+
+ # parallel-install makes it impossible to blame a specific package
+ has parallel-install ${FEATURES} && return
+
# The eqatag call is prohibitively expensive if the cache is
# missing and there are a large number of files.
if [[ -z ${missing} && ${all_files[@]} ]]; then
diff --git a/bin/postinst-qa-check.d/50xdg-utils b/bin/postinst-qa-check.d/50xdg-utils
index d1285caf4..7094e75a1 100644
--- a/bin/postinst-qa-check.d/50xdg-utils
+++ b/bin/postinst-qa-check.d/50xdg-utils
@@ -29,6 +29,12 @@ xdg_desktop_database_check() {
fi
done
+ # preinst initializes the baseline state for the posinst check
+ [[ ${PORTAGE_QA_PHASE} == preinst ]] && return
+
+ # parallel-install makes it impossible to blame a specific package
+ has parallel-install ${FEATURES} && return
+
# The eqatag call is prohibitively expensive if the cache is
# missing and there are a large number of files.
if [[ -z ${missing} && ${all_files[@]} ]]; then
@@ -66,6 +72,12 @@ xdg_mimeinfo_database_check() {
fi
done
+ # preinst initializes the baseline state for the posinst check
+ [[ ${PORTAGE_QA_PHASE} == preinst ]] && return
+
+ # parallel-install makes it impossible to blame a specific package
+ has parallel-install ${FEATURES} && return
+
# The eqatag call is prohibitively expensive if the cache is
# missing and there are a large number of files.
if [[ -z ${missing} && ${all_files[@]} ]]; then
diff --git a/bin/preinst-qa-check.d/50gnome2-utils b/bin/preinst-qa-check.d/50gnome2-utils
new file mode 120000
index 000000000..ee57f814d
--- /dev/null
+++ b/bin/preinst-qa-check.d/50gnome2-utils
@@ -0,0 +1 @@
+../postinst-qa-check.d/50gnome2-utils \ No newline at end of file
diff --git a/bin/preinst-qa-check.d/50xdg-utils b/bin/preinst-qa-check.d/50xdg-utils
new file mode 120000
index 000000000..16f68a471
--- /dev/null
+++ b/bin/preinst-qa-check.d/50xdg-utils
@@ -0,0 +1 @@
+../postinst-qa-check.d/50xdg-utils \ No newline at end of file
diff --git a/doc/config/bashrc.docbook b/doc/config/bashrc.docbook
index f36fec5e6..8f6573432 100644
--- a/doc/config/bashrc.docbook
+++ b/doc/config/bashrc.docbook
@@ -27,4 +27,19 @@
will be called after src_compile.
</para>
</sect1>
+ <sect1 id='config-bashrc-ebuild-die-hooks'>
+ <title>Ebuild Die Hooks</title>
+ <para>
+ The register_die_hook function registers one or more names of functions
+ to call when the ebuild fails for any reason, including file collisions
+ with other packages.
+ </para>
+ </sect1>
+ <sect1 id='config-bashrc-ebuild-success-hooks'>
+ <title>Ebuild Success Hooks</title>
+ <para>
+ The register_success_hook function registers one or more names of
+ functions to call when the ebuild builds and/or installs successfully.
+ </para>
+ </sect1>
</chapter>
diff --git a/man/ebuild.5 b/man/ebuild.5
index f5ee79364..a88db1a2f 100644
--- a/man/ebuild.5
+++ b/man/ebuild.5
@@ -1,4 +1,4 @@
-.TH "EBUILD" "5" "Nov 2014" "Portage VERSION" "Portage"
+.TH "EBUILD" "5" "Oct 2017" "Portage VERSION" "Portage"
.SH "NAME"
ebuild \- the internal format, variables, and functions in an ebuild script
@@ -730,7 +730,7 @@ allows for packages to depend on \fIvirtual/jdk\fR rather than on blackdown
or sun specifically.
The \fBPROVIDE\fR variable has been deprecated. See
-\fIhttps://wiki.gentoo.org/wiki/GLEP:37\fR for details.
+\fIhttps://www.gentoo.org/glep/glep-0037.html\fR for details.
.TP
.B DOCS
@@ -1110,16 +1110,6 @@ Example:
installed)
.fi
-.SS "Hooks:"
-.TP
-.B register_die_hook\fR \fI[list of function names]
-Register one or more functions to call when the ebuild fails for any reason,
-including file collisions with other packages.
-.TP
-.B register_success_hook\fR \fI[list of function names]
-Register one or more functions to call when the ebuild builds and/or installs
-successfully.
-
.SS "Output:"
.TP
.B einfo\fR \fI"disposable message"
@@ -1231,70 +1221,6 @@ Please do \fBnot\fR use this in place of 'emake install DESTDIR=${D}'.
That is the preferred way of installing make\-based packages. Also, do
not utilize the \fIEXTRA_EINSTALL\fR variable since it is for users.
-.PD 0
-.TP
-.B prepall
-.TP
-.B prepalldocs
-.TP
-.B prepallinfo
-.TP
-.B prepallman
-.TP
-.B prepallstrip
-.PD 1
-Useful for when a package installs into \fB${D}\fR via scripts
-(i.e. makefiles). If you want to be sure that libraries are executable,
-aclocal files are installed into the right place, doc/info/man files are
-all compressed, and that executables are all stripped of debugging symbols,
-then use these suite of functions.
-.RS
-.PD 0
-.TP
-.B prepall:
-Runs \fBprepallman\fR, \fBprepallinfo\fR, \fBprepallstrip\fR, sets
-libraries +x, and then checks aclocal directories. Please note this
-does \fI*not*\fR run \fBprepalldocs\fR.
-.TP
-.B prepalldocs:
-Compresses all doc files in ${ED}/usr/share/doc.
-.TP
-.B prepallinfo:
-Compresses all info files in ${ED}/usr/share/info.
-.TP
-.B prepallman:
-Compresses all man files in ${ED}/usr/share/man.
-.TP
-.B prepallstrip:
-Strips all executable files of debugging symboles. This includes libraries.
-.RE
-
-.TP
-.B prepinfo\fR \fI[dir]
-.TP
-.B prepman\fR \fI[dir]
-.TP
-.B prepstrip\fR \fI[dir]
-.PD 1
-Similar to the \fBprepall\fR functions, these are subtle in their differences.
-.RS
-.PD 0
-.TP
-.B prepinfo:
-If a \fIdir\fR is not specified, then \fBprepinfo\fR will assume the dir
-\fIusr\fR. \fBprepinfo\fR will then compress all the files in
-${ED}/\fIdir\fR/info.
-.TP
-.B prepman:
-If a \fIdir\fR is not specified, then \fBprepman\fR will assume the dir
-\fIusr\fR. \fBprepman\fR will then compress all the files in
-${ED}/\fIdir\fR/man/*/.
-.TP
-.B prepstrip:
-All the files found in ${ED}/\fIdir\fR will be stripped. You may specify
-multiple directories.
-.RE
-.PD 1
.TP
.B docompress\fR \fI[\-x] <path> [list of more paths]
.RS
diff --git a/man/emerge.1 b/man/emerge.1
index 84b033bac..1f333530b 100644
--- a/man/emerge.1
+++ b/man/emerge.1
@@ -108,7 +108,7 @@ later updating.
.BR \-\-check\-news
Scan all repositories for relevant unread GLEP 42 news items, and display
how many are found. See
-\fIhttps://wiki.gentoo.org/wiki/GLEP:42\fR.
+\fIhttps://www.gentoo.org/glep/glep-0042.html\fR.
.TP
.BR \-\-clean
Cleans up the system by examining the installed packages and removing older
diff --git a/man/make.conf.5 b/man/make.conf.5
index 653e8bc12..b9051830f 100644
--- a/man/make.conf.5
+++ b/man/make.conf.5
@@ -62,7 +62,7 @@ with the '@' symbol. License groups are defined in the \fIlicense_groups\fR
file (see \fBportage\fR(5)). In addition to license and group names, the
\fI*\fR and \fI-*\fR wildcard tokens are also supported. Refer to GLEP 23
for further information:
-\fIhttps://wiki.gentoo.org/wiki/GLEP:23\fR.
+\fIhttps://www.gentoo.org/glep/glep-0023.html\fR.
.br
Defaults to the value of * -@EULA.
.br
@@ -506,7 +506,7 @@ configures new enough distcc to use the proxy.
.TP
.B news
Enable GLEP 42 news support. See
-\fIhttps://wiki.gentoo.org/wiki/GLEP:42\fR.
+\fIhttps://www.gentoo.org/glep/glep-0042.html\fR.
.TP
.B noauto
When utilizing \fBebuild\fR(1), only run the function requested. Also, forces
diff --git a/man/portage.5 b/man/portage.5
index 5f1f2bbb0..89dc8ce44 100644
--- a/man/portage.5
+++ b/man/portage.5
@@ -1296,7 +1296,7 @@ version is followed by a hyphen and an integer build\-id.
.RS
.I Example:
.nf
-# Specify the repository name (overriding profils/repo_name).
+# Specify the repository name (overriding profiles/repo_name).
repo\-name = foo-overlay
# eclasses provided by java-overlay take precedence over identically named
@@ -1310,7 +1310,7 @@ aliases = foo-overlay
eapis\-banned = 0 1
# indicate that ebuilds with the specified EAPIs are deprecated
-eapis\-deprecated = 2 3
+eapis\-deprecated = 2 3 4
# sign commits in this repo, which requires Git >=1.7.9, and
# key configured by `git config user.signingkey key_id`
@@ -1330,11 +1330,11 @@ use\-manifests = strict
manifest\-hashes = SHA256 SHA512 WHIRLPOOL
# indicate that this repo enables repoman's --echangelog=y option automatically
-update\-changelog = true
+update\-changelog = false
-# indicate that this repo contains both md5-dict and pms cache formats,
+# indicate that this repo contains the md5-dict cache format,
# which may be generated by egencache(1)
-cache\-formats = md5-dict pms
+cache\-formats = md5-dict
# indicate that this repo contains profiles that may use directories for
# package.mask, package.provided, package.use, package.use.force,
@@ -1408,7 +1408,7 @@ A list of all the variables which will be displayed when you run `emerge info`.
This contains groups of licenses that may be specifed in the
\fBACCEPT_LICENSE\fR variable (see \fBmake.conf\fR(5)). Refer
to GLEP 23 for further information:
-\fIhttps://wiki.gentoo.org/wiki/GLEP:23\fR.
+\fIhttps://www.gentoo.org/glep/glep-0023.html\fR.
.I Format:
.nf
@@ -1542,7 +1542,7 @@ All local USE flags are listed here along with the package and a
description. This file is automatically generated from the
metadata.xml files that are included with each individual package.
Refer to GLEP 56 for further information:
-\fIhttps://wiki.gentoo.org/wiki/GLEP:56\fR.
+\fIhttps://www.gentoo.org/glep/glep-0056.html\fR.
.nf
.I Format:
diff --git a/pym/_emerge/BinpkgExtractorAsync.py b/pym/_emerge/BinpkgExtractorAsync.py
index e85f4ecac..07ba2a1b7 100644
--- a/pym/_emerge/BinpkgExtractorAsync.py
+++ b/pym/_emerge/BinpkgExtractorAsync.py
@@ -74,21 +74,27 @@ class BinpkgExtractorAsync(SpawnProcess):
self._async_wait()
return
- # Add -q to decomp_cmd opts, in order to avoid "trailing garbage
- # after EOF ignored" warning messages due to xpak trailer.
+ pkg_xpak = portage.xpak.tbz2(self.pkg_path)
+ pkg_xpak.scan()
+
# SIGPIPE handling (128 + SIGPIPE) should be compatible with
# assert_sigpipe_ok() that's used by the ebuild unpack() helper.
self.args = [self._shell_binary, "-c",
- ("%s -cq -- %s | tar -xp %s -C %s -f - ; " + \
- "p=(${PIPESTATUS[@]}) ; " + \
- "if [[ ${p[0]} != 0 && ${p[0]} != %d ]] ; then " % (128 + signal.SIGPIPE) + \
- "echo bzip2 failed with status ${p[0]} ; exit ${p[0]} ; fi ; " + \
- "if [ ${p[1]} != 0 ] ; then " + \
- "echo tar failed with status ${p[1]} ; exit ${p[1]} ; fi ; " + \
+ ("cmd0=(head -c-%d -- %s) cmd1=(%s) cmd2=(tar -xp %s -C %s -f -); " + \
+ '"${cmd0[@]}" | "${cmd1[@]}" | "${cmd2[@]}"; ' + \
+ "p=(${PIPESTATUS[@]}) ; for i in {0..2}; do " + \
+ "if [[ ${p[$i]} != 0 && ${p[$i]} != %d ]] ; then " + \
+ "echo command $(eval \"echo \\\"'\\${cmd$i[*]}'\\\"\") " + \
+ "failed with status ${p[$i]} ; exit ${p[$i]} ; fi ; done; " + \
+ "if [ ${p[$i]} != 0 ] ; then " + \
+ "echo command $(eval \"echo \\\"'\\${cmd$i[*]}'\\\"\") " + \
+ "failed with status ${p[$i]} ; exit ${p[$i]} ; fi ; " + \
"exit 0 ;") % \
- (decomp_cmd,
+ (pkg_xpak.xpaksize,
portage._shell_quote(self.pkg_path),
+ decomp_cmd,
tar_options,
- portage._shell_quote(self.image_dir))]
+ portage._shell_quote(self.image_dir),
+ 128 + signal.SIGPIPE)]
SpawnProcess._start(self)
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
index 16cf70d45..c37423f0c 100644
--- a/pym/_emerge/depgraph.py
+++ b/pym/_emerge/depgraph.py
@@ -357,6 +357,47 @@ class _rebuild_config(object):
class _dynamic_depgraph_config(object):
+ """
+ ``dynamic_depgraph_config`` is an object that is used to collect settings and important data structures that are
+ used in calculating Portage dependencies. Each depgraph created by the depgraph.py code gets its own
+ ``dynamic_depgraph_config``, whereas ``frozen_depgraph_config`` is shared among all depgraphs.
+
+ **self.digraph**
+
+ Of particular importance is the instance variable ``self.digraph``, which is an instance of
+ ``portage.util.digraph``, a directed graph data structure. ``portage.util.digraph`` is used for a variety of
+ purposes in the Portage codebase, but in this particular scenario as ``self.digraph``, it is used to create a
+ dependency tree of Portage packages. So for ``self.digraph``, each *node* of the directed graph is a ``Package``,
+ while *edges* connect nodes and each edge can have a Priority. The Priority setting is used to help resolve
+ circular dependencies, and should be interpreted in the direction of parent to child.
+
+ Conceptually, think of ``self.digraph`` as containing user-specified packages or sets at the very top, with
+ dependencies hanging down as children, and dependencies of those children as children of children, etc. The depgraph
+ is intended to model dependency relationships, not the order that packages should be installed.
+
+ **resolving the digraph**
+
+ To convert a digraph to an ordered list of packages to merge in an order where all dependencies are properly
+ satisfied, we would first start by looking at leaf nodes, which are nodes that have no dependencies of their own. We
+ could then traverse the digraph upwards from the leaf nodes, towards the parents. Along the way, depending on emerge
+ options, we could make decisions what packages should be installed or rebuilt. This is how ``self.digraph`` is used
+ in the code.
+
+ **digraph creation**
+
+ The ``depgraph.py`` code creates the digraph by first adding emerge arguments to the digraph as the main parents,
+ so if ``@world`` is specified, then the world set is added as the main parents. Then, ``emerge`` will determine
+ the dependencies of these packages, and depending on what options are passed to ``emerge``, will look at installed
+ packages, binary packages and available ebuilds that could be merged to satisfy dependencies, and these will be
+ added as children in the digraph. Children of children will be added as dependencies as needed, depending on the
+ depth setting used by ``emerge``.
+
+ As the digraph is created, it is perfectly fine for Packages to be added to the digraph that conflict with one
+ another. After the digraph has been fully populated to the necessary depth, code within ``depgraph.py`` will
+ identify any conflicts that are modeled within the digraph and determine the best way to handle them.
+
+ """
+
def __init__(self, depgraph, myparams, allow_backtracking, backtrack_parameters):
self.myparams = myparams.copy()
self._vdb_loaded = False
@@ -4998,7 +5039,7 @@ class depgraph(object):
if atom.soname:
repo_list = [None]
elif atom.repo is None and hasattr(db, "getRepositories"):
- repo_list = db.getRepositories()
+ repo_list = db.getRepositories(catpkg=atom.cp)
else:
repo_list = [atom.repo]
@@ -5449,7 +5490,7 @@ class depgraph(object):
atom_set = InternalPackageSet(initial_atoms=(atom,),
allow_repo=True)
if atom.repo is None and hasattr(db, "getRepositories"):
- repo_list = db.getRepositories()
+ repo_list = db.getRepositories(catpkg=atom_exp.cp)
else:
repo_list = [atom.repo]
diff --git a/pym/_emerge/resolver/package_tracker.py b/pym/_emerge/resolver/package_tracker.py
index 398d4cf46..ccb0b11cf 100644
--- a/pym/_emerge/resolver/package_tracker.py
+++ b/pym/_emerge/resolver/package_tracker.py
@@ -31,38 +31,89 @@ class PackageConflict(_PackageConflict):
class PackageTracker(object):
"""
- This class tracks packages which are currently
- installed and packages which have been pulled into
- the dependency graph.
+ **Behavior**
- It automatically tracks conflicts between packages.
+ This section is intended to give you a good conceptual overview of the ``PackageTracker`` class and its general
+ behavior -- how you can expect it to behave and how in turn expects to be used successfully by the programmer.
- Possible conflicts:
- 1) Packages that share the same SLOT.
- 2) Packages with the same cpv.
- Not yet implemented:
- 3) Packages that block each other.
+ This class is used to model the behavior of a real Gentoo or other system using Portage for package management,
+ along with the installed and to-be-installed packages. The installed packages are ones that are already on the
+ system and recorded in ``/var/db/pkg``, while the to-be-installed packages are a group of packages that Portage is
+ considering installing on the system, based on the information in Portage's dependency graph. Multiple roots are
+ supported, so that situations can be modeled where ROOT is set to a non-default value (non-``/``).
+
+ You can use the add_pkg() method to add a to-be-merged package to the PackageTracker, and ``add_installed_pkg()`` to
+ add an already-installed package to the package tracker. Typical use of the package tracker involves the
+ ``depgraph.py`` code populating the package tracker with calls to ``add_installed_pkg()`` to add all installed
+ packages on the system, and then it is initialized and ready for use. At that point, ``depgraph.py`` can use
+ ``add_pkg()`` to add to-be-installed packages to the system.
+
+ It's worth mentioning that ``PackageTracker`` uses ``Package`` objects as arguments, and stores these objects
+ internally. There are parts of the code that ensure that a ``Package`` instance is added to the PackageTracker
+ only once.
+
+ Note that when a to-be-merged package is added to the package tracker via ``add_pkg()``, it will "cover up"
+ (replace) any installed package that shares the same root-catpkg-slot or root-catpkg-version, meaning that calling
+ the ``all_pkgs()`` or ``match()`` method will not return the installed package in the list. And the code does
+ support the scenario where ``add_installed_pkg(pkg2)`` is called *after* a call to ``add_pkg(pkg1)`` -- in this
+ case, if ``pkg1`` would 'cover up' ``pkg2``, this will be identified and handled correctly.
+
+ But the package tracker is designed to have an important behavior in this regard -- because PackageTracker has a
+ ``remove()`` method, these replaced/covered-up packages are not permanently removed -- so if you ``remove()`` a
+ to-be-installed package that was "replacing" an installed package, the installed package will "reappear". This
+ removal functionality is used by the slot conflict code in ``depgraph.py`` to modify the list of to-be-installed
+ packages as it addresses slot conflicts.
+
+ One of the main purposes of the PackageTracker is to detect conflicts between packages. Conflicts are detected
+ on to-be-installed packages only.
+
+ A slot conflict is a situation where a to-be-installed package is added to the package tracker via ``add_pkg()``,
+ and there is already a to-be-installed package added that has the same root, catpkg and slot. These cannot co-exist.
+
+ A cpv conflict is a situation where a to-be-installed package is added to the package tracker via ``add_pkg()``, and
+ there is already a to-be-installed package add that has the same root, catpkg, and version+revision. These cannot
+ co-exist.
+
+ The package tracker does not prevent slot and cpv conflicts from occurring. Instead, it allows them to be recorded
+ and the ``conflicts()`` and ``slot_conflicts()`` method will cause the package tracker to look at its internal data
+ structures and generate ``PackageConflict()`` objects for each conflict it finds.
+
+ The ``match()`` method is used extensively by ``depgraph.py`` to find packages that match a particular dependency
+ atom. The code now also supports soname dependencies.
+
+ **Future Functionality**
+
+ The package tracker may be extended in the future to track additional useful information:
+
+ * Packages that block one another. This information is not currently injected into the package tracker.
+
+ * Sub-slot conflicts. It is possible to identify situations where a to-be-installed package is in a new sub-slot.
+ In this case, the depgraph can be queried for parents of this dependency, and these parents can be scheduled
+ to be rebuilt.
+
+ :ivar _cp_pkg_map: The collection of to-be-installed (not yet merged) packages. We care about conflicts in these
+ packages.
+ :ivar _cp_vdb_pkg_map: The collection of already-installed packages.
+ :ivar _multi_pkgs: A list of keys in ``self._cp_pkg_map`` that have potential slot and cpv conflicts.
+ :ivar _replacing: The mechanism by which ``PackageTracker`` records to-be-installed packages that 'cover up'
+ already-installed packages. ``self._replacing[cp_key] = [ new_pkg_that_replaced_cp_key... ]``.
+ :ivar _replaced_by: ``self.replaced_by[cp_key] == [ replaced_pkg_1, replaced_pkg_2 ]``
"""
def __init__(self, soname_deps=False):
+
"""
- @param soname_deps: enable soname match support
- @type soname_deps: bool
+ :param soname_deps bool: Determines whether support for soname deps should be enabled or not.
"""
- # Mapping from package keys to set of packages.
+
self._cp_pkg_map = collections.defaultdict(list)
self._cp_vdb_pkg_map = collections.defaultdict(list)
- # List of package keys that may contain conflicts.
- # The insetation order must be preserved.
self._multi_pkgs = []
# Cache for result of conflicts().
self._conflicts_cache = None
- # Records for each pulled package which installed package
- # are replaced.
self._replacing = collections.defaultdict(list)
- # Records which pulled packages replace this package.
self._replaced_by = collections.defaultdict(list)
self._match_cache = collections.defaultdict(dict)
@@ -258,7 +309,7 @@ class PackageTracker(object):
Iterates over present slot conflicts.
This is only intended for consumers that haven't been
updated to deal with other kinds of conflicts.
- This funcion should be removed once all consumers are updated.
+ This function should be removed once all consumers are updated.
"""
return (conflict for conflict in self.conflicts() \
if conflict.description == "slot conflict")
diff --git a/pym/portage/checksum.py b/pym/portage/checksum.py
index ff132751b..5424ce56b 100644
--- a/pym/portage/checksum.py
+++ b/pym/portage/checksum.py
@@ -27,8 +27,8 @@ import tempfile
# SHA512: hashlib
# RMD160: hashlib, pycrypto, mhash
# WHIRLPOOL: hashlib, mhash, bundled
-# BLAKE2B (512): hashlib (3.6+), pycrypto
-# BLAKE2S (512): hashlib (3.6+), pycrypto
+# BLAKE2B (512): hashlib (3.6+), pyblake2, pycrypto
+# BLAKE2S (512): hashlib (3.6+), pyblake2, pycrypto
# SHA3_256: hashlib (3.6+), pysha3, pycrypto
# SHA3_512: hashlib (3.6+), pysha3, pycrypto
@@ -124,6 +124,17 @@ for local_name, hash_name in (
origin='hashlib')
+# Support using pyblake2 as fallback for python<3.6
+if "BLAKE2B" not in hashfunc_map or "BLAKE2S" not in hashfunc_map:
+ try:
+ import pyblake2
+
+ _generate_hash_function("BLAKE2B", pyblake2.blake2b, origin="pyblake2")
+ _generate_hash_function("BLAKE2S", pyblake2.blake2s, origin="pyblake2")
+ except ImportError:
+ pass
+
+
# Support using pysha3 as fallback for python<3.6
if "SHA3_256" not in hashfunc_map or "SHA3_512" not in hashfunc_map:
try:
@@ -304,7 +315,7 @@ def is_prelinkable_elf(filename):
finally:
f.close()
return (len(magic) == 17 and magic.startswith(b'\x7fELF') and
- magic[16] in (b'\x02', b'\x03')) # 2=ET_EXEC, 3=ET_DYN
+ magic[16:17] in (b'\x02', b'\x03')) # 2=ET_EXEC, 3=ET_DYN
def perform_md5(x, calc_prelink=0):
return perform_checksum(x, "MD5", calc_prelink)[0]
diff --git a/pym/portage/dbapi/porttree.py b/pym/portage/dbapi/porttree.py
index a3254d017..f5979d2d0 100644
--- a/pym/portage/dbapi/porttree.py
+++ b/pym/portage/dbapi/porttree.py
@@ -16,7 +16,7 @@ portage.proxy.lazyimport.lazyimport(globals(),
'portage.package.ebuild.doebuild:doebuild',
'portage.util:ensure_dirs,shlex_split,writemsg,writemsg_level',
'portage.util.listdir:listdir',
- 'portage.versions:best,catpkgsplit,_pkgsplit@pkgsplit,ver_regexp,_pkg_str',
+ 'portage.versions:best,catsplit,catpkgsplit,_pkgsplit@pkgsplit,ver_regexp,_pkg_str',
)
from portage.cache import volatile
@@ -43,6 +43,8 @@ import os as _os
import sys
import traceback
import warnings
+import errno
+import collections
try:
from urllib.parse import urlparse
@@ -101,6 +103,68 @@ class _dummy_list(list):
except ValueError:
pass
+
+class _better_cache(object):
+
+ """
+ The purpose of better_cache is to locate catpkgs in repositories using ``os.listdir()`` as much as possible, which
+ is less expensive IO-wise than exhaustively doing a stat on each repo for a particular catpkg. better_cache stores a
+ list of repos in which particular catpkgs appear. Various dbapi methods use better_cache to locate repositories of
+ interest related to particular catpkg rather than performing an exhaustive scan of all repos/overlays.
+
+ Better_cache.items data may look like this::
+
+ { "sys-apps/portage" : [ repo1, repo2 ] }
+
+ Without better_cache, Portage will get slower and slower (due to excessive IO) as more overlays are added.
+
+ Also note that it is OK if this cache has some 'false positive' catpkgs in it. We use it to search for specific
+ catpkgs listed in ebuilds. The likelihood of a false positive catpkg in our cache causing a problem is extremely
+ low, because the user of our cache is passing us a catpkg that came from somewhere and has already undergone some
+ validation, and even then will further interrogate the short-list of repos we return to gather more information
+ on the catpkg.
+
+ Thus, the code below is optimized for speed rather than painstaking correctness. I have added a note to
+ ``dbapi.getRepositories()`` to ensure that developers are aware of this just in case.
+
+ The better_cache has been redesigned to perform on-demand scans -- it will only scan a category at a time, as
+ needed. This should further optimize IO performance by not scanning category directories that are not needed by
+ Portage.
+ """
+
+ def __init__(self, repositories):
+ self._items = collections.defaultdict(list)
+ self._scanned_cats = set()
+
+ # ordered list of all portree locations we'll scan:
+ self._repo_list = [repo for repo in reversed(list(repositories))
+ if repo.location is not None]
+
+ def __getitem__(self, catpkg):
+ result = self._items.get(catpkg)
+ if result is not None:
+ return result
+
+ cat, pkg = catsplit(catpkg)
+ if cat not in self._scanned_cats:
+ self._scan_cat(cat)
+ return self._items[catpkg]
+
+ def _scan_cat(self, cat):
+ for repo in self._repo_list:
+ cat_dir = repo.location + "/" + cat
+ try:
+ pkg_list = os.listdir(cat_dir)
+ except OSError as e:
+ if e.errno not in (errno.ENOTDIR, errno.ENOENT, errno.ESTALE):
+ raise
+ continue
+ for p in pkg_list:
+ if os.path.isdir(cat_dir + "/" + p):
+ self._items[cat + "/" + p].append(repo)
+ self._scanned_cats.add(cat)
+
+
class portdbapi(dbapi):
"""this tree will scan a portage directory located at root (passed to init)"""
portdbapi_instances = _dummy_list()
@@ -253,6 +317,7 @@ class portdbapi(dbapi):
"RESTRICT", "SLOT", "DEFINED_PHASES", "REQUIRED_USE"])
self._aux_cache = {}
+ self._better_cache = None
self._broken_ebuilds = set()
@property
@@ -342,12 +407,25 @@ class portdbapi(dbapi):
except KeyError:
return None
- def getRepositories(self):
+ def getRepositories(self, catpkg=None):
+
"""
- This function is required for GLEP 42 compliance; it will return a list of
- repository IDs
- TreeMap = {id: path}
+ With catpkg=None, this will return a complete list of repositories in this dbapi. With catpkg set to a value,
+ this method will return a short-list of repositories that contain this catpkg. Use this second approach if
+ possible, to avoid exhaustively searching all repos for a particular catpkg. It's faster for this method to
+ find the catpkg than for you do it yourself. When specifying catpkg, you should have reasonable assurance that
+ the category is valid and PMS-compliant as the caching mechanism we use does not perform validation checks for
+ categories.
+
+ This function is required for GLEP 42 compliance.
+
+ @param catpkg: catpkg for which we want a list of repositories; we'll get a list of all repos containing this
+ catpkg; if None, return a list of all Repositories that contain a particular catpkg.
+ @return: a list of repositories.
"""
+
+ if catpkg is not None and self._better_cache is not None:
+ return [repo.name for repo in self._better_cache[catpkg]]
return self._ordered_repo_name_list
def getMissingRepoNames(self):
@@ -363,7 +441,7 @@ class portdbapi(dbapi):
"""
return self.settings.repositories.ignored_repos
- def findname2(self, mycpv, mytree=None, myrepo = None):
+ def findname2(self, mycpv, mytree=None, myrepo=None):
"""
Returns the location of the CPV, and what overlay it was in.
Searches overlays first, then PORTDIR; this allows us to return the first
@@ -385,16 +463,33 @@ class portdbapi(dbapi):
if psplit is None or len(mysplit) != 2:
raise InvalidPackageName(mycpv)
+ try:
+ cp = mycpv.cp
+ except AttributeError:
+ cp = mysplit[0] + "/" + psplit[0]
+
+ if self._better_cache is None:
+ if mytree:
+ mytrees = [mytree]
+ else:
+ mytrees = reversed(self.porttrees)
+ else:
+ try:
+ repos = self._better_cache[cp]
+ except KeyError:
+ return (None, 0)
+
+ mytrees = []
+ for repo in repos:
+ if mytree is not None and mytree != repo.location:
+ continue
+ mytrees.append(repo.location)
+
# For optimal performace in this hot spot, we do manual unicode
# handling here instead of using the wrapped os module.
encoding = _encodings['fs']
errors = 'strict'
- if mytree:
- mytrees = [mytree]
- else:
- mytrees = reversed(self.porttrees)
-
relative_path = mysplit[0] + _os.sep + psplit[0] + _os.sep + \
mysplit[1] + ".ebuild"
@@ -764,8 +859,10 @@ class portdbapi(dbapi):
else:
# assume it's iterable
mytrees = mytree
- else:
+ elif self._better_cache is None:
mytrees = self.porttrees
+ else:
+ mytrees = [repo.location for repo in self._better_cache[mycp]]
for oroot in mytrees:
try:
file_list = os.listdir(os.path.join(oroot, mycp))
@@ -814,10 +911,12 @@ class portdbapi(dbapi):
"minimum-all-ignore-profile", "minimum-visible"):
self.xcache[x]={}
self.frozen=1
+ self._better_cache = _better_cache(self.repositories)
def melt(self):
self.xcache = {}
self._aux_cache = {}
+ self._better_cache = None
self.frozen = 0
def xmatch(self,level,origdep,mydep=None,mykey=None,mylist=None):
diff --git a/pym/portage/elog/mod_echo.py b/pym/portage/elog/mod_echo.py
index bb34a1e44..fb86547a4 100644
--- a/pym/portage/elog/mod_echo.py
+++ b/pym/portage/elog/mod_echo.py
@@ -19,7 +19,9 @@ def process(mysettings, key, logentries, fulltext):
logfile = None
# output logfile explicitly only if it isn't in tempdir, otherwise
# it will be removed anyway
- if "PORT_LOGDIR" in mysettings:
+ if (key == mysettings.mycpv and
+ "PORT_LOGDIR" in mysettings and
+ "PORTAGE_LOG_FILE" in mysettings):
logfile = mysettings["PORTAGE_LOG_FILE"]
_items.append((mysettings["ROOT"], key, logentries, logfile))
diff --git a/pym/portage/package/ebuild/doebuild.py b/pym/portage/package/ebuild/doebuild.py
index a5adf2c92..4505dafeb 100644
--- a/pym/portage/package/ebuild/doebuild.py
+++ b/pym/portage/package/ebuild/doebuild.py
@@ -1814,6 +1814,7 @@ _post_phase_cmds = {
"preinst_sfperms",
"preinst_selinux_labels",
"preinst_suid_scan",
+ "preinst_qa_check",
],
"postinst" : [
diff --git a/pym/portage/sync/modules/rsync/rsync.py b/pym/portage/sync/modules/rsync/rsync.py
index 45a70e7dd..01e4e5924 100644
--- a/pym/portage/sync/modules/rsync/rsync.py
+++ b/pym/portage/sync/modules/rsync/rsync.py
@@ -202,6 +202,7 @@ class RsyncSync(NewBase):
# reverse, for use with pop()
uris.reverse()
+ uris_orig = uris[:]
effective_maxretries = maxretries
if effective_maxretries < 0:
@@ -210,10 +211,13 @@ class RsyncSync(NewBase):
while (1):
if uris:
dosyncuri = uris.pop()
- else:
+ elif maxretries < 0 or retries > maxretries:
writemsg("!!! Exhausted addresses for %s\n"
% _unicode_decode(hostname), noiselevel=-1)
return (1, False)
+ else:
+ uris.extend(uris_orig)
+ dosyncuri = uris.pop()
if (retries==0):
if "--ask" in opts:
diff --git a/repoman/RELEASE-NOTES b/repoman/RELEASE-NOTES
index 07fd62353..ad347cf11 100644
--- a/repoman/RELEASE-NOTES
+++ b/repoman/RELEASE-NOTES
@@ -1,6 +1,11 @@
Release Notes; upgrade information mainly.
Features/major bugfixes are listed in NEWS
+repoman-2.3.4
+==================================
+* Support two new options: --bug (-b) and --closes (-c)
+
+
repoman-2.3.3
==================================
* Bug Fixes:
diff --git a/repoman/setup.py b/repoman/setup.py
index 4a1283ec5..9c4def929 100755
--- a/repoman/setup.py
+++ b/repoman/setup.py
@@ -447,7 +447,7 @@ def get_manpages():
setup(
name = 'repoman',
- version = '2.3.3',
+ version = '2.3.4',
url = 'https://wiki.gentoo.org/wiki/Project:Portage',
author = 'Gentoo Portage Development Team',
author_email = 'dev-portage@gentoo.org',
diff --git a/setup.py b/setup.py
index 256c5e354..a2d10bd73 100755
--- a/setup.py
+++ b/setup.py
@@ -659,7 +659,7 @@ class build_ext(_build_ext):
setup(
name = 'portage',
- version = '2.3.11',
+ version = '2.3.13',
url = 'https://wiki.gentoo.org/wiki/Project:Portage',
author = 'Gentoo Portage Development Team',
author_email = 'dev-portage@gentoo.org',
diff --git a/src/portage_util_file_copy_reflink_linux.c b/src/portage_util_file_copy_reflink_linux.c
index 4be9e0568..c3ce26b2b 100644
--- a/src/portage_util_file_copy_reflink_linux.c
+++ b/src/portage_util_file_copy_reflink_linux.c
@@ -56,12 +56,18 @@ initreflink_linux(void)
/**
* cfr_wrapper - A copy_file_range syscall wrapper function, having a
- * function signature that is compatible with sendfile.
+ * function signature that is compatible with sf_wrapper.
* @fd_out: output file descriptor
* @fd_in: input file descriptor
- * @off_out: offset of the output file
+ * @off_out: must point to a buffer that specifies the starting offset
+ * where bytes will be copied to fd_out, and this buffer is adjusted by
+ * the number of bytes copied.
* @len: number of bytes to copy between the file descriptors
*
+ * Bytes are copied from fd_in starting from *off_out, and the file
+ * offset of fd_in is not changed. Effects on the file offset of
+ * fd_out are undefined.
+ *
* Return: Number of bytes written to out_fd on success, -1 on failure
* (errno is set appropriately).
*/
@@ -69,7 +75,8 @@ static ssize_t
cfr_wrapper(int fd_out, int fd_in, off_t *off_out, size_t len)
{
#ifdef __NR_copy_file_range
- return syscall(__NR_copy_file_range, fd_in, NULL, fd_out,
+ off_t off_in = *off_out;
+ return syscall(__NR_copy_file_range, fd_in, &off_in, fd_out,
off_out, len, 0);
#else
/* This is how it fails at runtime when the syscall is not supported. */
@@ -79,18 +86,50 @@ cfr_wrapper(int fd_out, int fd_in, off_t *off_out, size_t len)
}
/**
+ * sf_wrapper - A sendfile wrapper function, having a function signature
+ * that is compatible with cfr_wrapper.
+ * @fd_out: output file descriptor
+ * @fd_in: input file descriptor
+ * @off_out: must point to a buffer that specifies the starting offset
+ * where bytes will be copied to fd_out, and this buffer is adjusted by
+ * the number of bytes copied.
+ * @len: number of bytes to copy between the file descriptors
+ *
+ * Bytes are copied from fd_in starting from *off_out, and the file
+ * offset of fd_in is not changed. Effects on the file offset of
+ * fd_out are undefined.
+ *
+ * Return: Number of bytes written to out_fd on success, -1 on failure
+ * (errno is set appropriately).
+ */
+static ssize_t
+sf_wrapper(int fd_out, int fd_in, off_t *off_out, size_t len)
+{
+ ssize_t ret;
+ off_t off_in = *off_out;
+ /* The sendfile docs do not specify behavior of the output file
+ * offset, therefore it must be adjusted with lseek.
+ */
+ if (lseek(fd_out, *off_out, SEEK_SET) < 0)
+ return -1;
+ ret = sendfile(fd_out, fd_in, &off_in, len);
+ if (ret > 0)
+ *off_out += ret;
+ return ret;
+}
+
+
+/**
* do_lseek_data - Adjust file offsets to the next location containing
* data, creating sparse empty blocks in the output file as needed.
* @fd_in: input file descriptor
* @fd_out: output file descriptor
* @off_out: offset of the output file
*
- * Use lseek SEEK_DATA to adjust the fd_in file offset to the next
- * location containing data, and adjust the fd_in file offset and
- * off_out to the same location (creating sparse empty blocks as
- * needed). On success, both fd_in and fd_out file offsets are
- * guaranteed to be exactly equal to the value that off_out points to.
- *
+ * Use lseek SEEK_DATA to adjust off_out to the next location from fd_in
+ * containing data (creates sparse empty blocks when appropriate). Effects
+ * on file offsets are undefined.
+ *
* Return: On success, the number of bytes to copy before the next hole,
* and -1 on failure (errno is set appropriately). Returns 0 when fd_in
* reaches EOF.
@@ -145,13 +184,6 @@ do_lseek_data(int fd_out, int fd_in, off_t *off_out) {
return -1;
}
- /* Revert SEEK_HOLE offset change, since we're going
- * to copy the data that comes before the hole.
- */
- if (lseek(fd_in, offset_data, SEEK_SET) < 0) {
- return -1;
- }
-
return offset_hole - offset_data;
#else
/* This is how it fails at runtime when lseek SEEK_DATA is not supported. */
@@ -232,10 +264,6 @@ _reflink_linux_file_copy(PyObject *self, PyObject *args)
break;
}
- /* For the copyfunc call, the fd_in file offset must be
- * exactly equal to offset_out. The above do_lseek_data
- * function guarantees correct state.
- */
copyfunc_ret = copyfunc(fd_out,
fd_in,
&offset_out,
@@ -250,7 +278,7 @@ _reflink_linux_file_copy(PyObject *self, PyObject *args)
* syscall is not available (less than Linux 4.5).
*/
error = 0;
- copyfunc = sendfile;
+ copyfunc = sf_wrapper;
copyfunc_ret = copyfunc(fd_out,
fd_in,
&offset_out,
@@ -284,27 +312,18 @@ _reflink_linux_file_copy(PyObject *self, PyObject *args)
} else {
stat_in_acquired = 1;
- /* For the sendfile call, the fd_in file offset must be
- * exactly equal to offset_out. Use lseek to ensure
- * correct state, in case an EINTR retry caused it to
- * get out of sync somewhow.
- */
- if (lseek(fd_in, offset_out, SEEK_SET) < 0) {
- error = errno;
- } else {
- while (offset_out < stat_in.st_size) {
- copyfunc_ret = sendfile(fd_out,
- fd_in,
- &offset_out,
- stat_in.st_size - offset_out);
+ while (offset_out < stat_in.st_size) {
+ copyfunc_ret = sf_wrapper(fd_out,
+ fd_in,
+ &offset_out,
+ stat_in.st_size - offset_out);
- if (copyfunc_ret < 0) {
- error = errno;
- if (errno == EINVAL && !offset_out) {
- sendfile_works = 0;
- }
- break;
+ if (copyfunc_ret < 0) {
+ error = errno;
+ if (errno == EINVAL && !offset_out) {
+ sendfile_works = 0;
}
+ break;
}
}
}