##// END OF EJS Templates
py3: fix formatting of path-auditing errors
Yuya Nishihara -
r36667:d3b893ec default
parent child Browse files
Show More
@@ -1,350 +1,352
1 1 test-abort-checkin.t
2 2 test-add.t
3 3 test-addremove-similar.t
4 4 test-addremove.t
5 5 test-amend-subrepo.t
6 6 test-ancestor.py
7 7 test-annotate.py
8 8 test-annotate.t
9 test-audit-path.t
10 test-audit-subrepo.t
9 11 test-automv.t
10 12 test-backout.t
11 13 test-backwards-remove.t
12 14 test-basic.t
13 15 test-bheads.t
14 16 test-bisect2.t
15 17 test-bookmarks-current.t
16 18 test-bookmarks-merge.t
17 19 test-bookmarks-rebase.t
18 20 test-bookmarks-strip.t
19 21 test-bookmarks.t
20 22 test-branch-option.t
21 23 test-branch-tag-confict.t
22 24 test-branches.t
23 25 test-bundle-phases.t
24 26 test-bundle-vs-outgoing.t
25 27 test-bundle2-multiple-changegroups.t
26 28 test-cappedreader.py
27 29 test-casecollision.t
28 30 test-cat.t
29 31 test-censor.t
30 32 test-changelog-exec.t
31 33 test-check-commit.t
32 34 test-check-execute.t
33 35 test-check-module-imports.t
34 36 test-check-pyflakes.t
35 37 test-check-pylint.t
36 38 test-check-shbang.t
37 39 test-children.t
38 40 test-clone-pull-corruption.t
39 41 test-clone-r.t
40 42 test-clone-update-order.t
41 43 test-command-template.t
42 44 test-commit-amend.t
43 45 test-commit-unresolved.t
44 46 test-commit.t
45 47 test-completion.t
46 48 test-conflict.t
47 49 test-confused-revert.t
48 50 test-contrib-check-code.t
49 51 test-contrib-check-commit.t
50 52 test-convert-authormap.t
51 53 test-convert-clonebranches.t
52 54 test-convert-datesort.t
53 55 test-convert-filemap.t
54 56 test-convert-hg-sink.t
55 57 test-convert-hg-source.t
56 58 test-convert-hg-startrev.t
57 59 test-copy-move-merge.t
58 60 test-copytrace-heuristics.t
59 61 test-debugbuilddag.t
60 62 test-debugbundle.t
61 63 test-debugextensions.t
62 64 test-debugindexdot.t
63 65 test-debugrename.t
64 66 test-diff-binary-file.t
65 67 test-diff-change.t
66 68 test-diff-copy-depth.t
67 69 test-diff-hashes.t
68 70 test-diff-issue2761.t
69 71 test-diff-newlines.t
70 72 test-diff-reverse.t
71 73 test-diff-subdir.t
72 74 test-diffdir.t
73 75 test-directaccess.t
74 76 test-dirstate-backup.t
75 77 test-dirstate-nonnormalset.t
76 78 test-doctest.py
77 79 test-double-merge.t
78 80 test-drawdag.t
79 81 test-duplicateoptions.py
80 82 test-empty-dir.t
81 83 test-empty-file.t
82 84 test-empty-group.t
83 85 test-empty.t
84 86 test-encoding-func.py
85 87 test-encoding.t
86 88 test-eol-add.t
87 89 test-eol-clone.t
88 90 test-eol-tag.t
89 91 test-eol-update.t
90 92 test-excessive-merge.t
91 93 test-exchange-obsmarkers-case-A1.t
92 94 test-exchange-obsmarkers-case-A2.t
93 95 test-exchange-obsmarkers-case-A3.t
94 96 test-exchange-obsmarkers-case-A4.t
95 97 test-exchange-obsmarkers-case-A5.t
96 98 test-exchange-obsmarkers-case-A6.t
97 99 test-exchange-obsmarkers-case-A7.t
98 100 test-exchange-obsmarkers-case-B1.t
99 101 test-exchange-obsmarkers-case-B2.t
100 102 test-exchange-obsmarkers-case-B3.t
101 103 test-exchange-obsmarkers-case-B4.t
102 104 test-exchange-obsmarkers-case-B5.t
103 105 test-exchange-obsmarkers-case-B6.t
104 106 test-exchange-obsmarkers-case-B7.t
105 107 test-exchange-obsmarkers-case-C1.t
106 108 test-exchange-obsmarkers-case-C2.t
107 109 test-exchange-obsmarkers-case-C3.t
108 110 test-exchange-obsmarkers-case-C4.t
109 111 test-exchange-obsmarkers-case-D1.t
110 112 test-exchange-obsmarkers-case-D2.t
111 113 test-exchange-obsmarkers-case-D3.t
112 114 test-exchange-obsmarkers-case-D4.t
113 115 test-execute-bit.t
114 116 test-extdiff.t
115 117 test-extra-filelog-entry.t
116 118 test-filebranch.t
117 119 test-fileset-generated.t
118 120 test-flags.t
119 121 test-generaldelta.t
120 122 test-getbundle.t
121 123 test-git-export.t
122 124 test-glog-topological.t
123 125 test-gpg.t
124 126 test-graft.t
125 127 test-hghave.t
126 128 test-hgignore.t
127 129 test-hgk.t
128 130 test-hgweb-removed.t
129 131 test-histedit-arguments.t
130 132 test-histedit-base.t
131 133 test-histedit-bookmark-motion.t
132 134 test-histedit-commute.t
133 135 test-histedit-drop.t
134 136 test-histedit-edit.t
135 137 test-histedit-fold-non-commute.t
136 138 test-histedit-fold.t
137 139 test-histedit-no-change.t
138 140 test-histedit-non-commute-abort.t
139 141 test-histedit-non-commute.t
140 142 test-histedit-obsolete.t
141 143 test-histedit-outgoing.t
142 144 test-histedit-templates.t
143 145 test-http-branchmap.t
144 146 test-http-clone-r.t
145 147 test-identify.t
146 148 test-imports-checker.t
147 149 test-inherit-mode.t
148 150 test-issue1089.t
149 151 test-issue1102.t
150 152 test-issue1175.t
151 153 test-issue1306.t
152 154 test-issue1438.t
153 155 test-issue1502.t
154 156 test-issue1802.t
155 157 test-issue1877.t
156 158 test-issue1993.t
157 159 test-issue2137.t
158 160 test-issue3084.t
159 161 test-issue4074.t
160 162 test-issue522.t
161 163 test-issue586.t
162 164 test-issue612.t
163 165 test-issue619.t
164 166 test-issue672.t
165 167 test-issue842.t
166 168 test-journal-exists.t
167 169 test-largefiles-small-disk.t
168 170 test-locate.t
169 171 test-lock-badness.t
170 172 test-logexchange.t
171 173 test-lrucachedict.py
172 174 test-mactext.t
173 175 test-manifest-merging.t
174 176 test-manifest.py
175 177 test-manifest.t
176 178 test-match.py
177 179 test-mdiff.py
178 180 test-merge-closedheads.t
179 181 test-merge-commit.t
180 182 test-merge-criss-cross.t
181 183 test-merge-default.t
182 184 test-merge-internal-tools-pattern.t
183 185 test-merge-local.t
184 186 test-merge-remove.t
185 187 test-merge-revert.t
186 188 test-merge-revert2.t
187 189 test-merge-subrepos.t
188 190 test-merge-symlinks.t
189 191 test-merge-types.t
190 192 test-merge1.t
191 193 test-merge10.t
192 194 test-merge2.t
193 195 test-merge4.t
194 196 test-merge5.t
195 197 test-merge6.t
196 198 test-merge7.t
197 199 test-merge8.t
198 200 test-merge9.t
199 201 test-mq-git.t
200 202 test-mq-pull-from-bundle.t
201 203 test-mq-qdiff.t
202 204 test-mq-qimport-fail-cleanup.t
203 205 test-mq-qqueue.t
204 206 test-mq-qrefresh.t
205 207 test-mq-qsave.t
206 208 test-narrow-clone-no-ellipsis.t
207 209 test-narrow-clone-nonlinear.t
208 210 test-narrow-clone.t
209 211 test-narrow-commit.t
210 212 test-narrow-copies.t
211 213 test-narrow-debugrebuilddirstate.t
212 214 test-narrow-exchange-merges.t
213 215 test-narrow-exchange.t
214 216 test-narrow-merge.t
215 217 test-narrow-patch.t
216 218 test-narrow-patterns.t
217 219 test-narrow-pull.t
218 220 test-narrow-rebase.t
219 221 test-narrow-shallow-merges.t
220 222 test-narrow-shallow.t
221 223 test-narrow-update.t
222 224 test-newbranch.t
223 225 test-obshistory.t
224 226 test-obsmarker-template.t
225 227 test-obsmarkers-effectflag.t
226 228 test-obsolete-bundle-strip.t
227 229 test-obsolete-changeset-exchange.t
228 230 test-obsolete-checkheads.t
229 231 test-obsolete-distributed.t
230 232 test-obsolete-tag-cache.t
231 233 test-parents.t
232 234 test-pathconflicts-merge.t
233 235 test-pathconflicts-update.t
234 236 test-pending.t
235 237 test-permissions.t
236 238 test-phases.t
237 239 test-pull-branch.t
238 240 test-pull-http.t
239 241 test-pull-permission.t
240 242 test-pull-pull-corruption.t
241 243 test-pull-r.t
242 244 test-pull-update.t
243 245 test-purge.t
244 246 test-push-checkheads-partial-C1.t
245 247 test-push-checkheads-partial-C2.t
246 248 test-push-checkheads-partial-C3.t
247 249 test-push-checkheads-partial-C4.t
248 250 test-push-checkheads-pruned-B1.t
249 251 test-push-checkheads-pruned-B2.t
250 252 test-push-checkheads-pruned-B3.t
251 253 test-push-checkheads-pruned-B4.t
252 254 test-push-checkheads-pruned-B5.t
253 255 test-push-checkheads-pruned-B6.t
254 256 test-push-checkheads-pruned-B7.t
255 257 test-push-checkheads-pruned-B8.t
256 258 test-push-checkheads-superceed-A1.t
257 259 test-push-checkheads-superceed-A2.t
258 260 test-push-checkheads-superceed-A3.t
259 261 test-push-checkheads-superceed-A4.t
260 262 test-push-checkheads-superceed-A5.t
261 263 test-push-checkheads-superceed-A6.t
262 264 test-push-checkheads-superceed-A7.t
263 265 test-push-checkheads-superceed-A8.t
264 266 test-push-checkheads-unpushed-D1.t
265 267 test-push-checkheads-unpushed-D2.t
266 268 test-push-checkheads-unpushed-D3.t
267 269 test-push-checkheads-unpushed-D4.t
268 270 test-push-checkheads-unpushed-D5.t
269 271 test-push-checkheads-unpushed-D6.t
270 272 test-push-checkheads-unpushed-D7.t
271 273 test-push-http.t
272 274 test-push-warn.t
273 275 test-pushvars.t
274 276 test-rebase-abort.t
275 277 test-rebase-base-flag.t
276 278 test-rebase-bookmarks.t
277 279 test-rebase-brute-force.t
278 280 test-rebase-cache.t
279 281 test-rebase-check-restore.t
280 282 test-rebase-collapse.t
281 283 test-rebase-dest.t
282 284 test-rebase-detach.t
283 285 test-rebase-emptycommit.t
284 286 test-rebase-inmemory.t
285 287 test-rebase-interruptions.t
286 288 test-rebase-issue-noparam-single-rev.t
287 289 test-rebase-legacy.t
288 290 test-rebase-named-branches.t
289 291 test-rebase-newancestor.t
290 292 test-rebase-obsolete.t
291 293 test-rebase-parameters.t
292 294 test-rebase-partial.t
293 295 test-rebase-pull.t
294 296 test-rebase-rename.t
295 297 test-rebase-scenario-global.t
296 298 test-rebase-templates.t
297 299 test-rebase-transaction.t
298 300 test-record.t
299 301 test-relink.t
300 302 test-remove.t
301 303 test-rename-after-merge.t
302 304 test-rename-dir-merge.t
303 305 test-rename-merge1.t
304 306 test-rename.t
305 307 test-repair-strip.t
306 308 test-repo-compengines.t
307 309 test-resolve.t
308 310 test-revert-flags.t
309 311 test-revert-unknown.t
310 312 test-revlog-ancestry.py
311 313 test-revlog-group-emptyiter.t
312 314 test-revlog-mmapindex.t
313 315 test-revlog-packentry.t
314 316 test-revset-dirstate-parents.t
315 317 test-revset-outgoing.t
316 318 test-run-tests.py
317 319 test-serve.t
318 320 test-share.t
319 321 test-show-stack.t
320 322 test-show-work.t
321 323 test-show.t
322 324 test-simple-update.t
323 325 test-single-head.t
324 326 test-sparse-clear.t
325 327 test-sparse-merges.t
326 328 test-sparse-requirement.t
327 329 test-sparse-verbose-json.t
328 330 test-ssh-clone-r.t
329 331 test-ssh-proto.t
330 332 test-sshserver.py
331 333 test-status-rev.t
332 334 test-status-terse.t
333 335 test-strip-cross.t
334 336 test-strip.t
335 337 test-unamend.t
336 338 test-uncommit.t
337 339 test-unified-test.t
338 340 test-unrelated-pull.t
339 341 test-up-local-change.t
340 342 test-update-branches.t
341 343 test-update-dest.t
342 344 test-update-issue1456.t
343 345 test-update-names.t
344 346 test-update-reverse.t
345 347 test-url-rev.t
346 348 test-username-newline.t
347 349 test-verify.t
348 350 test-websub.t
349 351 test-win32text.t
350 352 test-xdg.t
@@ -1,263 +1,264
1 1 from __future__ import absolute_import
2 2
3 3 import errno
4 4 import os
5 5 import posixpath
6 6 import stat
7 7
8 8 from .i18n import _
9 9 from . import (
10 10 encoding,
11 11 error,
12 12 pycompat,
13 13 util,
14 14 )
15 15
16 16 def _lowerclean(s):
17 17 return encoding.hfsignoreclean(s.lower())
18 18
19 19 class pathauditor(object):
20 20 '''ensure that a filesystem path contains no banned components.
21 21 the following properties of a path are checked:
22 22
23 23 - ends with a directory separator
24 24 - under top-level .hg
25 25 - starts at the root of a windows drive
26 26 - contains ".."
27 27
28 28 More check are also done about the file system states:
29 29 - traverses a symlink (e.g. a/symlink_here/b)
30 30 - inside a nested repository (a callback can be used to approve
31 31 some nested repositories, e.g., subrepositories)
32 32
33 33 The file system checks are only done when 'realfs' is set to True (the
34 34 default). They should be disable then we are auditing path for operation on
35 35 stored history.
36 36
37 37 If 'cached' is set to True, audited paths and sub-directories are cached.
38 38 Be careful to not keep the cache of unmanaged directories for long because
39 39 audited paths may be replaced with symlinks.
40 40 '''
41 41
42 42 def __init__(self, root, callback=None, realfs=True, cached=False):
43 43 self.audited = set()
44 44 self.auditeddir = set()
45 45 self.root = root
46 46 self._realfs = realfs
47 47 self._cached = cached
48 48 self.callback = callback
49 49 if os.path.lexists(root) and not util.fscasesensitive(root):
50 50 self.normcase = util.normcase
51 51 else:
52 52 self.normcase = lambda x: x
53 53
54 54 def __call__(self, path, mode=None):
55 55 '''Check the relative path.
56 56 path may contain a pattern (e.g. foodir/**.txt)'''
57 57
58 58 path = util.localpath(path)
59 59 normpath = self.normcase(path)
60 60 if normpath in self.audited:
61 61 return
62 62 # AIX ignores "/" at end of path, others raise EISDIR.
63 63 if util.endswithsep(path):
64 64 raise error.Abort(_("path ends in directory separator: %s") % path)
65 65 parts = util.splitpath(path)
66 66 if (os.path.splitdrive(path)[0]
67 67 or _lowerclean(parts[0]) in ('.hg', '.hg.', '')
68 68 or pycompat.ospardir in parts):
69 69 raise error.Abort(_("path contains illegal component: %s") % path)
70 70 # Windows shortname aliases
71 71 for p in parts:
72 72 if "~" in p:
73 73 first, last = p.split("~", 1)
74 74 if last.isdigit() and first.upper() in ["HG", "HG8B6C"]:
75 75 raise error.Abort(_("path contains illegal component: %s")
76 76 % path)
77 77 if '.hg' in _lowerclean(path):
78 78 lparts = [_lowerclean(p.lower()) for p in parts]
79 79 for p in '.hg', '.hg.':
80 80 if p in lparts[1:]:
81 81 pos = lparts.index(p)
82 82 base = os.path.join(*parts[:pos])
83 83 raise error.Abort(_("path '%s' is inside nested repo %r")
84 % (path, base))
84 % (path, pycompat.bytestr(base)))
85 85
86 86 normparts = util.splitpath(normpath)
87 87 assert len(parts) == len(normparts)
88 88
89 89 parts.pop()
90 90 normparts.pop()
91 91 prefixes = []
92 92 # It's important that we check the path parts starting from the root.
93 93 # This means we won't accidentally traverse a symlink into some other
94 94 # filesystem (which is potentially expensive to access).
95 95 for i in range(len(parts)):
96 96 prefix = pycompat.ossep.join(parts[:i + 1])
97 97 normprefix = pycompat.ossep.join(normparts[:i + 1])
98 98 if normprefix in self.auditeddir:
99 99 continue
100 100 if self._realfs:
101 101 self._checkfs(prefix, path)
102 102 prefixes.append(normprefix)
103 103
104 104 if self._cached:
105 105 self.audited.add(normpath)
106 106 # only add prefixes to the cache after checking everything: we don't
107 107 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
108 108 self.auditeddir.update(prefixes)
109 109
110 110 def _checkfs(self, prefix, path):
111 111 """raise exception if a file system backed check fails"""
112 112 curpath = os.path.join(self.root, prefix)
113 113 try:
114 114 st = os.lstat(curpath)
115 115 except OSError as err:
116 116 # EINVAL can be raised as invalid path syntax under win32.
117 117 # They must be ignored for patterns can be checked too.
118 118 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
119 119 raise
120 120 else:
121 121 if stat.S_ISLNK(st.st_mode):
122 msg = _('path %r traverses symbolic link %r') % (path, prefix)
122 msg = (_('path %r traverses symbolic link %r')
123 % (pycompat.bytestr(path), pycompat.bytestr(prefix)))
123 124 raise error.Abort(msg)
124 125 elif (stat.S_ISDIR(st.st_mode) and
125 126 os.path.isdir(os.path.join(curpath, '.hg'))):
126 127 if not self.callback or not self.callback(curpath):
127 128 msg = _("path '%s' is inside nested repo %r")
128 raise error.Abort(msg % (path, prefix))
129 raise error.Abort(msg % (path, pycompat.bytestr(prefix)))
129 130
130 131 def check(self, path):
131 132 try:
132 133 self(path)
133 134 return True
134 135 except (OSError, error.Abort):
135 136 return False
136 137
137 138 def canonpath(root, cwd, myname, auditor=None):
138 139 '''return the canonical path of myname, given cwd and root
139 140
140 141 >>> def check(root, cwd, myname):
141 142 ... a = pathauditor(root, realfs=False)
142 143 ... try:
143 144 ... return canonpath(root, cwd, myname, a)
144 145 ... except error.Abort:
145 146 ... return 'aborted'
146 147 >>> def unixonly(root, cwd, myname, expected='aborted'):
147 148 ... if pycompat.iswindows:
148 149 ... return expected
149 150 ... return check(root, cwd, myname)
150 151 >>> def winonly(root, cwd, myname, expected='aborted'):
151 152 ... if not pycompat.iswindows:
152 153 ... return expected
153 154 ... return check(root, cwd, myname)
154 155 >>> winonly(b'd:\\\\repo', b'c:\\\\dir', b'filename')
155 156 'aborted'
156 157 >>> winonly(b'c:\\\\repo', b'c:\\\\dir', b'filename')
157 158 'aborted'
158 159 >>> winonly(b'c:\\\\repo', b'c:\\\\', b'filename')
159 160 'aborted'
160 161 >>> winonly(b'c:\\\\repo', b'c:\\\\', b'repo\\\\filename',
161 162 ... b'filename')
162 163 'filename'
163 164 >>> winonly(b'c:\\\\repo', b'c:\\\\repo', b'filename', b'filename')
164 165 'filename'
165 166 >>> winonly(b'c:\\\\repo', b'c:\\\\repo\\\\subdir', b'filename',
166 167 ... b'subdir/filename')
167 168 'subdir/filename'
168 169 >>> unixonly(b'/repo', b'/dir', b'filename')
169 170 'aborted'
170 171 >>> unixonly(b'/repo', b'/', b'filename')
171 172 'aborted'
172 173 >>> unixonly(b'/repo', b'/', b'repo/filename', b'filename')
173 174 'filename'
174 175 >>> unixonly(b'/repo', b'/repo', b'filename', b'filename')
175 176 'filename'
176 177 >>> unixonly(b'/repo', b'/repo/subdir', b'filename', b'subdir/filename')
177 178 'subdir/filename'
178 179 '''
179 180 if util.endswithsep(root):
180 181 rootsep = root
181 182 else:
182 183 rootsep = root + pycompat.ossep
183 184 name = myname
184 185 if not os.path.isabs(name):
185 186 name = os.path.join(root, cwd, name)
186 187 name = os.path.normpath(name)
187 188 if auditor is None:
188 189 auditor = pathauditor(root)
189 190 if name != rootsep and name.startswith(rootsep):
190 191 name = name[len(rootsep):]
191 192 auditor(name)
192 193 return util.pconvert(name)
193 194 elif name == root:
194 195 return ''
195 196 else:
196 197 # Determine whether `name' is in the hierarchy at or beneath `root',
197 198 # by iterating name=dirname(name) until that causes no change (can't
198 199 # check name == '/', because that doesn't work on windows). The list
199 200 # `rel' holds the reversed list of components making up the relative
200 201 # file name we want.
201 202 rel = []
202 203 while True:
203 204 try:
204 205 s = util.samefile(name, root)
205 206 except OSError:
206 207 s = False
207 208 if s:
208 209 if not rel:
209 210 # name was actually the same as root (maybe a symlink)
210 211 return ''
211 212 rel.reverse()
212 213 name = os.path.join(*rel)
213 214 auditor(name)
214 215 return util.pconvert(name)
215 216 dirname, basename = util.split(name)
216 217 rel.append(basename)
217 218 if dirname == name:
218 219 break
219 220 name = dirname
220 221
221 222 # A common mistake is to use -R, but specify a file relative to the repo
222 223 # instead of cwd. Detect that case, and provide a hint to the user.
223 224 hint = None
224 225 try:
225 226 if cwd != root:
226 227 canonpath(root, root, myname, auditor)
227 228 relpath = util.pathto(root, cwd, '')
228 229 if relpath[-1] == pycompat.ossep:
229 230 relpath = relpath[:-1]
230 231 hint = (_("consider using '--cwd %s'") % relpath)
231 232 except error.Abort:
232 233 pass
233 234
234 235 raise error.Abort(_("%s not under root '%s'") % (myname, root),
235 236 hint=hint)
236 237
237 238 def normasprefix(path):
238 239 '''normalize the specified path as path prefix
239 240
240 241 Returned value can be used safely for "p.startswith(prefix)",
241 242 "p[len(prefix):]", and so on.
242 243
243 244 For efficiency, this expects "path" argument to be already
244 245 normalized by "os.path.normpath", "os.path.realpath", and so on.
245 246
246 247 See also issue3033 for detail about need of this function.
247 248
248 249 >>> normasprefix(b'/foo/bar').replace(pycompat.ossep, b'/')
249 250 '/foo/bar/'
250 251 >>> normasprefix(b'/').replace(pycompat.ossep, b'/')
251 252 '/'
252 253 '''
253 254 d, p = os.path.splitdrive(path)
254 255 if len(p) != len(pycompat.ossep):
255 256 return path + pycompat.ossep
256 257 else:
257 258 return path
258 259
259 260 # forward two methods from posixpath that do what we need, but we'd
260 261 # rather not let our internals know that we're thinking in posix terms
261 262 # - instead we'll let them be oblivious.
262 263 join = posixpath.join
263 264 dirname = posixpath.dirname
General Comments 0
You need to be logged in to leave comments. Login now