Show More
@@ -286,7 +286,7 b' def addremove(repo, pats=[], opts={}, dr' | |||
|
286 | 286 | similarity = float(opts.get('similarity') or 0) |
|
287 | 287 | # we'd use status here, except handling of symlinks and ignore is tricky |
|
288 | 288 | added, unknown, deleted, removed = [], [], [], [] |
|
289 | audit_path = util.path_auditor(repo.root) | |
|
289 | audit_path = scmutil.path_auditor(repo.root) | |
|
290 | 290 | m = match(repo, pats, opts) |
|
291 | 291 | for abs in repo.walk(m): |
|
292 | 292 | target = repo.wjoin(abs) |
@@ -3554,7 +3554,7 b' def revert(ui, repo, *pats, **opts):' | |||
|
3554 | 3554 | fc = ctx[f] |
|
3555 | 3555 | repo.wwrite(f, fc.data(), fc.flags()) |
|
3556 | 3556 | |
|
3557 | audit_path = util.path_auditor(repo.root) | |
|
3557 | audit_path = scmutil.path_auditor(repo.root) | |
|
3558 | 3558 | for f in remove[0]: |
|
3559 | 3559 | if repo.dirstate[f] == 'a': |
|
3560 | 3560 | repo.dirstate.forget(f) |
@@ -31,7 +31,7 b' class localrepository(repo.repository):' | |||
|
31 | 31 | self.root = os.path.realpath(util.expandpath(path)) |
|
32 | 32 | self.path = os.path.join(self.root, ".hg") |
|
33 | 33 | self.origroot = path |
|
34 | self.auditor = util.path_auditor(self.root, self._checknested) | |
|
34 | self.auditor = scmutil.path_auditor(self.root, self._checknested) | |
|
35 | 35 | self.opener = scmutil.opener(self.path) |
|
36 | 36 | self.wopener = scmutil.opener(self.root) |
|
37 | 37 | self.baseui = baseui |
@@ -7,7 +7,7 b'' | |||
|
7 | 7 | |
|
8 | 8 | from node import nullid, nullrev, hex, bin |
|
9 | 9 | from i18n import _ |
|
10 | import util, filemerge, copies, subrepo | |
|
10 | import scmutil, util, filemerge, copies, subrepo | |
|
11 | 11 | import errno, os, shutil |
|
12 | 12 | |
|
13 | 13 | class mergestate(object): |
@@ -303,7 +303,7 b' def applyupdates(repo, action, wctx, mct' | |||
|
303 | 303 | repo.ui.debug("removing %s\n" % f) |
|
304 | 304 | os.unlink(repo.wjoin(f)) |
|
305 | 305 | |
|
306 | audit_path = util.path_auditor(repo.root) | |
|
306 | audit_path = scmutil.path_auditor(repo.root) | |
|
307 | 307 | |
|
308 | 308 | numupdates = len(action) |
|
309 | 309 | for i, a in enumerate(action): |
@@ -7,7 +7,7 b'' | |||
|
7 | 7 | |
|
8 | 8 | from i18n import _ |
|
9 | 9 | import util, error |
|
10 | import os, errno | |
|
10 | import os, errno, stat | |
|
11 | 11 | |
|
12 | 12 | def checkportable(ui, f): |
|
13 | 13 | '''Check if filename f is portable and warn or abort depending on config''' |
@@ -26,6 +26,81 b' def checkportable(ui, f):' | |||
|
26 | 26 | raise error.ConfigError( |
|
27 | 27 | _("ui.portablefilenames value is invalid ('%s')") % val) |
|
28 | 28 | |
|
29 | class path_auditor(object): | |
|
30 | '''ensure that a filesystem path contains no banned components. | |
|
31 | the following properties of a path are checked: | |
|
32 | ||
|
33 | - ends with a directory separator | |
|
34 | - under top-level .hg | |
|
35 | - starts at the root of a windows drive | |
|
36 | - contains ".." | |
|
37 | - traverses a symlink (e.g. a/symlink_here/b) | |
|
38 | - inside a nested repository (a callback can be used to approve | |
|
39 | some nested repositories, e.g., subrepositories) | |
|
40 | ''' | |
|
41 | ||
|
42 | def __init__(self, root, callback=None): | |
|
43 | self.audited = set() | |
|
44 | self.auditeddir = set() | |
|
45 | self.root = root | |
|
46 | self.callback = callback | |
|
47 | ||
|
48 | def __call__(self, path): | |
|
49 | '''Check the relative path. | |
|
50 | path may contain a pattern (e.g. foodir/**.txt)''' | |
|
51 | ||
|
52 | if path in self.audited: | |
|
53 | return | |
|
54 | # AIX ignores "/" at end of path, others raise EISDIR. | |
|
55 | if util.endswithsep(path): | |
|
56 | raise util.Abort(_("path ends in directory separator: %s") % path) | |
|
57 | normpath = os.path.normcase(path) | |
|
58 | parts = util.splitpath(normpath) | |
|
59 | if (os.path.splitdrive(path)[0] | |
|
60 | or parts[0].lower() in ('.hg', '.hg.', '') | |
|
61 | or os.pardir in parts): | |
|
62 | raise util.Abort(_("path contains illegal component: %s") % path) | |
|
63 | if '.hg' in path.lower(): | |
|
64 | lparts = [p.lower() for p in parts] | |
|
65 | for p in '.hg', '.hg.': | |
|
66 | if p in lparts[1:]: | |
|
67 | pos = lparts.index(p) | |
|
68 | base = os.path.join(*parts[:pos]) | |
|
69 | raise util.Abort(_('path %r is inside nested repo %r') | |
|
70 | % (path, base)) | |
|
71 | ||
|
72 | parts.pop() | |
|
73 | prefixes = [] | |
|
74 | while parts: | |
|
75 | prefix = os.sep.join(parts) | |
|
76 | if prefix in self.auditeddir: | |
|
77 | break | |
|
78 | curpath = os.path.join(self.root, prefix) | |
|
79 | try: | |
|
80 | st = os.lstat(curpath) | |
|
81 | except OSError, err: | |
|
82 | # EINVAL can be raised as invalid path syntax under win32. | |
|
83 | # They must be ignored for patterns can be checked too. | |
|
84 | if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL): | |
|
85 | raise | |
|
86 | else: | |
|
87 | if stat.S_ISLNK(st.st_mode): | |
|
88 | raise util.Abort( | |
|
89 | _('path %r traverses symbolic link %r') | |
|
90 | % (path, prefix)) | |
|
91 | elif (stat.S_ISDIR(st.st_mode) and | |
|
92 | os.path.isdir(os.path.join(curpath, '.hg'))): | |
|
93 | if not self.callback or not self.callback(curpath): | |
|
94 | raise util.Abort(_('path %r is inside nested repo %r') % | |
|
95 | (path, prefix)) | |
|
96 | prefixes.append(prefix) | |
|
97 | parts.pop() | |
|
98 | ||
|
99 | self.audited.add(path) | |
|
100 | # only add prefixes to the cache after checking everything: we don't | |
|
101 | # want to add "foo/bar/baz" before checking if there's a "foo/.hg" | |
|
102 | self.auditeddir.update(prefixes) | |
|
103 | ||
|
29 | 104 | class opener(object): |
|
30 | 105 | '''Open files relative to a base directory |
|
31 | 106 | |
@@ -35,7 +110,7 b' class opener(object):' | |||
|
35 | 110 | def __init__(self, base, audit=True): |
|
36 | 111 | self.base = base |
|
37 | 112 | if audit: |
|
38 |
self.auditor = |
|
|
113 | self.auditor = path_auditor(base) | |
|
39 | 114 | else: |
|
40 | 115 | self.auditor = util.always |
|
41 | 116 | self.createmode = None |
@@ -132,7 +207,7 b' def canonpath(root, cwd, myname, auditor' | |||
|
132 | 207 | name = os.path.join(root, cwd, name) |
|
133 | 208 | name = os.path.normpath(name) |
|
134 | 209 | if auditor is None: |
|
135 |
auditor = |
|
|
210 | auditor = path_auditor(root) | |
|
136 | 211 | if name != rootsep and name.startswith(rootsep): |
|
137 | 212 | name = name[len(rootsep):] |
|
138 | 213 | auditor(name) |
@@ -8,7 +8,7 b'' | |||
|
8 | 8 | import errno, os, re, xml.dom.minidom, shutil, posixpath |
|
9 | 9 | import stat, subprocess, tarfile |
|
10 | 10 | from i18n import _ |
|
11 | import config, util, node, error, cmdutil, url, bookmarks | |
|
11 | import config, scmutil, util, node, error, cmdutil, url, bookmarks | |
|
12 | 12 | hg = None |
|
13 | 13 | |
|
14 | 14 | nullstate = ('', '', 'empty') |
@@ -234,7 +234,7 b' def subrepo(ctx, path):' | |||
|
234 | 234 | import hg as h |
|
235 | 235 | hg = h |
|
236 | 236 | |
|
237 | util.path_auditor(ctx._repo.root)(path) | |
|
237 | scmutil.path_auditor(ctx._repo.root)(path) | |
|
238 | 238 | state = ctx.substate.get(path, nullstate) |
|
239 | 239 | if state[2] not in types: |
|
240 | 240 | raise util.Abort(_('unknown subrepo type %s') % state[2]) |
@@ -16,7 +16,7 b' hide platform-specific details from the ' | |||
|
16 | 16 | from i18n import _ |
|
17 | 17 | import error, osutil, encoding |
|
18 | 18 | import errno, re, shutil, sys, tempfile, traceback |
|
19 |
import os |
|
|
19 | import os, time, calendar, textwrap, unicodedata, signal | |
|
20 | 20 | import imp, socket |
|
21 | 21 | |
|
22 | 22 | # Python compatibility |
@@ -492,80 +492,6 b' def checkwinfilename(path):' | |||
|
492 | 492 | return _("filename ends with '%s', which is not allowed " |
|
493 | 493 | "on Windows") % t |
|
494 | 494 | |
|
495 | class path_auditor(object): | |
|
496 | '''ensure that a filesystem path contains no banned components. | |
|
497 | the following properties of a path are checked: | |
|
498 | ||
|
499 | - ends with a directory separator | |
|
500 | - under top-level .hg | |
|
501 | - starts at the root of a windows drive | |
|
502 | - contains ".." | |
|
503 | - traverses a symlink (e.g. a/symlink_here/b) | |
|
504 | - inside a nested repository (a callback can be used to approve | |
|
505 | some nested repositories, e.g., subrepositories) | |
|
506 | ''' | |
|
507 | ||
|
508 | def __init__(self, root, callback=None): | |
|
509 | self.audited = set() | |
|
510 | self.auditeddir = set() | |
|
511 | self.root = root | |
|
512 | self.callback = callback | |
|
513 | ||
|
514 | def __call__(self, path): | |
|
515 | '''Check the relative path. | |
|
516 | path may contain a pattern (e.g. foodir/**.txt)''' | |
|
517 | ||
|
518 | if path in self.audited: | |
|
519 | return | |
|
520 | # AIX ignores "/" at end of path, others raise EISDIR. | |
|
521 | if endswithsep(path): | |
|
522 | raise Abort(_("path ends in directory separator: %s") % path) | |
|
523 | normpath = os.path.normcase(path) | |
|
524 | parts = splitpath(normpath) | |
|
525 | if (os.path.splitdrive(path)[0] | |
|
526 | or parts[0].lower() in ('.hg', '.hg.', '') | |
|
527 | or os.pardir in parts): | |
|
528 | raise Abort(_("path contains illegal component: %s") % path) | |
|
529 | if '.hg' in path.lower(): | |
|
530 | lparts = [p.lower() for p in parts] | |
|
531 | for p in '.hg', '.hg.': | |
|
532 | if p in lparts[1:]: | |
|
533 | pos = lparts.index(p) | |
|
534 | base = os.path.join(*parts[:pos]) | |
|
535 | raise Abort(_('path %r is inside nested repo %r') | |
|
536 | % (path, base)) | |
|
537 | ||
|
538 | parts.pop() | |
|
539 | prefixes = [] | |
|
540 | while parts: | |
|
541 | prefix = os.sep.join(parts) | |
|
542 | if prefix in self.auditeddir: | |
|
543 | break | |
|
544 | curpath = os.path.join(self.root, prefix) | |
|
545 | try: | |
|
546 | st = os.lstat(curpath) | |
|
547 | except OSError, err: | |
|
548 | # EINVAL can be raised as invalid path syntax under win32. | |
|
549 | # They must be ignored for patterns can be checked too. | |
|
550 | if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL): | |
|
551 | raise | |
|
552 | else: | |
|
553 | if stat.S_ISLNK(st.st_mode): | |
|
554 | raise Abort(_('path %r traverses symbolic link %r') % | |
|
555 | (path, prefix)) | |
|
556 | elif (stat.S_ISDIR(st.st_mode) and | |
|
557 | os.path.isdir(os.path.join(curpath, '.hg'))): | |
|
558 | if not self.callback or not self.callback(curpath): | |
|
559 | raise Abort(_('path %r is inside nested repo %r') % | |
|
560 | (path, prefix)) | |
|
561 | prefixes.append(prefix) | |
|
562 | parts.pop() | |
|
563 | ||
|
564 | self.audited.add(path) | |
|
565 | # only add prefixes to the cache after checking everything: we don't | |
|
566 | # want to add "foo/bar/baz" before checking if there's a "foo/.hg" | |
|
567 | self.auditeddir.update(prefixes) | |
|
568 | ||
|
569 | 495 | def lookup_reg(key, name=None, scope=None): |
|
570 | 496 | return None |
|
571 | 497 |
General Comments 0
You need to be logged in to leave comments.
Login now