##// END OF EJS Templates
move path_auditor from util to scmutil
Adrian Buehlmann -
r13972:d1f4e7fd default
parent child Browse files
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 = util.path_auditor(base)
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 = util.path_auditor(root)
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, stat, time, calendar, textwrap, unicodedata, signal
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