mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	
		
			
	
	
		
			206 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			206 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								import os, sys, __builtin__, tempfile
							 | 
						||
| 
								 | 
							
								_os = sys.modules[os.name]
							 | 
						||
| 
								 | 
							
								_open = open
							 | 
						||
| 
								 | 
							
								from distutils.errors import DistutilsError
							 | 
						||
| 
								 | 
							
								__all__ = [
							 | 
						||
| 
								 | 
							
								    "AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup",
							 | 
						||
| 
								 | 
							
								]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def run_setup(setup_script, args):
							 | 
						||
| 
								 | 
							
								    """Run a distutils setup script, sandboxed in its directory"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    old_dir = os.getcwd()
							 | 
						||
| 
								 | 
							
								    save_argv = sys.argv[:]
							 | 
						||
| 
								 | 
							
								    save_path = sys.path[:]
							 | 
						||
| 
								 | 
							
								    setup_dir = os.path.abspath(os.path.dirname(setup_script))
							 | 
						||
| 
								 | 
							
								    temp_dir = os.path.join(setup_dir,'temp')
							 | 
						||
| 
								 | 
							
								    if not os.path.isdir(temp_dir): os.makedirs(temp_dir)
							 | 
						||
| 
								 | 
							
								    save_tmp = tempfile.tempdir
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        tempfile.tempdir = temp_dir
							 | 
						||
| 
								 | 
							
								        os.chdir(setup_dir)
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            sys.argv[:] = [setup_script]+list(args)
							 | 
						||
| 
								 | 
							
								            sys.path.insert(0, setup_dir)
							 | 
						||
| 
								 | 
							
								            DirectorySandbox(setup_dir).run(
							 | 
						||
| 
								 | 
							
								                lambda: execfile(
							 | 
						||
| 
								 | 
							
								                    "setup.py",
							 | 
						||
| 
								 | 
							
								                    {'__file__':setup_script, '__name__':'__main__'}
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        except SystemExit, v:
							 | 
						||
| 
								 | 
							
								            if v.args and v.args[0]:
							 | 
						||
| 
								 | 
							
								                raise
							 | 
						||
| 
								 | 
							
								            # Normal exit, just return
							 | 
						||
| 
								 | 
							
								    finally:
							 | 
						||
| 
								 | 
							
								        os.chdir(old_dir)
							 | 
						||
| 
								 | 
							
								        sys.path[:] = save_path
							 | 
						||
| 
								 | 
							
								        sys.argv[:] = save_argv
							 | 
						||
| 
								 | 
							
								        tempfile.tempdir = save_tmp
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class AbstractSandbox:
							 | 
						||
| 
								 | 
							
								    """Wrap 'os' module and 'open()' builtin for virtualizing setup scripts"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _active = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self):
							 | 
						||
