]> mj.ucw.cz Git - eval.git/blobdiff - isolate/isolate.c
MO-P: CEOI contestants
[eval.git] / isolate / isolate.c
index 460f1b06ec4b52b44bd520bdfdb1952d2a5315db..cb33c603ab5e1ca0cd1bd91781b412679c983684 100644 (file)
@@ -1,8 +1,8 @@
 /*
  *     A Process Isolator based on Linux Containers
  *
- *     (c) 2012-2013 Martin Mares <mj@ucw.cz>
- *     (c) 2012-2013 Bernard Blackham <bernard@blackham.com.au>
+ *     (c) 2012-2015 Martin Mares <mj@ucw.cz>
+ *     (c) 2012-2014 Bernard Blackham <bernard@blackham.com.au>
  */
 
 #define _GNU_SOURCE
@@ -20,6 +20,7 @@
 #include <getopt.h>
 #include <sched.h>
 #include <time.h>
+#include <ftw.h>
 #include <grp.h>
 #include <mntent.h>
 #include <limits.h>
@@ -31,6 +32,7 @@
 #include <sys/stat.h>
 #include <sys/quota.h>
 #include <sys/vfs.h>
+#include <sys/fsuid.h>
 
 #define NONRET __attribute__((noreturn))
 #define UNUSED __attribute__((unused))
@@ -63,7 +65,7 @@ static uid_t orig_uid;
 static gid_t orig_gid;
 
 static int partial_line;
-static char cleanup_cmd[256];
+static int cleanup_ownership;
 
 static struct timeval start_time;
 static int ticks_per_sec;
@@ -79,6 +81,8 @@ static void cg_stats(void);
 static int get_wall_time_ms(void);
 static int get_run_time_ms(struct rusage *rus);
 
+static void chowntree(char *path, uid_t uid, gid_t gid);
+
 /*** Meta-files ***/
 
 static FILE *metafile;
@@ -91,7 +95,11 @@ meta_open(const char *name)
       metafile = stdout;
       return;
     }
+  if (setfsuid(getuid()) < 0)
+    die("Failed to switch FS UID: %m");
   metafile = fopen(name, "w");
+  if (setfsuid(geteuid()) < 0)
+    die("Failed to switch FS UID back: %m");
   if (!metafile)
     die("Failed to open metafile '%s'",name);
 }
@@ -132,16 +140,6 @@ final_stats(struct rusage *rus)
 
 /*** Messages and exits ***/
 
-static void
-xsystem(const char *cmd)
-{
-  int ret = system(cmd);
-  if (ret < 0)
-    die("system(\"%s\"): %m", cmd);
-  if (!WIFEXITED(ret) || WEXITSTATUS(ret))
-    die("system(\"%s\"): Exited with status %d", cmd, ret);
-}
-
 static void NONRET
 box_exit(int rc)
 {
@@ -162,8 +160,8 @@ box_exit(int rc)
        final_stats(&rus);
     }
 
-  if (rc < 2 && cleanup_cmd[0])
-    xsystem(cleanup_cmd);
+  if (rc < 2 && cleanup_ownership)
+    chowntree("box", orig_uid, orig_gid);
 
   meta_close();
   exit(rc);
@@ -265,6 +263,48 @@ static int dir_exists(char *path)
   return (stat(path, &st) >= 0 && S_ISDIR(st.st_mode));
 }
 
