# HG changeset patch # User Adrian Buehlmann # Date 1303332211 -7200 # Node ID d1f4e7fd970a10ede833e7073441afc5313570fa # Parent bfeaa88b875d1d54d76bbe07d0e5a41d9825f95c move path_auditor from util to scmutil diff -r bfeaa88b875d -r d1f4e7fd970a mercurial/cmdutil.py --- a/mercurial/cmdutil.py Wed Apr 20 21:41:41 2011 +0200 +++ b/mercurial/cmdutil.py Wed Apr 20 22:43:31 2011 +0200 @@ -286,7 +286,7 @@ similarity = float(opts.get('similarity') or 0) # we'd use status here, except handling of symlinks and ignore is tricky added, unknown, deleted, removed = [], [], [], [] - audit_path = util.path_auditor(repo.root) + audit_path = scmutil.path_auditor(repo.root) m = match(repo, pats, opts) for abs in repo.walk(m): target = repo.wjoin(abs) diff -r bfeaa88b875d -r d1f4e7fd970a mercurial/commands.py --- a/mercurial/commands.py Wed Apr 20 21:41:41 2011 +0200 +++ b/mercurial/commands.py Wed Apr 20 22:43:31 2011 +0200 @@ -3554,7 +3554,7 @@ fc = ctx[f] repo.wwrite(f, fc.data(), fc.flags()) - audit_path = util.path_auditor(repo.root) + audit_path = scmutil.path_auditor(repo.root) for f in remove[0]: if repo.dirstate[f] == 'a': repo.dirstate.forget(f) diff -r bfeaa88b875d -r d1f4e7fd970a mercurial/localrepo.py --- a/mercurial/localrepo.py Wed Apr 20 21:41:41 2011 +0200 +++ b/mercurial/localrepo.py Wed Apr 20 22:43:31 2011 +0200 @@ -31,7 +31,7 @@ self.root = os.path.realpath(util.expandpath(path)) self.path = os.path.join(self.root, ".hg") self.origroot = path - self.auditor = util.path_auditor(self.root, self._checknested) + self.auditor = scmutil.path_auditor(self.root, self._checknested) self.opener = scmutil.opener(self.path) self.wopener = scmutil.opener(self.root) self.baseui = baseui diff -r bfeaa88b875d -r d1f4e7fd970a mercurial/merge.py --- a/mercurial/merge.py Wed Apr 20 21:41:41 2011 +0200 +++ b/mercurial/merge.py Wed Apr 20 22:43:31 2011 +0200 @@ -7,7 +7,7 @@ from node import nullid, nullrev, hex, bin from i18n import _ -import util, filemerge, copies, subrepo +import scmutil, util, filemerge, copies, subrepo import errno, os, shutil class mergestate(object): @@ -303,7 +303,7 @@ repo.ui.debug("removing %s\n" % f) os.unlink(repo.wjoin(f)) - audit_path = util.path_auditor(repo.root) + audit_path = scmutil.path_auditor(repo.root) numupdates = len(action) for i, a in enumerate(action): diff -r bfeaa88b875d -r d1f4e7fd970a mercurial/scmutil.py --- a/mercurial/scmutil.py Wed Apr 20 21:41:41 2011 +0200 +++ b/mercurial/scmutil.py Wed Apr 20 22:43:31 2011 +0200 @@ -7,7 +7,7 @@ from i18n import _ import util, error -import os, errno +import os, errno, stat def checkportable(ui, f): '''Check if filename f is portable and warn or abort depending on config''' @@ -26,6 +26,81 @@ raise error.ConfigError( _("ui.portablefilenames value is invalid ('%s')") % val) +class path_auditor(object): + '''ensure that a filesystem path contains no banned components. + the following properties of a path are checked: + + - ends with a directory separator + - under top-level .hg + - starts at the root of a windows drive + - contains ".." + - traverses a symlink (e.g. a/symlink_here/b) + - inside a nested repository (a callback can be used to approve + some nested repositories, e.g., subrepositories) + ''' + + def __init__(self, root, callback=None): + self.audited = set() + self.auditeddir = set() + self.root = root + self.callback = callback + + def __call__(self, path): + '''Check the relative path. + path may contain a pattern (e.g. foodir/**.txt)''' + + if path in self.audited: + return + # AIX ignores "/" at end of path, others raise EISDIR. + if util.endswithsep(path): + raise util.Abort(_("path ends in directory separator: %s") % path) + normpath = os.path.normcase(path) + parts = util.splitpath(normpath) + if (os.path.splitdrive(path)[0] + or parts[0].lower() in ('.hg', '.hg.', '') + or os.pardir in parts): + raise util.Abort(_("path contains illegal component: %s") % path) + if '.hg' in path.lower(): + lparts = [p.lower() for p in parts] + for p in '.hg', '.hg.': + if p in lparts[1:]: + pos = lparts.index(p) + base = os.path.join(*parts[:pos]) + raise util.Abort(_('path %r is inside nested repo %r') + % (path, base)) + + parts.pop() + prefixes = [] + while parts: + prefix = os.sep.join(parts) + if prefix in self.auditeddir: + break + curpath = os.path.join(self.root, prefix) + try: + st = os.lstat(curpath) + except OSError, err: + # EINVAL can be raised as invalid path syntax under win32. + # They must be ignored for patterns can be checked too. + if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL): + raise + else: + if stat.S_ISLNK(st.st_mode): + raise util.Abort( + _('path %r traverses symbolic link %r') + % (path, prefix)) + elif (stat.S_ISDIR(st.st_mode) and + os.path.isdir(os.path.join(curpath, '.hg'))): + if not self.callback or not self.callback(curpath): + raise util.Abort(_('path %r is inside nested repo %r') % + (path, prefix)) + prefixes.append(prefix) + parts.pop() + + self.audited.add(path) + # only add prefixes to the cache after checking everything: we don't + # want to add "foo/bar/baz" before checking if there's a "foo/.hg" + self.auditeddir.update(prefixes) + class opener(object): '''Open files relative to a base directory @@ -35,7 +110,7 @@ def __init__(self, base, audit=True): self.base = base if audit: - self.auditor = util.path_auditor(base) + self.auditor = path_auditor(base) else: self.auditor = util.always self.createmode = None @@ -132,7 +207,7 @@ name = os.path.join(root, cwd, name) name = os.path.normpath(name) if auditor is None: - auditor = util.path_auditor(root) + auditor = path_auditor(root) if name != rootsep and name.startswith(rootsep): name = name[len(rootsep):] auditor(name) diff -r bfeaa88b875d -r d1f4e7fd970a mercurial/subrepo.py --- a/mercurial/subrepo.py Wed Apr 20 21:41:41 2011 +0200 +++ b/mercurial/subrepo.py Wed Apr 20 22:43:31 2011 +0200 @@ -8,7 +8,7 @@ import errno, os, re, xml.dom.minidom, shutil, posixpath import stat, subprocess, tarfile from i18n import _ -import config, util, node, error, cmdutil, url, bookmarks +import config, scmutil, util, node, error, cmdutil, url, bookmarks hg = None nullstate = ('', '', 'empty') @@ -234,7 +234,7 @@ import hg as h hg = h - util.path_auditor(ctx._repo.root)(path) + scmutil.path_auditor(ctx._repo.root)(path) state = ctx.substate.get(path, nullstate) if state[2] not in types: raise util.Abort(_('unknown subrepo type %s') % state[2]) diff -r bfeaa88b875d -r d1f4e7fd970a mercurial/util.py --- a/mercurial/util.py Wed Apr 20 21:41:41 2011 +0200 +++ b/mercurial/util.py Wed Apr 20 22:43:31 2011 +0200 @@ -16,7 +16,7 @@ from i18n import _ import error, osutil, encoding import errno, re, shutil, sys, tempfile, traceback -import os, stat, time, calendar, textwrap, unicodedata, signal +import os, time, calendar, textwrap, unicodedata, signal import imp, socket # Python compatibility @@ -492,80 +492,6 @@ return _("filename ends with '%s', which is not allowed " "on Windows") % t -class path_auditor(object): - '''ensure that a filesystem path contains no banned components. - the following properties of a path are checked: - - - ends with a directory separator - - under top-level .hg - - starts at the root of a windows drive - - contains ".." - - traverses a symlink (e.g. a/symlink_here/b) - - inside a nested repository (a callback can be used to approve - some nested repositories, e.g., subrepositories) - ''' - - def __init__(self, root, callback=None): - self.audited = set() - self.auditeddir = set() - self.root = root - self.callback = callback - - def __call__(self, path): - '''Check the relative path. - path may contain a pattern (e.g. foodir/**.txt)''' - - if path in self.audited: - return - # AIX ignores "/" at end of path, others raise EISDIR. - if endswithsep(path): - raise Abort(_("path ends in directory separator: %s") % path) - normpath = os.path.normcase(path) - parts = splitpath(normpath) - if (os.path.splitdrive(path)[0] - or parts[0].lower() in ('.hg', '.hg.', '') - or os.pardir in parts): - raise Abort(_("path contains illegal component: %s") % path) - if '.hg' in path.lower(): - lparts = [p.lower() for p in parts] - for p in '.hg', '.hg.': - if p in lparts[1:]: - pos = lparts.index(p) - base = os.path.join(*parts[:pos]) - raise Abort(_('path %r is inside nested repo %r') - % (path, base)) - - parts.pop() - prefixes = [] - while parts: - prefix = os.sep.join(parts) - if prefix in self.auditeddir: - break - curpath = os.path.join(self.root, prefix) - try: - st = os.lstat(curpath) - except OSError, err: - # EINVAL can be raised as invalid path syntax under win32. - # They must be ignored for patterns can be checked too. - if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL): - raise - else: - if stat.S_ISLNK(st.st_mode): - raise Abort(_('path %r traverses symbolic link %r') % - (path, prefix)) - elif (stat.S_ISDIR(st.st_mode) and - os.path.isdir(os.path.join(curpath, '.hg'))): - if not self.callback or not self.callback(curpath): - raise Abort(_('path %r is inside nested repo %r') % - (path, prefix)) - prefixes.append(prefix) - parts.pop() - - self.audited.add(path) - # only add prefixes to the cache after checking everything: we don't - # want to add "foo/bar/baz" before checking if there's a "foo/.hg" - self.auditeddir.update(prefixes) - def lookup_reg(key, name=None, scope=None): return None