From a5ee18acd75108983b5e92d6f356dab4dc3ec3d4 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Sun, 22 Apr 2012 23:10:47 +0200 Subject: [PATCH] Isolate: Added support for setting disk quotas --- TODO | 1 - isolate/isolate.1.txt | 4 ++ isolate/isolate.c | 99 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 102 insertions(+), 2 deletions(-) diff --git a/TODO b/TODO index f6dd5ae..10c4f0d 100644 --- a/TODO +++ b/TODO @@ -43,5 +43,4 @@ Test: ping-pong timing attacks Test: big static memory Examine the use of taskstats for measuring memory Doc: mount -t cgroup none -o cpuset,cpuacct,memory /sys/fs/cgroup -Set up quotas Switch license to GPL2/GPL3 diff --git a/isolate/isolate.1.txt b/isolate/isolate.1.txt index 2af23f3..0e7952a 100644 --- a/isolate/isolate.1.txt +++ b/isolate/isolate.1.txt @@ -71,6 +71,10 @@ OPTIONS Limit process stack to 'size' kilobytes. By default, the whole address space is available for the stack, but it is subject to the *--mem* limit. +*-q, --quota=*'blocks'*,*'inodes':: + Set disk quota to a given number of blocks and inodes. This requires the + filesystem to be mounted with support for quotas. + *-i, --stdin=*'file':: Redirect standard input from 'file'. The 'file' has to be accessible inside the sandbox. diff --git a/isolate/isolate.c b/isolate/isolate.c index 33c5088..e2acf44 100644 --- a/isolate/isolate.c +++ b/isolate/isolate.c @@ -21,12 +21,16 @@ #include #include #include +#include +#include #include #include #include #include #include #include +#include +#include #define NONRET __attribute__((noreturn)) #define UNUSED __attribute__((unused)) @@ -39,6 +43,8 @@ static int pass_environ; static int verbose; static int memory_limit; static int stack_limit; +static int block_quota; +static int inode_quota; static int max_processes = 1; static char *redir_stdin, *redir_stdout, *redir_stderr; @@ -759,6 +765,86 @@ cg_remove(void) die("Cannot remove control group %s: %m", cg_path); } +/*** Disk quotas ***/ + +static int +path_begins_with(char *path, char *with) +{ + while (*with) + if (*path++ != *with++) + return 0; + return (!*with || *with == '/'); +} + +static char * +find_device(char *path) +{ + FILE *f = setmntent("/proc/mounts", "r"); + if (!f) + die("Cannot open /proc/mounts: %m"); + + struct mntent *me; + int best_len = 0; + char *best_dev = NULL; + while (me = getmntent(f)) + { + if (!path_begins_with(me->mnt_fsname, "/dev")) + continue; + if (path_begins_with(path, me->mnt_dir)) + { + int len = strlen(me->mnt_dir); + if (len > best_len) + { + best_len = len; + free(best_dev); + best_dev = xstrdup(me->mnt_fsname); + } + } + } + endmntent(f); + return best_dev; +} + +static void +set_quota(void) +{ + if (!block_quota) + return; + + char cwd[PATH_MAX]; + if (!getcwd(cwd, sizeof(cwd))) + die("getcwd: %m"); + + char *dev = find_device(cwd); + if (!dev) + die("Cannot identify filesystem which contains %s", cwd); + msg("Quota: Mapped path %s to a filesystem on %s\n", cwd, dev); + + // Sanity check + struct stat dev_st, cwd_st; + if (stat(dev, &dev_st) < 0) + die("Cannot identify block device %s: %m", dev); + if (!S_ISBLK(dev_st.st_mode)) + die("Expected that %s is a block device", dev); + if (stat(".", &cwd_st) < 0) + die("Cannot stat cwd: %m"); + if (cwd_st.st_dev != dev_st.st_rdev) + die("Identified %s as a filesystem on %s, but it is obviously false", cwd, dev); + + struct dqblk dq = { + .dqb_bhardlimit = block_quota, + .dqb_bsoftlimit = block_quota, + .dqb_ihardlimit = inode_quota, + .dqb_isoftlimit = inode_quota, + .dqb_valid = QIF_LIMITS, + }; + if (quotactl(QCMD(Q_SETQUOTA, USRQUOTA), dev, box_uid, (caddr_t) &dq) < 0) + die("Cannot set disk quota: %m"); + msg("Quota: Set block quota %d and inode quota %d\n", block_quota, inode_quota); + + free(dev); +} + /*** The keeper process ***/ static void @@ -1079,6 +1165,7 @@ init(void) die("Cannot chown box: %m"); cg_prepare(); + set_quota(); puts(box_dir); } @@ -1170,6 +1257,7 @@ Options:\n\ -e, --full-env\t\tInherit full environment of the parent process\n\ -m, --mem=\tLimit address space to KB\n\ -M, --meta=\tOutput process information to (name:value)\n\ +-q, --quota=,\tSet disk quota to blocks and inodes\n\ -k, --stack=\tLimit stack size to KB (default: 0=unlimited)\n\ -r, --stderr=\tRedirect stderr to \n\ -i, --stdin=\tRedirect stdin from \n\ @@ -1197,7 +1285,7 @@ enum opt_code { OPT_CG_TIMING, }; -static const char short_opts[] = "c:d:eE:i:k:m:M:o:p::r:t:vw:x:"; +static const char short_opts[] = "c:d:eE:i:k:m:M:o:p::q:r:t:vw:x:"; static const struct option long_opts[] = { { "box-id", 1, NULL, 'b' }, @@ -1213,6 +1301,7 @@ static const struct option long_opts[] = { { "mem", 1, NULL, 'm' }, { "meta", 1, NULL, 'M' }, { "processes", 2, NULL, 'p' }, + { "quota", 1, NULL, 'q' }, { "run", 0, NULL, OPT_RUN }, { "stack", 1, NULL, 'k' }, { "stderr", 1, NULL, 'r' }, @@ -1229,6 +1318,7 @@ int main(int argc, char **argv) { int c; + char *sep; enum opt_code mode = 0; init_dir_rules(); @@ -1274,6 +1364,13 @@ main(int argc, char **argv) else max_processes = 0; break; + case 'q': + sep = strchr(optarg, ','); + if (!sep) + usage(); + block_quota = atoi(optarg); + inode_quota = atoi(sep+1); + break; case 'r': redir_stderr = optarg; break; -- 2.39.2