+static int rmtree_helper(const char *fpath, const struct stat *sb,
+    int typeflag UNUSED, struct FTW *ftwbuf UNUSED)
+{
+  if (S_ISDIR(sb->st_mode))
+    {
+      if (rmdir(fpath) < 0)
+       die("Cannot rmdir %s: %m", fpath);
+    }
+  else
+    {
+      if (unlink(fpath) < 0)
+       die("Cannot unlink %s: %m", fpath);
+    }
+  return FTW_CONTINUE;
+}
+
+static void
+rmtree(char *path)
+{
+  nftw(path, rmtree_helper, 32, FTW_MOUNT | FTW_PHYS | FTW_DEPTH);
+}
+
+static uid_t chown_uid;
+static gid_t chown_gid;
+
+static int chowntree_helper(const char *fpath, const struct stat *sb UNUSED,
+    int typeflag UNUSED, struct FTW *ftwbuf UNUSED)
+{
+  if (lchown(fpath, chown_uid, chown_gid) < 0)
+    die("Cannot chown %s: %m", fpath);
+  else
+    return FTW_CONTINUE;
+}
+
+static void
+chowntree(char *path, uid_t uid, gid_t gid)
+{
+  chown_uid = uid;
+  chown_gid = gid;
+  nftw(path, chowntree_helper, 32, FTW_MOUNT | FTW_PHYS);
+}
+
 /*** Environment rules ***/
 
 struct env_rule {
@@ -601,14 +641,14 @@ static const struct cg_controller_desc cg_controllers[CG_NUM_CONTROLLERS+1] = {
 
 #define FOREACH_CG_CONTROLLER(_controller) \
   for (cg_controller (_controller) = 0; \
-      (_controller) < CG_NUM_CONTROLLERS; (_controller)++)
+       (_controller) < CG_NUM_CONTROLLERS; (_controller)++)
 
 static const char *cg_controller_name(cg_controller c)
 {
   return cg_controllers[c].name;
 }
 
-static const int cg_controller_optional(cg_controller c)
+static int cg_controller_optional(cg_controller c)
 {
   return cg_controllers[c].optional;
 }
@@ -647,7 +687,11 @@ cg_read(cg_controller controller, const char *attr, char *buf)
 
   int n = read(fd, buf, CG_BUFSIZE);
   if (n < 0)
-    die("Cannot read %s: %m", path);
+    {
+      if (maybe)
+       return 0;
+      die("Cannot read %s: %m", path);
+    }
   if (n >= CG_BUFSIZE - 1)
     die("Attribute %s too long", path);
   if (n > 0 && buf[n-1] == '\n')
@@ -773,7 +817,7 @@ cg_enter(void)
   if (cg_memory_limit)
     {
       cg_write(CG_MEMORY, "memory.limit_in_bytes", "%lld\n", (long long) cg_memory_limit << 10);
-      cg_write(CG_MEMORY, "memory.memsw.limit_in_bytes", "%lld\n", (long long) cg_memory_limit << 10);
+      cg_write(CG_MEMORY, "?memory.memsw.limit_in_bytes", "%lld\n", (long long) cg_memory_limit << 10);
     }
 
   if (cg_timing)
@@ -824,10 +868,12 @@ cg_remove(void)
 
   FOREACH_CG_CONTROLLER(controller)
     {
-      if (cg_controller_optional(controller)) {
-       if (!cg_read(controller, "?tasks", buf))
-         continue;
-      } else
+      if (cg_controller_optional(controller))
+       {
+         if (!cg_read(controller, "?tasks", buf))
+           continue;
+       }
+      else
        cg_read(controller, "tasks", buf);
 
       if (buf[0])
@@ -1185,7 +1231,7 @@ setup_rlimits(void)
 #define RLIM(res, val) setup_rlim("RLIMIT_" #res, RLIMIT_##res, val)
 
   if (memory_limit)
-    RLIM(AS, memory_limit * 1024);
+    RLIM(AS, (rlim_t)memory_limit * 1024);
 
   RLIM(STACK, (stack_limit ? (rlim_t)stack_limit * 1024 : RLIM_INFINITY));
   RLIM(NOFILE, 64);
@@ -1203,6 +1249,7 @@ box_inside(void *arg)
   char **args = arg;
   write_errors_to_fd = error_pipes[1];
   close(error_pipes[0]);
+  meta_close();
 
   cg_enter();
   setup_root();
@@ -1238,7 +1285,7 @@ static void
 init(void)
 {
   msg("Preparing sandbox directory\n");
-  xsystem("rm -rf box");
+  rmtree("box");
   if (mkdir("box", 0700) < 0)
     die("Cannot create box: %m");
   if (chown("box", orig_uid, orig_gid) < 0)
@@ -1257,9 +1304,7 @@ cleanup(void)
     die("Box directory not found, there isn't anything to clean up");
 
   msg("Deleting sandbox directory\n");
-  xsystem("rm -rf *");
-  if (rmdir(box_dir) < 0)
-    die("Cannot remove %s: %m", box_dir);
+  rmtree(box_dir);
   cg_remove();
 }
 
@@ -1269,10 +1314,8 @@ run(char **argv)
   if (!dir_exists("box"))
     die("Box directory not found, did you run `isolate --init'?");
 
-  char cmd[256];
-  snprintf(cmd, sizeof(cmd), "chown -R %d.%d box", box_uid, box_gid);
-  xsystem(cmd);
-  snprintf(cleanup_cmd, sizeof(cleanup_cmd), "chown -R %d.%d box", orig_uid, orig_gid);
+  chowntree("box", box_uid, box_gid);
+  cleanup_ownership = 1;
 
   if (pipe(error_pipes) < 0)
     die("pipe: %m");
@@ -1481,7 +1524,7 @@ main(int argc, char **argv)
       case OPT_RUN:
       case OPT_CLEANUP:
       case OPT_VERSION:
-       if (!mode || mode == c)
+       if (!mode || (int) mode == c)
          mode = c;
        else
          usage("Only one command is allowed.\n");