Show More
@@ -286,7 +286,7 b' def addremove(repo, pats=[], opts={}, dr' | |||||
286 | similarity = float(opts.get('similarity') or 0) |
|
286 | similarity = float(opts.get('similarity') or 0) | |
287 | # we'd use status here, except handling of symlinks and ignore is tricky |
|
287 | # we'd use status here, except handling of symlinks and ignore is tricky | |
288 | added, unknown, deleted, removed = [], [], [], [] |
|
288 | added, unknown, deleted, removed = [], [], [], [] | |
289 | audit_path = util.path_auditor(repo.root) |
|
289 | audit_path = scmutil.path_auditor(repo.root) | |
290 | m = match(repo, pats, opts) |
|
290 | m = match(repo, pats, opts) | |
291 | for abs in repo.walk(m): |
|
291 | for abs in repo.walk(m): | |
292 | target = repo.wjoin(abs) |
|
292 | target = repo.wjoin(abs) |
@@ -3554,7 +3554,7 b' def revert(ui, repo, *pats, **opts):' | |||||
3554 | fc = ctx[f] |
|
3554 | fc = ctx[f] | |
3555 | repo.wwrite(f, fc.data(), fc.flags()) |
|
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 | for f in remove[0]: |
|
3558 | for f in remove[0]: | |
3559 | if repo.dirstate[f] == 'a': |
|
3559 | if repo.dirstate[f] == 'a': | |
3560 | repo.dirstate.forget(f) |
|
3560 | repo.dirstate.forget(f) |
@@ -31,7 +31,7 b' class localrepository(repo.repository):' | |||||
31 | self.root = os.path.realpath(util.expandpath(path)) |
|
31 | self.root = os.path.realpath(util.expandpath(path)) | |
32 | self.path = os.path.join(self.root, ".hg") |
|
32 | self.path = os.path.join(self.root, ".hg") | |
33 | self.origroot = path |
|
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 | self.opener = scmutil.opener(self.path) |
|
35 | self.opener = scmutil.opener(self.path) | |
36 | self.wopener = scmutil.opener(self.root) |
|
36 | self.wopener = scmutil.opener(self.root) | |
37 | self.baseui = baseui |
|
37 | self.baseui = baseui |
@@ -7,7 +7,7 b'' | |||||
7 |
|
7 | |||
8 | from node import nullid, nullrev, hex, bin |
|
8 | from node import nullid, nullrev, hex, bin | |
9 | from i18n import _ |
|
9 | from i18n import _ | |
10 | import util, filemerge, copies, subrepo |
|
10 | import scmutil, util, filemerge, copies, subrepo | |
11 | import errno, os, shutil |
|
11 | import errno, os, shutil | |
12 |
|
12 | |||
13 | class mergestate(object): |
|
13 | class mergestate(object): | |
@@ -303,7 +303,7 b' def applyupdates(repo, action, wctx, mct' | |||||
303 | repo.ui.debug("removing %s\n" % f) |
|
303 | repo.ui.debug("removing %s\n" % f) | |
304 | os.unlink(repo.wjoin(f)) |
|
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 | numupdates = len(action) |
|
308 | numupdates = len(action) | |
309 | for i, a in enumerate(action): |
|
309 | for i, a in enumerate(action): |
@@ -7,7 +7,7 b'' | |||||
7 |
|
7 | |||
8 | from i18n import _ |
|
8 | from i18n import _ | |
9 | import util, error |
|
9 | import util, error | |
10 | import os, errno |
|
10 | import os, errno, stat | |
11 |
|
11 | |||
12 | def checkportable(ui, f): |
|
12 | def checkportable(ui, f): | |
13 | '''Check if filename f is portable and warn or abort depending on config''' |
|
13 | '''Check if filename f is portable and warn or abort depending on config''' | |
@@ -26,6 +26,81 b' def checkportable(ui, f):' | |||||
26 | raise error.ConfigError( |
|
26 | raise error.ConfigError( | |
27 | _("ui.portablefilenames value is invalid ('%s')") % val) |
|
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 | class opener(object): |
|
104 | class opener(object): | |
30 | '''Open files relative to a base directory |
|
105 | '''Open files relative to a base directory | |
31 |
|
106 | |||
@@ -35,7 +110,7 b' class opener(object):' | |||||
35 | def __init__(self, base, audit=True): |
|
110 | def __init__(self, base, audit=True): | |
36 | self.base = base |
|
111 | self.base = base | |
37 | if audit: |
|
112 | if audit: | |
38 |
self.auditor = |
|
113 | self.auditor = path_auditor(base) | |
39 | else: |
|
114 | else: | |
40 | self.auditor = util.always |
|
115 | self.auditor = util.always | |
41 | self.createmode = None |
|
116 | self.createmode = None | |
@@ -132,7 +207,7 b' def canonpath(root, cwd, myname, auditor' | |||||
132 | name = os.path.join(root, cwd, name) |
|
207 | name = os.path.join(root, cwd, name) | |
133 | name = os.path.normpath(name) |
|
208 | name = os.path.normpath(name) | |
134 | if auditor is None: |
|
209 | if auditor is None: | |
135 |
auditor = |
|
210 | auditor = path_auditor(root) | |
136 | if name != rootsep and name.startswith(rootsep): |
|
211 | if name != rootsep and name.startswith(rootsep): | |
137 | name = name[len(rootsep):] |
|
212 | name = name[len(rootsep):] | |
138 | auditor(name) |
|
213 | auditor(name) |
@@ -8,7 +8,7 b'' | |||||
8 | import errno, os, re, xml.dom.minidom, shutil, posixpath |
|
8 | import errno, os, re, xml.dom.minidom, shutil, posixpath | |
9 | import stat, subprocess, tarfile |
|
9 | import stat, subprocess, tarfile | |
10 | from i18n import _ |
|
10 | from i18n import _ | |
11 | import config, util, node, error, cmdutil, url, bookmarks |
|
11 | import config, scmutil, util, node, error, cmdutil, url, bookmarks | |
12 | hg = None |
|
12 | hg = None | |
13 |
|
13 | |||
14 | nullstate = ('', '', 'empty') |
|
14 | nullstate = ('', '', 'empty') | |
@@ -234,7 +234,7 b' def subrepo(ctx, path):' | |||||
234 | import hg as h |
|
234 | import hg as h | |
235 | hg = h |
|
235 | hg = h | |
236 |
|
236 | |||
237 | util.path_auditor(ctx._repo.root)(path) |
|
237 | scmutil.path_auditor(ctx._repo.root)(path) | |
238 | state = ctx.substate.get(path, nullstate) |
|
238 | state = ctx.substate.get(path, nullstate) | |
239 | if state[2] not in types: |
|
239 | if state[2] not in types: | |
240 | raise util.Abort(_('unknown subrepo type %s') % state[2]) |
|
240 | raise util.Abort(_('unknown subrepo type %s') % state[2]) |
@@ -16,7 +16,7 b' hide platform-specific details from the ' | |||||
16 | from i18n import _ |
|
16 | from i18n import _ | |
17 | import error, osutil, encoding |
|
17 | import error, osutil, encoding | |
18 | import errno, re, shutil, sys, tempfile, traceback |
|
18 | import errno, re, shutil, sys, tempfile, traceback | |
19 |
import os |
|
19 | import os, time, calendar, textwrap, unicodedata, signal | |
20 | import imp, socket |
|
20 | import imp, socket | |
21 |
|
21 | |||
22 | # Python compatibility |
|
22 | # Python compatibility | |
@@ -492,80 +492,6 b' def checkwinfilename(path):' | |||||
492 | return _("filename ends with '%s', which is not allowed " |
|
492 | return _("filename ends with '%s', which is not allowed " | |
493 | "on Windows") % t |
|
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 | def lookup_reg(key, name=None, scope=None): |
|
495 | def lookup_reg(key, name=None, scope=None): | |
570 | return None |
|
496 | return None | |
571 |
|
497 |
General Comments 0
You need to be logged in to leave comments.
Login now