[gentoo-commits] proj/portage:master commit in: lib/portage/package/ebuild/, lib/portage/tests/ebuild/

2019-10-21 Thread Zac Medico
commit: d9855418352398013ae787bb73f70e935ec109ca
Author: Zac Medico  gentoo  org>
AuthorDate: Mon Oct 21 09:51:33 2019 +
Commit: Zac Medico  gentoo  org>
CommitDate: Mon Oct 21 09:55:36 2019 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=d9855418

fetch: yield unicode from layout get_filenames methods

Reported-by: Michał Górny  gentoo.org>
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/package/ebuild/fetch.py| 18 ++
 lib/portage/tests/ebuild/test_fetch.py | 26 ++
 2 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/lib/portage/package/ebuild/fetch.py 
b/lib/portage/package/ebuild/fetch.py
index cedf12b19..ac25e4326 100644
--- a/lib/portage/package/ebuild/fetch.py
+++ b/lib/portage/package/ebuild/fetch.py
@@ -7,6 +7,7 @@ __all__ = ['fetch']
 
 import errno
 import functools
+import glob
 import io
 import itertools
 import json
@@ -27,7 +28,6 @@ except ImportError:
 
 import portage
 portage.proxy.lazyimport.lazyimport(globals(),
-   'glob:glob',
'portage.package.ebuild.config:check_config_instance,config',
'portage.package.ebuild.doebuild:doebuild_environment,' + \
'_doebuild_spawn',
@@ -272,7 +272,13 @@ class FlatLayout(object):
def get_filenames(self, distdir):
for dirpath, dirnames, filenames in os.walk(distdir,
onerror=_raise_exc):
-   return iter(filenames)
+   for filename in filenames:
+   try:
+   yield portage._unicode_decode(filename, 
errors='strict')
+   except UnicodeDecodeError:
+   # Ignore it. Distfiles names must have 
valid UTF8 encoding.
+   pass
+   return
 
@staticmethod
def verify_args(args):
@@ -301,8 +307,12 @@ class FilenameHashLayout(object):
c = c // 4
pattern += c * '[0-9a-f]' + '/'
pattern += '*'
-   return (x.rsplit('/', 1)[1]
-   for x in glob(os.path.join(distdir, pattern)))
+   for x in glob.iglob(os.path.join(distdir, pattern)):
+   try:
+   yield portage._unicode_decode(x, 
errors='strict').rsplit('/', 1)[1]
+   except UnicodeDecodeError:
+   # Ignore it. Distfiles names must have valid 
UTF8 encoding.
+   pass
 
@staticmethod
def verify_args(args):

diff --git a/lib/portage/tests/ebuild/test_fetch.py 
b/lib/portage/tests/ebuild/test_fetch.py
index 538fb1754..9a8a4a544 100644
--- a/lib/portage/tests/ebuild/test_fetch.py
+++ b/lib/portage/tests/ebuild/test_fetch.py
@@ -448,3 +448,29 @@ class EbuildFetchTestCase(TestCase):
 '''
self.assertRaises(ConfigParserError, mlc.read_from_file,
io.StringIO(conf))
+
+   def test_filename_hash_layout_get_filenames(self):
+   layouts = (
+   FlatLayout(),
+   FilenameHashLayout('SHA1', '4'),
+   FilenameHashLayout('SHA1', '8'),
+   FilenameHashLayout('SHA1', '8:16'),
+   FilenameHashLayout('SHA1', '8:16:24'),
+   )
+   filename = 'foo-1.tar.gz'
+
+   for layout in layouts:
+   distdir = tempfile.mkdtemp()
+   try:
+   path = os.path.join(distdir, 
layout.get_path(filename))
+   try:
+   os.makedirs(os.path.dirname(path))
+   except OSError:
+   pass
+
+   with open(path, 'wb') as f:
+   pass
+
+   self.assertEqual([filename], 
list(layout.get_filenames(distdir)))
+   finally:
+   shutil.rmtree(distdir)



[gentoo-commits] proj/portage:master commit in: lib/portage/package/ebuild/, lib/portage/tests/ebuild/

2019-10-14 Thread Zac Medico
commit: 6b5889afb1e80bc673ce782e65fc0f49ee7d0908
Author: Zac Medico  gentoo  org>
AuthorDate: Sun Oct 13 22:13:18 2019 +
Commit: Zac Medico  gentoo  org>
CommitDate: Mon Oct 14 19:46:16 2019 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=6b5889af

fetch: minimal skiprocheck fixes (bug 220533)

Fix cases here fetch assumes that DISTDIR is writable when it's actually
read-only. This preserves old behavior which allowed users to override
FETCHCOMMAND to fetch files on a remote system, even though DISTDIR is
locally mounted in read-only mode.

Bug: https://bugs.gentoo.org/220533
Fixes: ebbde237d33e ("fetch: atomic downloads (bug 175612)")
Signed-off-by: Zac Medico  gentoo.org>

 lib/portage/package/ebuild/fetch.py| 38 --
 lib/portage/tests/ebuild/test_fetch.py | 22 
 2 files changed, 45 insertions(+), 15 deletions(-)

diff --git a/lib/portage/package/ebuild/fetch.py 
b/lib/portage/package/ebuild/fetch.py
index cd204b755..1d5e07260 100644
--- a/lib/portage/package/ebuild/fetch.py
+++ b/lib/portage/package/ebuild/fetch.py
@@ -529,11 +529,12 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0,
if listonly or ("distlocks" not in features):
use_locks = 0
 
+   distdir_writable = os.access(mysettings["DISTDIR"], os.W_OK)
fetch_to_ro = 0
if "skiprocheck" in features:
fetch_to_ro = 1
 
-   if not os.access(mysettings["DISTDIR"],os.W_OK) and fetch_to_ro:
+   if not distdir_writable and fetch_to_ro:
if use_locks:
writemsg(colorize("BAD",
_("!!! For fetching to a read-only filesystem, "
@@ -613,8 +614,11 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0,
for myfile, myuri in file_uri_tuples:
if myfile not in filedict:
filedict[myfile]=[]
-   mirror_cache = os.path.join(mysettings["DISTDIR"],
-   ".mirror-cache.json")
+   if distdir_writable:
+   mirror_cache = 
os.path.join(mysettings["DISTDIR"],
+   ".mirror-cache.json")
+   else:
+   mirror_cache = None
for l in locations:
filedict[myfile].append(functools.partial(
get_mirror_url, l, myfile, 
mirror_cache))
@@ -790,7 +794,7 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0,
pruned_digests["size"] = size
 
myfile_path = os.path.join(mysettings["DISTDIR"], myfile)
-   download_path = myfile_path + _download_suffix
+   download_path = myfile_path if fetch_to_ro else myfile_path + 
_download_suffix
has_space = True
has_space_superuser = True
file_lock = None
@@ -1058,7 +1062,8 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0,

"File renamed to '%s'\n\n") % \

temp_filename, noiselevel=-1)
else:
-   
_movefile(download_path, myfile_path, mysettings=mysettings)
+   if not 
fetch_to_ro:
+   
_movefile(download_path, myfile_path, mysettings=mysettings)
eout = EOutput()
eout.quiet = \

mysettings.get("PORTAGE_QUIET", None) == "1"
@@ -1177,7 +1182,7 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0,
del e
fetched = 0
else:
-   if mystat.st_size < 
fetch_resume_size:
+   if distdir_writable and 
mystat.st_size < fetch_resume_size:
writemsg(_(">>> 
Deleting distfile with size "
"%d 
(smaller than " "PORTAGE_FETCH_RESU"

"ME_MIN_SIZE)\n") % mystat.st_size)
@@ -1315,13 +1320,14 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0,
 

[gentoo-commits] proj/portage:master commit in: lib/portage/package/ebuild/, lib/portage/tests/ebuild/

2019-10-04 Thread Michał Górny
commit: 6a539b7c5163899db1d58cf152aeab1b2b4f9be4
Author: Michał Górny  gentoo  org>
AuthorDate: Thu Oct  3 14:19:54 2019 +
Commit: Michał Górny  gentoo  org>
CommitDate: Fri Oct  4 21:25:00 2019 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=6a539b7c

fetch: Support GLEP 75 mirror structure

Add a support for the subset of GLEP 75 needed by Gentoo Infra.  This
includes fetching and parsing layout.conf, and support for flat layout
and filename-hash layout with cutoffs being multiplies of 4.

Bug: https://bugs.gentoo.org/646898
Closes: https://github.com/gentoo/portage/pull/462
Reviewed-by: Zac Medico  gentoo.org>
Signed-off-by: Michał Górny  gentoo.org>

 lib/portage/package/ebuild/fetch.py| 160 -
 lib/portage/tests/ebuild/test_fetch.py |  94 ++-
 2 files changed, 250 insertions(+), 4 deletions(-)

diff --git a/lib/portage/package/ebuild/fetch.py 
b/lib/portage/package/ebuild/fetch.py
index 227bf45ae..4458796fc 100644
--- a/lib/portage/package/ebuild/fetch.py
+++ b/lib/portage/package/ebuild/fetch.py
@@ -6,13 +6,17 @@ from __future__ import print_function
 __all__ = ['fetch']
 
 import errno
+import functools
 import io
+import itertools
+import json
 import logging
 import random
 import re
 import stat
 import sys
 import tempfile
+import time
 
 from collections import OrderedDict
 
@@ -27,12 +31,17 @@ portage.proxy.lazyimport.lazyimport(globals(),
'portage.package.ebuild.doebuild:doebuild_environment,' + \
'_doebuild_spawn',
'portage.package.ebuild.prepare_build_dirs:prepare_build_dirs',
+   'portage.util:atomic_ofstream',
+   'portage.util.configparser:SafeConfigParser,read_configs,' +
+   'NoOptionError,ConfigParserError',
+   'portage.util._urlopen:urlopen',
 )
 
 from portage import os, selinux, shutil, _encodings, \
_movefile, _shell_quote, _unicode_encode
 from portage.checksum import (get_valid_checksum_keys, perform_md5, verify_all,
-   _filter_unaccelarated_hashes, _hash_filter, _apply_hash_filter)
+   _filter_unaccelarated_hashes, _hash_filter, _apply_hash_filter,
+   checksum_str)
 from portage.const import BASH_BINARY, CUSTOM_MIRRORS_FILE, \
GLOBAL_CONFIG_PATH
 from portage.data import portage_gid, portage_uid, secpass, userpriv_groups
@@ -253,6 +262,146 @@ _size_suffix_map = {
'Y' : 80,
 }
 
+
+class FlatLayout(object):
+   def get_path(self, filename):
+   return filename
+
+   @staticmethod
+   def verify_args(args):
+   return len(args) == 1
+
+
+class FilenameHashLayout(object):
+   def __init__(self, algo, cutoffs):
+   self.algo = algo
+   self.cutoffs = [int(x) for x in cutoffs.split(':')]
+
+   def get_path(self, filename):
+   fnhash = checksum_str(filename.encode('utf8'), self.algo)
+   ret = ''
+   for c in self.cutoffs:
+   assert c % 4 == 0
+   c = c // 4
+   ret += fnhash[:c] + '/'
+   fnhash = fnhash[c:]
+   return ret + filename
+
+   @staticmethod
+   def verify_args(args):
+   if len(args) != 3:
+   return False
+   if args[1] not in get_valid_checksum_keys():
+   return False
+   # argsidate cutoffs
+   for c in args[2].split(':'):
+   try:
+   c = int(c)
+   except ValueError:
+   break
+   else:
+   if c % 4 != 0:
+   break
+   else:
+   return True
+   return False
+
+
+class MirrorLayoutConfig(object):
+   """
+   Class to read layout.conf from a mirror.
+   """
+
+   def __init__(self):
+   self.structure = ()
+
+   def read_from_file(self, f):
+   cp = SafeConfigParser()
+   read_configs(cp, [f])
+   vals = []
+   for i in itertools.count():
+   try:
+   vals.append(tuple(cp.get('structure', '%d' % 
i).split()))
+   except NoOptionError:
+   break
+   self.structure = tuple(vals)
+
+   def serialize(self):
+   return self.structure
+
+   def deserialize(self, data):
+   self.structure = data
+
+   @staticmethod
+   def validate_structure(val):
+   if val[0] == 'flat':
+   return FlatLayout.verify_args(val)
+   if val[0] == 'filename-hash':
+   return FilenameHashLayout.verify_args(val)
+   return False
+
+   def get_best_supported_layout(self):
+   for val in 

[gentoo-commits] proj/portage:master commit in: lib/portage/package/ebuild/, lib/portage/tests/ebuild/, lib/_emerge/

2019-04-29 Thread Zac Medico
commit: ebbde237d33e783c562cc6c70987969ac7228b96
Author: Zac Medico  gentoo  org>
AuthorDate: Sat Apr 27 21:59:57 2019 +
Commit: Zac Medico  gentoo  org>
CommitDate: Tue Apr 30 02:28:21 2019 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=ebbde237

fetch: atomic downloads (bug 175612)

Direct FETCHCOMMAND/RESUMECOMMAND output to a temporary file with
a constant .__download__ suffix, and atomically rename the file
to remove the suffix only after the download has completed
successfully (includes digest verification when applicable).
Also add unit tests to cover most fetch cases.

Bug: https://bugs.gentoo.org/175612
Signed-off-by: Zac Medico  gentoo.org>

 lib/_emerge/BinpkgVerifier.py  |   4 +-
 lib/portage/package/ebuild/fetch.py| 105 ++-
 lib/portage/tests/ebuild/test_fetch.py | 230 +
 3 files changed, 303 insertions(+), 36 deletions(-)

diff --git a/lib/_emerge/BinpkgVerifier.py b/lib/_emerge/BinpkgVerifier.py
index 7a6d15e80..f98f511a8 100644
--- a/lib/_emerge/BinpkgVerifier.py
+++ b/lib/_emerge/BinpkgVerifier.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2013 Gentoo Foundation
+# Copyright 1999-2019 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 import errno
@@ -108,7 +108,7 @@ class BinpkgVerifier(CompositeTask):
def _digest_exception(self, name, value, expected):
 
head, tail = os.path.split(self._pkg_path)
-   temp_filename = _checksum_failure_temp_file(head, tail)
+   temp_filename = 
_checksum_failure_temp_file(self.pkg.root_config.settings, head, tail)
 
self.scheduler.output((
"\n!!! Digest verification failed:\n"

diff --git a/lib/portage/package/ebuild/fetch.py 
b/lib/portage/package/ebuild/fetch.py
index bfd97601c..227bf45ae 100644
--- a/lib/portage/package/ebuild/fetch.py
+++ b/lib/portage/package/ebuild/fetch.py
@@ -30,7 +30,7 @@ portage.proxy.lazyimport.lazyimport(globals(),
 )
 
 from portage import os, selinux, shutil, _encodings, \
-   _shell_quote, _unicode_encode
+   _movefile, _shell_quote, _unicode_encode
 from portage.checksum import (get_valid_checksum_keys, perform_md5, verify_all,
_filter_unaccelarated_hashes, _hash_filter, _apply_hash_filter)
 from portage.const import BASH_BINARY, CUSTOM_MIRRORS_FILE, \
@@ -46,6 +46,8 @@ from portage.util import apply_recursive_permissions, \
varexpand, writemsg, writemsg_level, writemsg_stdout
 from portage.process import spawn
 
+_download_suffix = '.__download__'
+
 _userpriv_spawn_kwargs = (
("uid",portage_uid),
("gid",portage_gid),
@@ -139,7 +141,7 @@ def _userpriv_test_write_file(settings, file_path):
_userpriv_test_write_file_cache[file_path] = rval
return rval
 
-def _checksum_failure_temp_file(distdir, basename):
+def _checksum_failure_temp_file(settings, distdir, basename):
"""
First try to find a duplicate temp file with the same checksum and 
return
that filename if available. Otherwise, use mkstemp to create a new 
unique
@@ -149,9 +151,13 @@ def _checksum_failure_temp_file(distdir, basename):
"""
 
filename = os.path.join(distdir, basename)
+   if basename.endswith(_download_suffix):
+   normal_basename = basename[:-len(_download_suffix)]
+   else:
+   normal_basename = basename
size = os.stat(filename).st_size
checksum = None
-   tempfile_re = re.compile(re.escape(basename) + 
r'\._checksum_failure_\..*')
+   tempfile_re = re.compile(re.escape(normal_basename) + 
r'\._checksum_failure_\..*')
for temp_filename in os.listdir(distdir):
if not tempfile_re.match(temp_filename):
continue
@@ -173,9 +179,9 @@ def _checksum_failure_temp_file(distdir, basename):
return temp_filename
 
fd, temp_filename = \
-   tempfile.mkstemp("", basename + "._checksum_failure_.", distdir)
+   tempfile.mkstemp("", normal_basename + "._checksum_failure_.", 
distdir)
os.close(fd)
-   os.rename(filename, temp_filename)
+   _movefile(filename, temp_filename, mysettings=settings)
return temp_filename
 
 def _check_digests(filename, digests, show_errors=1):
@@ -602,6 +608,7 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0,
pruned_digests["size"] = size
 
myfile_path = os.path.join(mysettings["DISTDIR"], myfile)
+   download_path = myfile_path + _download_suffix
has_space = True
has_space_superuser = True
file_lock = None
@@ -679,12 +686,15 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0,
del e
continue
 
-   if