Gentoo Archives: gentoo-commits

From: Zac Medico <zmedico@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/portage:master commit in: pym/portage/
Date: Tue, 15 Dec 2015 16:18:47
Message-Id: 1450195853.3c2cce57700e8a2be4774d653cd632d9e59aab78.zmedico@gentoo
1 commit: 3c2cce57700e8a2be4774d653cd632d9e59aab78
2 Author: Zac Medico <zmedico <AT> gentoo <DOT> org>
3 AuthorDate: Tue Dec 15 07:29:36 2015 +0000
4 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
5 CommitDate: Tue Dec 15 16:10:53 2015 +0000
6 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=3c2cce57
7
8 Manifest._apply_max_mtime: account for removals and renames (bug 567920)
9
10 Include directory mtimes in the max mtime calculation, in order
11 to account for removals and renames.
12
13 Fixes: 6dacd0ed9f6d ("Manifest.write: stable/predictable Manifest mtime for rsync (bug 557962)")
14 X-Gentoo-Bug: 567920
15 X-Gentoo-Bug-url: https://bugs.gentoo.org/show_bug.cgi?id=567920
16 Acked-by: Alexander Berntsen <bernalex <AT> gentoo.org>
17
18 pym/portage/manifest.py | 40 +++++++++++++++++++++++++++++-----------
19 1 file changed, 29 insertions(+), 11 deletions(-)
20
21 diff --git a/pym/portage/manifest.py b/pym/portage/manifest.py
22 index f5cf0f5..818515f 100644
23 --- a/pym/portage/manifest.py
24 +++ b/pym/portage/manifest.py
25 @@ -282,7 +282,8 @@ class Manifest(object):
26 try:
27 myentries = list(self._createManifestEntries())
28 update_manifest = True
29 - existing_st = None
30 + preserved_stats = {}
31 + preserved_stats[self.pkgdir.rstrip(os.sep)] = os.stat(self.pkgdir)
32 if myentries and not force:
33 try:
34 f = io.open(_unicode_encode(self.getFullname(),
35 @@ -290,7 +291,7 @@ class Manifest(object):
36 mode='r', encoding=_encodings['repo.content'],
37 errors='replace')
38 oldentries = list(self._parseManifestLines(f))
39 - existing_st = os.fstat(f.fileno())
40 + preserved_stats[self.getFullname()] = os.fstat(f.fileno())
41 f.close()
42 if len(oldentries) == len(myentries):
43 update_manifest = False
44 @@ -312,7 +313,7 @@ class Manifest(object):
45 # non-empty for all currently known use cases.
46 write_atomic(self.getFullname(), "".join("%s\n" %
47 _unicode(myentry) for myentry in myentries))
48 - self._apply_max_mtime(existing_st, myentries)
49 + self._apply_max_mtime(preserved_stats, myentries)
50 rval = True
51 else:
52 # With thin manifest, there's no need to have
53 @@ -332,17 +333,21 @@ class Manifest(object):
54 raise
55 return rval
56
57 - def _apply_max_mtime(self, existing_st, entries):
58 + def _apply_max_mtime(self, preserved_stats, entries):
59 """
60 Set the Manifest mtime to the max mtime of all relevant files
61 - (the existing Manifest mtime is included in order to account for
62 - eclass modifications that change DIST entries). This results in a
63 + and directories. Directory mtimes account for file renames and
64 + removals. The existing Manifest mtime accounts for eclass
65 + modifications that change DIST entries. This results in a
66 stable/predictable mtime, which is useful when converting thin
67 manifests to thick manifests for distribution via rsync. For
68 portability, the mtime is set with 1 second resolution.
69
70 @param existing_st: stat result for existing Manifest
71 @type existing_st: posix.stat_result
72 + @param preserved_stats: maps paths to preserved stat results
73 + that should be used instead of os.stat() calls
74 + @type preserved_stats: dict
75 @param entries: list of current Manifest2Entry instances
76 @type entries: list
77 """
78 @@ -350,18 +355,31 @@ class Manifest(object):
79 # it always rounds down. Note that stat_result.st_mtime will round
80 # up from 0.999999999 to 1.0 when precision is lost during conversion
81 # from nanosecond resolution to float.
82 - max_mtime = None if existing_st is None else existing_st[stat.ST_MTIME]
83 + max_mtime = None
84 + _update_max = (lambda st: max_mtime if max_mtime is not None
85 + and max_mtime > st[stat.ST_MTIME] else st[stat.ST_MTIME])
86 + _stat = (lambda path: preserved_stats[path] if path in preserved_stats
87 + else os.stat(path))
88 +
89 + for stat_result in preserved_stats.values():
90 + max_mtime = _update_max(stat_result)
91 +
92 + dirs = set()
93 for entry in entries:
94 if entry.type == 'DIST':
95 continue
96 abs_path = (os.path.join(self.pkgdir, 'files', entry.name) if
97 entry.type == 'AUX' else os.path.join(self.pkgdir, entry.name))
98 - mtime = os.stat(abs_path)[stat.ST_MTIME]
99 - if max_mtime is None or mtime > max_mtime:
100 - max_mtime = mtime
101 + max_mtime = _update_max(_stat(abs_path))
102 +
103 + parent_dir = os.path.dirname(abs_path)
104 + if parent_dir not in dirs:
105 + dirs.add(parent_dir)
106 + max_mtime = _update_max(_stat(parent_dir))
107
108 if max_mtime is not None:
109 - os.utime(self.getFullname(), (max_mtime, max_mtime))
110 + for path in preserved_stats:
111 + os.utime(path, (max_mtime, max_mtime))
112
113 def sign(self):
114 """ Sign the Manifest """