mercurial/util.py
changeset 5141 d316124ebbea
parent 5094 a2c11f49e989
child 5142 d84329a11fdd
equal deleted inserted replaced
5140:f6c520fd70cf 5141:d316124ebbea
    11 This contains helper routines that are independent of the SCM core and hide
    11 This contains helper routines that are independent of the SCM core and hide
    12 platform-specific details from the core.
    12 platform-specific details from the core.
    13 """
    13 """
    14 
    14 
    15 from i18n import _
    15 from i18n import _
    16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile
    16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile, strutil
    17 import os, threading, time, calendar, ConfigParser, locale, glob
    17 import os, stat, threading, time, calendar, ConfigParser, locale, glob
    18 
    18 
    19 try:
    19 try:
    20     set = set
    20     set = set
    21     frozenset = frozenset
    21     frozenset = frozenset
    22 except NameError:
    22 except NameError:
   364         rootsep = root + os.sep
   364         rootsep = root + os.sep
   365     name = myname
   365     name = myname
   366     if not os.path.isabs(name):
   366     if not os.path.isabs(name):
   367         name = os.path.join(root, cwd, name)
   367         name = os.path.join(root, cwd, name)
   368     name = os.path.normpath(name)
   368     name = os.path.normpath(name)
       
   369     audit_path = path_auditor(root)
   369     if name != rootsep and name.startswith(rootsep):
   370     if name != rootsep and name.startswith(rootsep):
   370         name = name[len(rootsep):]
   371         name = name[len(rootsep):]
   371         audit_path(name)
   372         audit_path(name)
   372         return pconvert(name)
   373         return pconvert(name)
   373     elif name == root:
   374     elif name == root:
   678                 hardlink = False
   679                 hardlink = False
   679                 shutil.copy(src, dst)
   680                 shutil.copy(src, dst)
   680         else:
   681         else:
   681             shutil.copy(src, dst)
   682             shutil.copy(src, dst)
   682 
   683 
   683 def audit_path(path):
   684 class path_auditor(object):
   684     """Abort if path contains dangerous components"""
   685     '''ensure that a filesystem path contains no banned components.
   685     parts = os.path.normcase(path).split(os.sep)
   686     the following properties of a path are checked:
   686     if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
   687 
   687         or os.pardir in parts):
   688     - under top-level .hg
   688         raise Abort(_("path contains illegal component: %s") % path)
   689     - starts at the root of a windows drive
       
   690     - contains ".."
       
   691     - traverses a symlink (e.g. a/symlink_here/b)
       
   692     - inside a nested repository'''
       
   693 
       
   694     def __init__(self, root):
       
   695         self.audited = {}
       
   696         self.root = root
       
   697 
       
   698     def __call__(self, path):
       
   699         if path in self.audited:
       
   700             return
       
   701         parts = os.path.normcase(path).split(os.sep)
       
   702         if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
       
   703             or os.pardir in parts):
       
   704             raise Abort(_("path contains illegal component: %s") % path)
       
   705         def check(prefix):
       
   706             curpath = os.path.join(self.root, prefix)
       
   707             try:
       
   708                 st = os.lstat(curpath)
       
   709             except OSError, err:
       
   710                 if err.errno != errno.ENOENT:
       
   711                     raise
       
   712             else:
       
   713                 if stat.S_ISLNK(st.st_mode):
       
   714                     raise Abort(_('path %r traverses symbolic link %r') %
       
   715                                 (path, prefix))
       
   716                 if os.path.exists(os.path.join(curpath, '.hg')):
       
   717                     raise Abort(_('path %r is inside repo %r') %
       
   718                                 (path, prefix))
       
   719             self.audited[prefix] = True
       
   720         for c in strutil.rfindall(path, os.sep):
       
   721             check(path[:c])
       
   722         self.audited[path] = True
   689 
   723 
   690 def _makelock_file(info, pathname):
   724 def _makelock_file(info, pathname):
   691     ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
   725     ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
   692     os.write(ld, info)
   726     os.write(ld, info)
   693     os.close(ld)
   727     os.close(ld)
  1260     This class is used to hide the details of COW semantics and
  1294     This class is used to hide the details of COW semantics and
  1261     remote file access from higher level code.
  1295     remote file access from higher level code.
  1262     """
  1296     """
  1263     def __init__(self, base, audit=True):
  1297     def __init__(self, base, audit=True):
  1264         self.base = base
  1298         self.base = base
  1265         self.audit = audit
  1299         if audit:
       
  1300             self.audit_path = path_auditor(base)
       
  1301         else:
       
  1302             self.audit_path = always
  1266 
  1303 
  1267     def __getattr__(self, name):
  1304     def __getattr__(self, name):
  1268         if name == '_can_symlink':
  1305         if name == '_can_symlink':
  1269             self._can_symlink = checklink(self.base)
  1306             self._can_symlink = checklink(self.base)
  1270             return self._can_symlink
  1307             return self._can_symlink
  1271         raise AttributeError(name)
  1308         raise AttributeError(name)
  1272 
  1309 
  1273     def __call__(self, path, mode="r", text=False, atomictemp=False):
  1310     def __call__(self, path, mode="r", text=False, atomictemp=False):
  1274         if self.audit:
  1311         self.audit_path(path)
  1275             audit_path(path)
       
  1276         f = os.path.join(self.base, path)
  1312         f = os.path.join(self.base, path)
  1277 
  1313 
  1278         if not text and "b" not in mode:
  1314         if not text and "b" not in mode:
  1279             mode += "b" # for that other OS
  1315             mode += "b" # for that other OS
  1280 
  1316 
  1291             if nlink > 1:
  1327             if nlink > 1:
  1292                 rename(mktempcopy(f), f)
  1328                 rename(mktempcopy(f), f)
  1293         return posixfile(f, mode)
  1329         return posixfile(f, mode)
  1294 
  1330 
  1295     def symlink(self, src, dst):
  1331     def symlink(self, src, dst):
  1296         if self.audit:
  1332         self.audit_path(dst)
  1297             audit_path(dst)
       
  1298         linkname = os.path.join(self.base, dst)
  1333         linkname = os.path.join(self.base, dst)
  1299         try:
  1334         try:
  1300             os.unlink(linkname)
  1335             os.unlink(linkname)
  1301         except OSError:
  1336         except OSError:
  1302             pass
  1337             pass