Home | History | Annotate | Download | only in cgroup_xattr
      1 /*
      2  * Copyright (c) 2013-2015 Oracle and/or its affiliates. All Rights Reserved.
      3  *
      4  * This program is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU General Public License as
      6  * published by the Free Software Foundation; either version 2 of
      7  * the License, or (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it would be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12  * GNU General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU General Public License
     15  * along with this program; if not, write the Free Software Foundation,
     16  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     17  *
     18  * Author:
     19  * Alexey Kodanev <alexey.kodanev (at) oracle.com>
     20  *
     21  * Test checks following preconditions:
     22  * since Linux kernel 3.7 it is possible to set extended attributes
     23  * to cgroup files.
     24  */
     25 
     26 #include <sys/stat.h>
     27 #include <sys/mount.h>
     28 #include <sys/types.h>
     29 #include <sys/xattr.h>
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <fcntl.h>
     33 #include <dirent.h>
     34 #include <unistd.h>
     35 #include <string.h>
     36 #include <errno.h>
     37 
     38 #include "test.h"
     39 #include "safe_macros.h"
     40 
     41 char *TCID = "cgroup_xattr";
     42 
     43 static const char subdir_name[]	= "test";
     44 
     45 #define MAX_SUBSYS		16
     46 #define MAX_OPTIONS_LEN		256
     47 #define MAX_DIR_NAME		64
     48 
     49 /* struct to store available mount options */
     50 struct cgrp_option {
     51 	char str[MAX_OPTIONS_LEN];
     52 	char dir[MAX_DIR_NAME];
     53 	int hier;
     54 	int mounted;
     55 	int subdir;
     56 };
     57 static struct cgrp_option cgrp_opt[MAX_SUBSYS];
     58 static int cgrp_opt_num;
     59 
     60 struct tst_key {
     61 	const char *name;
     62 	int good;
     63 };
     64 
     65 /* only security.* & trusted.* are valid key names */
     66 static struct tst_key tkeys[] = {
     67 	{ .name = "security.",		.good = 0,	},  /* see setup() */
     68 	{ .name = "trusted.test",	.good = 1,	},
     69 	{ .name = "trusted.",		.good = 0,	},  /* see setup() */
     70 	{ .name = "user.",		.good = 0,	},
     71 	{ .name = "system.",		.good = 0,	},
     72 };
     73 
     74 #define DEFAULT_VALUE_SIZE	8
     75 
     76 /* struct to store key's value */
     77 struct tst_val {
     78 	char *buf;
     79 	size_t size;
     80 };
     81 static struct tst_val val;
     82 
     83 /* it fills value's buffer */
     84 static char id;
     85 
     86 /*
     87  * When test breaks, all open dirs should be closed
     88  * otherwise umount won't succeed
     89  */
     90 #define MAX_OPEN_DIR		32
     91 static DIR *odir[MAX_OPEN_DIR];
     92 static int odir_num;
     93 
     94 /* test options */
     95 static char *narg;
     96 static int nflag;
     97 static int skip_cleanup;
     98 static int verbose;
     99 static const option_t options[] = {
    100 	{"n:", &nflag, &narg},
    101 	{"s", &skip_cleanup, NULL},
    102 	{"v", &verbose, NULL},
    103 	{NULL, NULL, NULL}
    104 };
    105 
    106 static void help(void);
    107 static void setup(int argc, char *argv[]);
    108 static void test_run(void);
    109 static void cleanup(void);
    110 
    111 static int mount_cgroup(void);
    112 static int set_xattrs(const char *file);
    113 static int get_xattrs(const char *file);
    114 /*
    115  * set or get xattr recursively
    116  *
    117  * @path: start directory
    118  * @xattr_operation: can be set_xattrs() or get_xattrs()
    119  */
    120 static int cgrp_files_walking(const char *path,
    121 	int (*xattr_operation)(const char *));
    122 
    123 int main(int argc, char *argv[])
    124 {
    125 	setup(argc, argv);
    126 
    127 	test_run();
    128 
    129 	cleanup();
    130 
    131 	tst_exit();
    132 }
    133 
    134 static void help(void)
    135 {
    136 	printf("  -n x    Write x bytes to xattr value, default is %d\n",
    137 		DEFAULT_VALUE_SIZE);
    138 	printf("  -s      Skip cleanup\n");
    139 	printf("  -v      Verbose\n");
    140 }
    141 
    142 void setup(int argc, char *argv[])
    143 {
    144 	unsigned int i;
    145 
    146 	tst_parse_opts(argc, argv, options, help);
    147 
    148 	tst_require_root();
    149 
    150 	if (access("/proc/cgroups", F_OK) == -1)
    151 		tst_brkm(TCONF, NULL, "Kernel doesn't support cgroups");
    152 
    153 	if (tst_kvercmp(3, 7, 0) < 0) {
    154 		tst_brkm(TCONF, NULL,
    155 			"Test must be run with kernel 3.7 or newer");
    156 	}
    157 
    158 	for (i = 0; i < ARRAY_SIZE(tkeys); ++i) {
    159 		if (!strcmp(tkeys[i].name, "security.")) {
    160 			tkeys[i].good = tst_kvercmp(3, 15, 0) < 0;
    161 		} else if (!strcmp(tkeys[i].name, "trusted.")) {
    162 			tkeys[i].good = tst_kvercmp(4, 5, 0) < 0;
    163 		}
    164 	}
    165 
    166 	int value_size = DEFAULT_VALUE_SIZE;
    167 	if (nflag) {
    168 		if (sscanf(narg, "%i", &value_size) != 1)
    169 			tst_brkm(TBROK, NULL, "-n option arg is not a number");
    170 		if (value_size <= 0)
    171 			tst_brkm(TBROK, NULL, "-n option arg is less than 1");
    172 	}
    173 
    174 	/* initialize test value */
    175 	val.size = value_size;
    176 	val.buf = SAFE_MALLOC(NULL, value_size);
    177 
    178 	tst_sig(FORK, DEF_HANDLER, cleanup);
    179 
    180 	tst_tmpdir();
    181 
    182 	if (!mount_cgroup())
    183 		tst_brkm(TCONF, cleanup, "Nothing  mounted");
    184 }
    185 
    186 static void test_run(void)
    187 {
    188 	int i, set_res = 0, get_res = 0;
    189 
    190 	for (i = 0; i < cgrp_opt_num; ++i) {
    191 		if (!cgrp_opt[i].mounted)
    192 			continue;
    193 
    194 		SAFE_CHDIR(cleanup, cgrp_opt[i].dir);
    195 		/* reset value */
    196 		id = 0;
    197 		/* set xattr to each file in cgroup fs */
    198 		set_res |= cgrp_files_walking(".", set_xattrs);
    199 
    200 		id = 0;
    201 		/* get & check xattr */
    202 		get_res |= cgrp_files_walking(".", get_xattrs);
    203 		SAFE_CHDIR(cleanup, "..");
    204 	}
    205 
    206 	/* final results */
    207 	tst_resm(TINFO, "All test-cases have been completed, summary:");
    208 	tst_resm(TINFO, "Set tests result: %s", (set_res) ? "FAIL" : "PASS");
    209 	tst_resm(TINFO, "Get tests result: %s", (get_res) ? "FAIL" : "PASS");
    210 }
    211 
    212 static void cleanup(void)
    213 {
    214 	if (val.buf != NULL)
    215 		free(val.buf);
    216 
    217 	if (skip_cleanup)
    218 		return;
    219 
    220 	/*
    221 	 * Kernels 3.7 can crash while unmounting cgroups with xattr,
    222 	 * call tst_flush() to make sure all buffered data written
    223 	 * before it happens
    224 	 */
    225 	tst_flush();
    226 
    227 	int i;
    228 	for (i = 0; i < odir_num; ++i) {
    229 		SAFE_CLOSEDIR(NULL, odir[i]);
    230 	}
    231 
    232 	char *cwd = tst_get_tmpdir();
    233 	SAFE_CHDIR(NULL, cwd);
    234 	free(cwd);
    235 
    236 	for (i = 0; i < cgrp_opt_num; ++i) {
    237 		if (cgrp_opt[i].subdir) {
    238 			SAFE_CHDIR(NULL, cgrp_opt[i].dir);
    239 			SAFE_RMDIR(NULL, subdir_name);
    240 			SAFE_CHDIR(NULL, "..");
    241 		}
    242 		if (cgrp_opt[i].mounted) {
    243 			SAFE_UMOUNT(NULL, cgrp_opt[i].dir);
    244 		}
    245 	}
    246 
    247 	tst_rmdir();
    248 }
    249 
    250 int mount_cgroup(void)
    251 {
    252 	FILE *fd = fopen("/proc/cgroups", "r");
    253 	if (fd == NULL)
    254 		tst_brkm(TBROK, cleanup, "Failed to read /proc/cgroups");
    255 	char str[MAX_DIR_NAME], name[MAX_DIR_NAME];
    256 	int hier = 0, num = 0, enabled = 0, first = 1;
    257 	/* make mount options */
    258 	while ((fgets(str, MAX_DIR_NAME, fd)) != NULL) {
    259 		/* skip first line */
    260 		if (first) {
    261 			first = 0;
    262 			continue;
    263 		}
    264 		if (sscanf(str, "%s\t%d\t%d\t%d",
    265 			name, &hier, &num, &enabled) != 4)
    266 			tst_brkm(TBROK, cleanup, "Can't parse /proc/cgroups");
    267 		if (!enabled)
    268 			continue;
    269 
    270 		/* BUG WORKAROUND
    271 		 * Only mount those subsystems, which are not mounted yet.
    272 		 * It's a workaround to a bug when mount doesn't return any err
    273 		 * code while mounting already mounted subsystems, but with
    274 		 * additional "xattr" option. In that case, mount will succeed,
    275 		 * but xattr won't be supported in the new mount anyway.
    276 		 * Should be removed as soon as a fix committed to upstream.
    277 		 *
    278 		 * But not applicable for kernels >= 3.15 where xattr supported
    279 		 * natively.
    280 		 */
    281 		if (hier != 0 && tst_kvercmp(3, 15, 0) < 0)
    282 			continue;
    283 
    284 		int i, found = 0;
    285 		for (i = 0; i < cgrp_opt_num; ++i) {
    286 			if (cgrp_opt[i].hier == hier) {
    287 				found = 1;
    288 				break;
    289 			}
    290 		}
    291 		if (!found) {
    292 			i = cgrp_opt_num++;
    293 			cgrp_opt[i].hier = hier;
    294 		}
    295 		char *str = cgrp_opt[i].str;
    296 		if (str[0] == '\0')
    297 			strcpy(str, "xattr");
    298 		snprintf(str + strlen(str), MAX_OPTIONS_LEN - strlen(str),
    299 			",%s", name);
    300 	}
    301 	fclose(fd);
    302 
    303 	int i, any_mounted = 0;
    304 	for (i = 0; i < cgrp_opt_num; ++i) {
    305 		char dir[MAX_DIR_NAME];
    306 		struct cgrp_option *opt = &cgrp_opt[i];
    307 		tst_resm(TINFO, "mount options %d: %s (hier = %d)",
    308 			i, opt->str, opt->hier);
    309 		snprintf(opt->dir, MAX_DIR_NAME, "cgx_%d", opt->hier);
    310 		SAFE_MKDIR(cleanup, opt->dir, 0755);
    311 
    312 		if (mount(opt->dir, opt->dir, "cgroup", 0, opt->str) == -1) {
    313 			tst_resm(TINFO, "Can't mount: %s", dir);
    314 			continue;
    315 		}
    316 
    317 		any_mounted = 1;
    318 		opt->mounted = 1;
    319 
    320 		/* create new hierarchy */
    321 		SAFE_CHDIR(cleanup, opt->dir);
    322 		SAFE_MKDIR(cleanup, subdir_name, 0755);
    323 		opt->subdir = 1;
    324 		SAFE_CHDIR(cleanup, "..");
    325 	}
    326 	return any_mounted;
    327 }
    328 
    329 static int set_xattrs(const char *file)
    330 {
    331 	unsigned int i, err, fail, res = 0;
    332 
    333 	for (i = 0; i < ARRAY_SIZE(tkeys); ++i) {
    334 		err = setxattr(file, tkeys[i].name,
    335 			(const void *)val.buf, val.size, 0) == -1;
    336 
    337 		fail = err && tkeys[i].good;
    338 		res |= fail;
    339 
    340 		tst_resm((fail) ? TFAIL : TPASS,
    341 			"Expect: %s set xattr key '%s' to file '%s'",
    342 			(tkeys[i].good) ? "can" : "can't",
    343 			tkeys[i].name, file);
    344 
    345 		if (verbose && tkeys[i].good)
    346 			tst_resm_hexd(TINFO, val.buf, val.size, "value:");
    347 	}
    348 	return res;
    349 }
    350 
    351 static int get_xattrs(const char *file)
    352 {
    353 	unsigned int i, fail, res = 0;
    354 
    355 	for (i = 0; i < ARRAY_SIZE(tkeys); ++i) {
    356 		/* get value size */
    357 		ssize_t size = getxattr(file, tkeys[i].name, NULL, 0);
    358 		fail = (size == -1 && tkeys[i].good);
    359 		res |= fail;
    360 
    361 		tst_resm((fail) ? TFAIL : TPASS,
    362 			"Expect: %s read xattr %s of file '%s'",
    363 			(tkeys[i].good) ? "can" : "can't",
    364 			tkeys[i].name, file);
    365 
    366 		if (fail || size == -1)
    367 			continue;
    368 
    369 		/* get xattr value */
    370 		char xval[size];
    371 		if (getxattr(file, tkeys[i].name, xval, size) == -1) {
    372 			tst_brkm(TBROK, cleanup,
    373 				"Can't get buffer of key %s",
    374 				tkeys[i].name);
    375 		}
    376 		fail = val.size != (size_t)size ||
    377 			strncmp(val.buf, xval, val.size) != 0;
    378 		res |= fail;
    379 
    380 		tst_resm((fail) ? TFAIL : TPASS, "Expect: values equal");
    381 
    382 		if (verbose && fail) {
    383 			tst_resm_hexd(TINFO, xval, size,
    384 				"Read  xattr  value:");
    385 			tst_resm_hexd(TINFO, val.buf, val.size,
    386 				"Expect xattr value:");
    387 		}
    388 	}
    389 	return res;
    390 }
    391 
    392 static int cgrp_files_walking(const char *path,
    393 	int (*xattr_operation)(const char *))
    394 {
    395 	int res = 0;
    396 	struct dirent *entry;
    397 	DIR *dir = opendir(path);
    398 
    399 	odir[odir_num] = dir;
    400 	if (++odir_num >= MAX_OPEN_DIR) {
    401 		tst_brkm(TBROK, cleanup,
    402 			"Unexpected num of open dirs, max: %d", MAX_OPEN_DIR);
    403 	}
    404 
    405 	SAFE_CHDIR(cleanup, path);
    406 
    407 	tst_resm(TINFO, "In dir %s", path);
    408 
    409 	errno = 0;
    410 	while ((entry = readdir(dir)) != NULL) {
    411 		const char *file = entry->d_name;
    412 		/* skip current and up directories */
    413 		if (!strcmp(file, "..") || !strcmp(file, "."))
    414 			continue;
    415 		struct stat stat_buf;
    416 		TEST(lstat(file, &stat_buf));
    417 		if (TEST_RETURN != -1 && S_ISDIR(stat_buf.st_mode)) {
    418 			/* proceed to subdir */
    419 			res |= cgrp_files_walking(file, xattr_operation);
    420 			tst_resm(TINFO, "In dir %s", path);
    421 		}
    422 		memset(val.buf, id++, val.size);
    423 		res |= xattr_operation(file);
    424 		errno = 0;
    425 	}
    426 	if (errno && !entry) {
    427 		tst_brkm(TWARN | TERRNO, cleanup,
    428 			"Error while reading dir '%s'", path);
    429 	}
    430 	if (closedir(dir) == -1)
    431 		tst_brkm(TWARN, cleanup, "Failed to close dir '%s'", path);
    432 	else
    433 		odir[--odir_num] = NULL;
    434 
    435 	if (strcmp(path, "."))
    436 		SAFE_CHDIR(cleanup, "..");
    437 	return res;
    438 }
    439