@@ -536,29 +536,6 @@ def cleanup(self, delete: bool = False):
536536 """
537537 pass
538538
539- def archive (self ) -> str | None :
540- """Archive the directory where the sandbox operated.
541-
542- Stores the archived sandbox in the file cacher and returns its digest.
543- Returns None if archiving failed.
544-
545- """
546- logger .info ("Archiving sandbox in %s." , self .get_root_path ())
547-
548- with tempfile .TemporaryFile (dir = self .temp_dir ) as sandbox_archive :
549- # Archive the working directory
550- content_path = self .get_root_path ()
551- try :
552- with tarfile .open (fileobj = sandbox_archive , mode = 'w:gz' ) as tar_file :
553- tar_file .add (content_path , os .path .basename (content_path ))
554- except Exception :
555- logger .warning ("Failed to archive sandbox" , exc_info = True )
556- return None
557-
558- # Put archive to FS
559- sandbox_archive .seek (0 )
560- return self .file_cacher .put_file_from_fobj (sandbox_archive , "Sandbox %s" % self .get_root_path ())
561-
562539
563540class StupidSandbox (SandboxBase ):
564541 """A stupid sandbox implementation. It has very few features and
@@ -891,11 +868,6 @@ class IsolateSandbox(SandboxBase):
891868 """
892869 next_id = 0
893870
894- # If the command line starts with this command name, we are just
895- # going to execute it without sandboxing, and with all permissions
896- # on the current directory.
897- SECURE_COMMANDS = ["/bin/cp" , "/bin/mv" , "/usr/bin/zip" , "/usr/bin/unzip" ]
898-
899871 def __init__ (self , file_cacher , name = None , temp_dir = None ):
900872 """Initialization.
901873
@@ -918,22 +890,25 @@ def __init__(self, file_cacher, name=None, temp_dir=None):
918890 box_id = IsolateSandbox .next_id % 10
919891 IsolateSandbox .next_id += 1
920892
921- # We create a directory "home" inside the outer temporary directory,
922- # that will be bind-mounted to "/tmp" inside the sandbox (some
923- # compilers need "/tmp" to exist, and this is a cheap shortcut to
924- # create it). The sandbox also runs code as a different user, and so
925- # we need to ensure that they can read and write to the directory.
926- # But we don't want everybody on the system to, which is why the
927- # outer directory exists with no read permissions.
928- self ._outer_dir = tempfile .mkdtemp (dir = self .temp_dir ,
929- prefix = "cms-%s-" % (self .name ))
930- self ._home = os .path .join (self ._outer_dir , "home" )
931- self ._home_dest = "/tmp"
932- os .mkdir (self ._home )
933- self .allow_writing_all ()
934-
935893 self .exec_name = 'isolate'
936894 self .box_exec = self .detect_box_executable ()
895+ self .box_id = box_id
896+
897+ # Tell isolate to get the sandbox ready. We do our best to cleanup
898+ # after ourselves, but we might have missed something if a previous
899+ # worker was interrupted in the middle of an execution, so we issue an
900+ # idempotent cleanup.
901+ self .cleanup ()
902+
903+ isolate_box_dir = self .initialize_isolate ()
904+
905+ # self._outer_dir = isolate_box_dir
906+ self ._outer_dir = tempfile .mkdtemp (
907+ dir = self .temp_dir , prefix = f"cms-{ self .name } -"
908+ )
909+ self ._home = os .path .join (isolate_box_dir , "box" )
910+ self ._home_dest = "/box"
911+
937912 # Used for -M - the meta file ends up in the outer directory. The
938913 # actual filename will be <info_basename>.<execution_number>.
939914 self .info_basename = os .path .join (self ._outer_dir , "run.log" )
@@ -946,7 +921,6 @@ def __init__(self, file_cacher, name=None, temp_dir=None):
946921 self ._home , self .box_exec )
947922
948923 # Default parameters for isolate
949- self .box_id = box_id # -b
950924 self .chdir = self ._home_dest # -c
951925 self .dirs = [] # -d
952926 self .preserve_env = False # -e
@@ -963,9 +937,6 @@ def __init__(self, file_cacher, name=None, temp_dir=None):
963937 self .wallclock_timeout : float | None = None # -w
964938 self .extra_timeout : float | None = None # -x
965939
966- self .add_mapped_directory (
967- self ._home , dest = self ._home_dest , options = "rw" )
968-
969940 # Create temporary directory on /dev/shm to prevent communication
970941 # between sandboxes.
971942 self .dirs .append ((None , "/dev/shm" , "tmp" ))
@@ -984,13 +955,6 @@ def __init__(self, file_cacher, name=None, temp_dir=None):
984955 # particular, the System.Native assembly.
985956 self .maybe_add_mapped_directory ("/etc/mono" , options = "noexec" )
986957
987- # Tell isolate to get the sandbox ready. We do our best to cleanup
988- # after ourselves, but we might have missed something if a previous
989- # worker was interrupted in the middle of an execution, so we issue an
990- # idempotent cleanup.
991- self .cleanup ()
992- self .initialize_isolate ()
993-
994958 def add_mapped_directory (
995959 self ,
996960 src : str ,
@@ -1021,61 +985,6 @@ def maybe_add_mapped_directory(
1021985 return self .add_mapped_directory (src , dest , options ,
1022986 ignore_if_not_existing = True )
1023987
1024- def allow_writing_all (self ):
1025- """Set permissions in such a way that any operation is allowed.
1026-
1027- """
1028- os .chmod (self ._home , 0o777 )
1029- for filename in os .listdir (self ._home ):
1030- os .chmod (os .path .join (self ._home , filename ), 0o777 )
1031-
1032- def allow_writing_none (self ):
1033- """Set permissions in such a way that the user cannot write anything.
1034-
1035- """
1036- os .chmod (self ._home , 0o755 )
1037- for filename in os .listdir (self ._home ):
1038- os .chmod (os .path .join (self ._home , filename ), 0o755 )
1039-
1040- def allow_writing_only (self , inner_paths : list [str ]):
1041- """Set permissions in so that the user can write only some paths.
1042-
1043- By default the user can only write to the home directory. This
1044- method further restricts permissions so that it can only write
1045- to some files inside the home directory.
1046-
1047- inner_paths: the only paths that the user is allowed to
1048- write to; they should be "inner" paths (from the perspective
1049- of the sandboxed process, not of the host system); they can
1050- be absolute or relative (in which case they are interpreted
1051- relative to the home directory); paths that point to a file
1052- outside the home directory are ignored.
1053-
1054- """
1055- outer_paths = []
1056- for inner_path in inner_paths :
1057- abs_inner_path = \
1058- os .path .realpath (os .path .join (self ._home_dest , inner_path ))
1059- # If an inner path is absolute (e.g., /fifo0/u0_to_m) then
1060- # it may be outside home and we should ignore it.
1061- if os .path .commonpath ([abs_inner_path ,
1062- self ._home_dest ]) != self ._home_dest :
1063- continue
1064- rel_inner_path = os .path .relpath (abs_inner_path , self ._home_dest )
1065- outer_path = os .path .join (self ._home , rel_inner_path )
1066- outer_paths .append (outer_path )
1067-
1068- # If one of the specified file do not exists, we touch it to
1069- # assign the correct permissions.
1070- for path in outer_paths :
1071- if not os .path .exists (path ):
1072- open (path , "wb" ).close ()
1073-
1074- # Close everything, then open only the specified.
1075- self .allow_writing_none ()
1076- for path in outer_paths :
1077- os .chmod (path , 0o722 )
1078-
1079988 def get_root_path (self ) -> str :
1080989 """Return the toplevel path of the sandbox.
1081990
@@ -1103,8 +1012,11 @@ def detect_box_executable(self) -> str:
11031012 return: the path to a valid (hopefully) isolate.
11041013
11051014 """
1015+ # TODO do we need this logic??
1016+ # i don't think isolate is usable without installing anyways, you need the config file.
11061017 paths = [os .path .join ('.' , 'isolate' , self .exec_name ),
11071018 os .path .join ('.' , self .exec_name )]
1019+ # in any case this location isn't useful, since we don't have isolate as a submodule anymore.
11081020 if '__file__' in globals ():
11091021 paths += [os .path .abspath (os .path .join (
11101022 os .path .dirname (__file__ ),
@@ -1358,44 +1270,13 @@ def _popen(
13581270 self .log = None
13591271 self .exec_num += 1
13601272
1361- # We run a selection of commands without isolate, as they need
1362- # to create new files. This is safe because these commands do
1363- # not depend on the user input.
1364- if command [0 ] in IsolateSandbox .SECURE_COMMANDS :
1365- logger .debug ("Executing non-securely: %s at %s" ,
1366- pretty_print_cmdline (command ), self ._home )
1367- try :
1368- prev_permissions = stat .S_IMODE (os .stat (self ._home ).st_mode )
1369- os .chmod (self ._home , 0o700 )
1370- with open (self .cmd_file , 'at' , encoding = "utf-8" ) as cmds :
1371- cmds .write ("%s\n " % (pretty_print_cmdline (command )))
1372- p = subprocess .Popen (command , cwd = self ._home ,
1373- stdin = stdin , stdout = stdout , stderr = stderr ,
1374- close_fds = close_fds )
1375- os .chmod (self ._home , prev_permissions )
1376- # For secure commands, we clear the output so that it
1377- # is not forwarded to the contestants. Secure commands
1378- # are "setup" commands, which should not fail or
1379- # provide information for the contestants.
1380- open (os .path .join (self ._home , self .stdout_file ), "wb" ).close ()
1381- open (os .path .join (self ._home , self .stderr_file ), "wb" ).close ()
1382- self ._write_empty_run_log (self .exec_num )
1383- except OSError :
1384- logger .critical (
1385- "Failed to execute program in sandbox with command: %s" ,
1386- pretty_print_cmdline (command ), exc_info = True )
1387- raise
1388- return p
1389-
13901273 args = [self .box_exec ] + self .build_box_options () + ["--" ] + command
13911274 logger .debug ("Executing program in sandbox with command: `%s'." ,
13921275 pretty_print_cmdline (args ))
1393- # Temporarily allow writing new files.
1394- prev_permissions = stat .S_IMODE (os .stat (self ._home ).st_mode )
1395- os .chmod (self ._home , 0o770 )
1396- with open (self .cmd_file , 'at' , encoding = "utf-8" ) as commands :
1397- commands .write ("%s\n " % (pretty_print_cmdline (args )))
1398- os .chmod (self ._home , prev_permissions )
1276+
1277+ with open (self .cmd_file , "a" ) as commands :
1278+ commands .write (pretty_print_cmdline (args ) + "\n " )
1279+
13991280 try :
14001281 p = subprocess .Popen (args ,
14011282 stdin = stdin , stdout = stdout , stderr = stderr ,
@@ -1408,15 +1289,6 @@ def _popen(
14081289
14091290 return p
14101291
1411- def _write_empty_run_log (self , index : int ):
1412- """Write a fake run.log file with no information."""
1413- info_file = "%s.%d" % (self .info_basename , index )
1414- with open (info_file , "wt" , encoding = "utf-8" ) as f :
1415- f .write ("time:0.000\n " )
1416- f .write ("time-wall:0.000\n " )
1417- f .write ("max-rss:0\n " )
1418- f .write ("cg-mem:0\n " )
1419-
14201292 def execute_without_std (
14211293 self , command : list [str ], wait : bool = False
14221294 ) -> bool | subprocess .Popen :
@@ -1469,35 +1341,20 @@ def translate_box_exitcode(self, exitcode: int) -> bool:
14691341 raise SandboxInterfaceException ("Sandbox exit status (%d) unknown"
14701342 % exitcode )
14711343
1472- def initialize_isolate (self ):
1344+ def initialize_isolate (self ) -> str :
14731345 """Initialize isolate's box."""
14741346 init_cmd = [self .box_exec , "--box-id=%d" % self .box_id , "--cg" , "--init" ]
14751347 try :
1476- subprocess .check_call (init_cmd )
1348+ return subprocess .run (init_cmd , check = True ,
1349+ capture_output = True , encoding = "utf-8" ).stdout .strip ()
14771350 except subprocess .CalledProcessError as e :
14781351 raise SandboxInterfaceException (
14791352 "Failed to initialize sandbox" ) from e
14801353
1481- def cleanup (self , delete = False ):
1354+ def cleanup (self , delete : bool = False ):
14821355 """See Sandbox.cleanup()."""
1483- # The user isolate assigns within the sandbox might have created
1484- # subdirectories and files therein, making the user outside the sandbox
1485- # unable to delete the whole tree. If the caller asked us to delete the
1486- # sandbox, we first issue a chmod within isolate to make sure that we
1487- # will be able to delete everything. If not, we leave the files as they
1488- # are to avoid masking possible problems the admin wanted to debug.
1489-
14901356 exe = [self .box_exec , "--box-id=%d" % self .box_id , "--cg" ]
14911357
1492- if delete :
1493- # Ignore exit status as some files may be owned by our user
1494- subprocess .call (
1495- exe + [
1496- "--dir=%s=%s:rw" % (self ._home_dest , self ._home ),
1497- "--run" , "--" ,
1498- "/bin/chmod" , "777" , "-R" , self ._home_dest ],
1499- stdout = subprocess .DEVNULL , stderr = subprocess .STDOUT )
1500-
15011358 # Tell isolate to cleanup the sandbox.
15021359 subprocess .check_call (
15031360 exe + ["--cleanup" ],
@@ -1508,6 +1365,37 @@ def cleanup(self, delete=False):
15081365 # Delete the working directory.
15091366 rmtree (self ._outer_dir )
15101367
1368+ def archive (self ) -> str | None :
1369+ """Archive the directory where the sandbox operated.
1370+
1371+ Stores the archived sandbox in the file cacher and returns its digest.
1372+ Returns None if archiving failed.
1373+
1374+ """
1375+ logger .info ("Archiving sandbox in %s." , self .get_root_path ())
1376+
1377+ with tempfile .TemporaryFile (dir = self .temp_dir ) as sandbox_archive :
1378+ # Archive the working directory
1379+ metadata_path = self .get_root_path ()
1380+ box_dir = self ._home
1381+ with tarfile .open (fileobj = sandbox_archive , mode = 'w:gz' ) as tar_file :
1382+ try :
1383+ # Add metadata files
1384+ for x in os .listdir (metadata_path ):
1385+ tar_file .add (os .path .join (metadata_path , x ), x )
1386+ # Add the box directory
1387+ tar_file .add (box_dir , "box" )
1388+ except Exception :
1389+ logger .warning (
1390+ "Failed to add files to sandbox archive" , exc_info = True
1391+ )
1392+
1393+ # Put archive to FS
1394+ sandbox_archive .seek (0 )
1395+ return self .file_cacher .put_file_from_fobj (
1396+ sandbox_archive , f"Sandbox { self .get_root_path ()} "
1397+ )
1398+
15111399
15121400# StupidSandbox hasn't been tested in a while. It should probably be removed,
15131401# but for now, for typing purposes let's assume we're always using
0 commit comments