1 |
On Sun, 10 May 2015 03:00:56 -0700 |
2 |
Zac Medico <zmedico@g.o> wrote: |
3 |
|
4 |
> Whenever a file/directory collision would have previously caused a |
5 |
> problem, the colliding file or directory will now be renamed. |
6 |
> |
7 |
> X-Gentoo-Bug: 256376 |
8 |
> X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=256376 |
9 |
> --- |
10 |
> pym/portage/dispatch_conf.py | 97 |
11 |
> +++++++++++++++++++++++++++++++++++++------- 1 file changed, 83 |
12 |
> insertions(+), 14 deletions(-) |
13 |
> |
14 |
> diff --git a/pym/portage/dispatch_conf.py |
15 |
> b/pym/portage/dispatch_conf.py index 98939fd..ed9a64a 100644 |
16 |
> --- a/pym/portage/dispatch_conf.py |
17 |
> +++ b/pym/portage/dispatch_conf.py |
18 |
> @@ -8,6 +8,7 @@ |
19 |
> |
20 |
> from __future__ import print_function, unicode_literals |
21 |
> |
22 |
> +import errno |
23 |
> import io |
24 |
> import functools |
25 |
> import stat |
26 |
> @@ -20,6 +21,7 @@ from portage import _encodings, os, shutil |
27 |
> from portage.env.loaders import KeyValuePairFileLoader |
28 |
> from portage.localization import _ |
29 |
> from portage.util import shlex_split, varexpand |
30 |
> +from portage.util.path import iter_parents |
31 |
> |
32 |
> RCS_BRANCH = '1.1.1' |
33 |
> RCS_LOCK = 'rcs -ko -M -l' |
34 |
> @@ -28,6 +30,7 @@ RCS_GET = 'co' |
35 |
> RCS_MERGE = "rcsmerge -p -r" + RCS_BRANCH + " '%s' > '%s'" |
36 |
> |
37 |
> DIFF3_MERGE = "diff3 -mE '%s' '%s' '%s' > '%s'" |
38 |
> +_ARCHIVE_ROTATE_MAX = 9 |
39 |
> |
40 |
> def diffstatusoutput(cmd, file1, file2): |
41 |
> """ |
42 |
> @@ -244,6 +247,77 @@ def rcs_archive(archive, curconf, newconf, |
43 |
> mrgconf): |
44 |
> return ret |
45 |
> |
46 |
> +def _file_archive_rotate(archive): |
47 |
> + """ |
48 |
> + Rename archive to archive + '.1', and perform similar |
49 |
> rotation |
50 |
> + for files up to archive + '.9'. |
51 |
> + |
52 |
> + @param archive: file path to archive |
53 |
> + @type archive: str |
54 |
> + """ |
55 |
> + |
56 |
> + max_suf = 0 |
57 |
> + try: |
58 |
> + for max_suf, max_st, max_path in ( |
59 |
> + (suf, os.lstat(path), path) for suf, path in |
60 |
> ( |
61 |
> + (suf, "%s.%s" % (archive, suf)) for suf in |
62 |
> range( |
63 |
> + 1, _ARCHIVE_ROTATE_MAX + 1))): |
64 |
> + pass |
65 |
> + except OSError as e: |
66 |
> + if e.errno not in (errno.ENOENT, errno.ESTALE): |
67 |
> + raise |
68 |
> + # There's already an unused suffix. |
69 |
> + else: |
70 |
> + # Free the max suffix in order to avoid possible |
71 |
> problems |
72 |
> + # when we rename another file or directory to the |
73 |
> same |
74 |
> + # location (see bug 256376). |
75 |
> + if stat.S_ISDIR(max_st.st_mode): |
76 |
> + # Removing a directory might destroy |
77 |
> something important, |
78 |
> + # so rename it instead. |
79 |
> + head, tail = os.path.split(archive) |
80 |
> + placeholder = tempfile.NamedTemporaryFile( |
81 |
> + prefix="%s." % tail, |
82 |
> + dir=head) |
83 |
> + placeholder.close() |
84 |
> + os.rename(max_path, placeholder.name) |
85 |
> + else: |
86 |
> + os.unlink(max_path) |
87 |
> + |
88 |
> + # The max suffix is now unused. |
89 |
> + max_suf -= 1 |
90 |
> + |
91 |
> + for suf in range(max_suf + 1, 1, -1): |
92 |
> + os.rename("%s.%s" % (archive, suf - 1), "%s.%s" % |
93 |
> (archive, suf)) + |
94 |
> + os.rename(archive, "%s.1" % (archive,)) |
95 |
> + |
96 |
> +def _file_archive_ensure_dir(parent_dir): |
97 |
> + """ |
98 |
> + Ensure that the parent directory for an archive exists. |
99 |
> + If a file exists where a directory is needed, then rename |
100 |
> + it (see bug 256376). |
101 |
> + |
102 |
> + @param parent_dir: path of parent directory |
103 |
> + @type parent_dir: str |
104 |
> + """ |
105 |
> + |
106 |
> + for parent in iter_parents(parent_dir): |
107 |
> + # Use lstat because a symlink to a directory might |
108 |
> point |
109 |
> + # to a directory outside of the config archive, |
110 |
> making |
111 |
> + # it an unsuitable parent. |
112 |
> + try: |
113 |
> + parent_st = os.lstat(parent) |
114 |
> + except OSError: |
115 |
> + pass |
116 |
> + else: |
117 |
> + if not stat.S_ISDIR(parent_st.st_mode): |
118 |
> + _file_archive_rotate(parent) |
119 |
> + break |
120 |
> + |
121 |
> + try: |
122 |
> + os.makedirs(parent_dir) |
123 |
> + except OSError: |
124 |
> + pass |
125 |
> |
126 |
> def file_archive(archive, curconf, newconf, mrgconf): |
127 |
> """Archive existing config to the archive-dir, bumping old |
128 |
> versions @@ -253,24 +327,13 @@ def file_archive(archive, curconf, |
129 |
> newconf, mrgconf): if newconf was specified, archive it as |
130 |
> a .dist.new version (which gets moved to the .dist version at the end |
131 |
> of the processing).""" |
132 |
> - try: |
133 |
> - os.makedirs(os.path.dirname(archive)) |
134 |
> - except OSError: |
135 |
> - pass |
136 |
> + _file_archive_ensure_dir(os.path.dirname(archive)) |
137 |
> |
138 |
> # Archive the current config file if it isn't already saved |
139 |
> if (os.path.lexists(archive) and |
140 |
> len(diffstatusoutput_mixed( |
141 |
> "diff -aq '%s' '%s'", curconf, archive)[1]) != 0): |
142 |
> - suf = 1 |
143 |
> - while suf < 9 and os.path.lexists(archive + '.' + |
144 |
> str(suf)): |
145 |
> - suf += 1 |
146 |
> - |
147 |
> - while suf > 1: |
148 |
> - os.rename(archive + '.' + str(suf-1), |
149 |
> archive + '.' + str(suf)) |
150 |
> - suf -= 1 |
151 |
> - |
152 |
> - os.rename(archive, archive + '.1') |
153 |
> + _file_archive_rotate(archive) |
154 |
> |
155 |
> try: |
156 |
> curconf_st = os.lstat(curconf) |
157 |
> @@ -294,6 +357,9 @@ def file_archive(archive, curconf, newconf, |
158 |
> mrgconf): stat.S_ISLNK(mystat.st_mode)): |
159 |
> # Save off new config file in the archive dir |
160 |
> with .dist.new suffix newconf_archive = archive + '.dist.new' |
161 |
> + if os.path.isdir(newconf_archive |
162 |
> + ) and not os.path.islink(newconf_archive): |
163 |
> + _file_archive_rotate(newconf_archive) |
164 |
> _archive_copy(mystat, newconf, newconf_archive) |
165 |
> |
166 |
> ret = 0 |
167 |
> @@ -325,4 +391,7 @@ def rcs_archive_post_process(archive): |
168 |
> def file_archive_post_process(archive): |
169 |
> """Rename the archive file with the .dist.new suffix to |
170 |
> a .dist suffix""" if os.path.lexists(archive + '.dist.new'): |
171 |
> - os.rename(archive + '.dist.new', archive + '.dist') |
172 |
> + dest = "%s.dist" % archive |
173 |
> + if os.path.isdir(dest) and not os.path.islink(dest): |
174 |
> + _file_archive_rotate(dest) |
175 |
> + os.rename(archive + '.dist.new', dest) |
176 |
|
177 |
looks good |
178 |
|
179 |
-- |
180 |
Brian Dolbec <dolsen> |