mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
	
	
		
			204 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			204 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
 |