Gentoo Archives: gentoo-portage-dev

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

Replies