mercurial/util.py
changeset 13972 d1f4e7fd970a
parent 13971 bfeaa88b875d
child 13974 23f2736abce3
equal deleted inserted replaced
13971:bfeaa88b875d 13972:d1f4e7fd970a
    14 """
    14 """
    15 
    15 
    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
    23 
    23 
    24 def sha1(s):
    24 def sha1(s):
   490         t = n[-1]
   490         t = n[-1]
   491         if t in '. ':
   491         if t in '. ':
   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 
   572 def hidewindow():
   498 def hidewindow():
   573     """Hide current shell window.
   499     """Hide current shell window.