| 
								 | 
							
								        self._attrs = [
							 | 
						||
| 
								 | 
							
								            name for name in dir(_os)
							 | 
						||
| 
								 | 
							
								                if not name.startswith('_') and hasattr(self,name)
							 | 
						||
| 
								 | 
							
								        ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _copy(self, source):
							 | 
						||
| 
								 | 
							
								        for name in self._attrs:
							 | 
						||
| 
								 | 
							
								            setattr(os, name, getattr(source,name))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def run(self, func):
							 | 
						||
| 
								 | 
							
								        """Run 'func' under os sandboxing"""
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            self._copy(self)
							 | 
						||
| 
								 | 
							
								            __builtin__.open = __builtin__.file = self._open
							 | 
						||
| 
								 | 
							
								            self._active = True
							 | 
						||
| 
								 | 
							
								            return func()
							 | 
						||
| 
								 | 
							
								        finally:
							 | 
						||
| 
								 | 
							
								            self._active = False
							 | 
						||
| 
								 | 
							
								            __builtin__.open = __builtin__.file = _open
							 | 
						||
| 
								 | 
							
								            self._copy(_os)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _mk_dual_path_wrapper(name):
							 | 
						||
| 
								 | 
							
								        original = getattr(_os,name)
							 | 
						||
| 
								 | 
							
								        def wrap(self,src,dst,*args,**kw):
							 | 
						||
| 
								 | 
							
								            if self._active:
							 | 
						||
| 
								 | 
							
								                src,dst = self._remap_pair(name,src,dst,*args,**kw)
							 | 
						||
| 
								 | 
							
								            return original(src,dst,*args,**kw)
							 | 
						||
| 
								 | 
							
								        return wrap
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for name in ["rename", "link", "symlink"]:
							 | 
						||
| 
								 | 
							
								        if hasattr(_os,name): locals()[name] = _mk_dual_path_wrapper(name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _mk_single_path_wrapper(name, original=None):
							 | 
						||
| 
								 | 
							
								        original = original or getattr(_os,name)
							 | 
						||
| 
								 | 
							
								        def wrap(self,path,*args,**kw):
							 | 
						||
| 
								 | 
							
								            if self._active:
							 | 
						||
| 
								 | 
							
								                path = self._remap_input(name,path,*args,**kw)
							 | 
						||
| 
								 | 
							
								            return original(path,*args,**kw)
							 | 
						||
| 
								 | 
							
								        return wrap
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _open = _mk_single_path_wrapper('file', _open)
							 | 
						||
| 
								 | 
							
								    for name in [
							 | 
						||
| 
								 | 
							
								        "stat", "listdir", "chdir", "open", "chmod", "chown", "mkdir",
							 | 
						||
| 
								 | 
							
								        "remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat",
							 | 
						||
| 
								 | 
							
								        "startfile", "mkfifo", "mknod", "pathconf", "access"
							 | 
						||
| 
								 | 
							
								    ]:
							 | 
						||
| 
								 | 
							
								        if hasattr(_os,name): locals()[name] = _mk_single_path_wrapper(name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _mk_single_with_return(name):
							 | 
						||
| 
								 | 
							
								        original = getattr(_os,name)
							 | 
						||
| 
								 | 
							
								        def wrap(self,path,*args,**kw):
							 | 
						||
| 
								 | 
							
								            if self._active:
							 | 
						||
| 
								 | 
							
								                path = self._remap_input(name,path,*args,**kw)
							 | 
						||
| 
								 | 
							
								                return self._remap_output(name, original(path,*args,**kw))
							 | 
						||
| 
								 | 
							
								            return original(path,*args,**kw)
							 | 
						||
| 
								 | 
							
								        return wrap
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for name in ['readlink', 'tempnam']:
							 | 
						||
| 
								 | 
							
								        if hasattr(_os,name): locals()[name] = _mk_single_with_return(name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _mk_query(name):
							 | 
						||
| 
								 | 
							
								        original = getattr(_os,name)
							 | 
						||
| 
								 | 
							
								        def wrap(self,*args,**kw):
							 | 
						||
| 
								 | 
							
								            retval = original(*args,**kw)
							 | 
						||
| 
								 | 
							
								            if self._active:
							 | 
						||
| 
								 | 
							
								                return self._remap_output(name, retval)
							 | 
						||
| 
								 | 
							
								            return retval
							 | 
						||
| 
								 | 
							
								        return wrap
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for name in ['getcwd', 'tmpnam']:
							 | 
						||
| 
								 | 
							
								        if hasattr(_os,name): locals()[name] = _mk_query(name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _validate_path(self,path):
							 | 
						||
| 
								 | 
							
								        """Called to remap or validate any path, whether input or output"""
							 | 
						||
| 
								 | 
							
								        return path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _remap_input(self,operation,path,*args,**kw):
							 | 
						||
| 
								 | 
							
								        """Called for path inputs"""
							 | 
						||
| 
								 | 
							
								        return self._validate_path(path)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _remap_output(self,operation,path):
							 | 
						||
| 
								 | 
							
								        """Called for path outputs"""
							 | 
						||
| 
								 | 
							
								        return self._validate_path(path)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _remap_pair(self,operation,src,dst,*args,**kw):
							 | 
						||
| 
								 | 
							
								        """Called for path pairs like rename, link, and symlink operations"""
							 | 
						||
| 
								 | 
							
								        return (
							 | 
						||
| 
								 | 
							
								            self._remap_input(operation+'-from',src,*args,**kw),
							 | 
						||
| 
								 | 
							
								            self._remap_input(operation+'-to',dst,*args,**kw)
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class DirectorySandbox(AbstractSandbox):
							 | 
						||
| 
								 | 
							
								    """Restrict operations to a single subdirectory - pseudo-chroot"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    write_ops = dict.fromkeys([
							 | 
						||
| 
								 | 
							
								        "open", "chmod", "chown", "mkdir", "remove", "unlink", "rmdir",
							 | 
						||
| 
								 | 
							
								        "utime", "lchown", "chroot", "mkfifo", "mknod", "tempnam",
							 | 
						||
| 
								 | 
							
								    ])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self,sandbox):
							 | 
						||
| 
								 | 
							
								        self._sandbox = os.path.normcase(os.path.realpath(sandbox))
							 | 
						||
| 
								 | 
							
								        self._prefix = os.path.join(self._sandbox,'')
							 | 
						||
| 
								 | 
							
								        AbstractSandbox.__init__(self)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _violation(self, operation, *args, **kw):
							 | 
						||
| 
								 | 
							
								        raise SandboxViolation(operation, args, kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _open(self, path, mode='r', *args, **kw):
							 | 
						||
| 
								 | 
							
								        if mode not in ('r', 'rt', 'rb', 'rU') and not self._ok(path):
							 | 
						||
| 
								 | 
							
								            self._violation("open", path, mode, *args, **kw)
							 | 
						||
| 
								 | 
							
								        return _open(path,mode,*args,**kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def tmpnam(self):
							 | 
						||
| 
								 | 
							
								        self._violation("tmpnam")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _ok(self,path):
							 | 
						||
| 
								 | 
							
								        active = self._active
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            self._active = False
							 | 
						||
| 
								 | 
							
								            realpath = os.path.normcase(os.path.realpath(path))
							 | 
						||
| 
								 | 
							
								            if realpath==self._sandbox or realpath.startswith(self._prefix):
							 | 
						||
| 
								 | 
							
								                return True
							 | 
						||
| 
								 | 
							
								        finally:
							 | 
						||
| 
								 | 
							
								            self._active = active
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _remap_input(self,operation,path,*args,**kw):
							 | 
						||
| 
								 | 
							
								        """Called for path inputs"""
							 | 
						||
| 
								 | 
							
								        if operation in self.write_ops and not self._ok(path):
							 | 
						||
| 
								 | 
							
								            self._violation(operation, os.path.realpath(path), *args, **kw)
							 | 
						||
| 
								 | 
							
								        return path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _remap_pair(self,operation,src,dst,*args,**kw):
							 | 
						||
| 
								 | 
							
								        """Called for path pairs like rename, link, and symlink operations"""
							 | 
						||
| 
								 | 
							
								        if not self._ok(src) or not self._ok(dst):
							 | 
						||
| 
								 | 
							
								            self._violation(operation, src, dst, *args, **kw)
							 | 
						||
| 
								 | 
							
								        return (src,dst)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class SandboxViolation(DistutilsError):
							 | 
						||
| 
								 | 
							
								    """A setup script attempted to modify the filesystem outside the sandbox"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __str__(self):
							 | 
						||
| 
								 | 
							
								        return """SandboxViolation: %s%r %s
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The package setup script has attempted to modify files on your system
							 | 
						||
| 
								 | 
							
								that are not within the EasyInstall build area, and has been aborted.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								This package cannot be safely installed by EasyInstall, and may not
							 | 
						||
| 
								 | 
							
								support alternate installation locations even if you run its setup
							 | 
						||
| 
								 | 
							
								script by hand.  Please inform the package's author and the EasyInstall
							 | 
						||
| 
								 | 
							
								maintainers to find out if a fix or workaround is available.""" % self.args
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 |