• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 
main(int argc,char * argv[])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 
help(void)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 
setup(int argc,char * argv[])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 
test_run(void)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 
cleanup(void)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 		if (closedir(odir[i]) == -1)
230 			tst_brkm(TBROK, NULL, "Failed to close dir\n");
231 	}
232 
233 	char *cwd = tst_get_tmpdir();
234 	SAFE_CHDIR(NULL, cwd);
235 	free(cwd);
236 
237 	for (i = 0; i < cgrp_opt_num; ++i) {
238 		if (cgrp_opt[i].subdir) {
239 			SAFE_CHDIR(NULL, cgrp_opt[i].dir);
240 			if (rmdir(subdir_name) == -1) {
241 				tst_brkm(TBROK | TERRNO, NULL,
242 					"Can't remove dir");
243 			}
244 			SAFE_CHDIR(NULL, "..");
245 		}
246 		if (cgrp_opt[i].mounted) {
247 			if (umount(cgrp_opt[i].dir) == -1) {
248 				tst_brkm(TBROK | TERRNO, NULL,
249 					"Can't unmount: %s", cgrp_opt[i].dir);
250 			}
251 		}
252 	}
253 
254 	tst_rmdir();
255 }
256 
mount_cgroup(void)257 int mount_cgroup(void)
258 {
259 	FILE *fd = fopen("/proc/cgroups", "r");
260 	if (fd == NULL)
261 		tst_brkm(TBROK, cleanup, "Failed to read /proc/cgroups");
262 	char str[MAX_DIR_NAME], name[MAX_DIR_NAME];
263 	int hier = 0, num = 0, enabled = 0, first = 1;
264 	/* make mount options */
265 	while ((fgets(str, MAX_DIR_NAME, fd)) != NULL) {
266 		/* skip first line */
267 		if (first) {
268 			first = 0;
269 			continue;
270 		}
271 		if (sscanf(str, "%s\t%d\t%d\t%d",
272 			name, &hier, &num, &enabled) != 4)
273 			tst_brkm(TBROK, cleanup, "Can't parse /proc/cgroups");
274 		if (!enabled)
275 			continue;
276 
277 		/* BUG WORKAROUND
278 		 * Only mount those subsystems, which are not mounted yet.
279 		 * It's a workaround to a bug when mount doesn't return any err
280 		 * code while mounting already mounted subsystems, but with
281 		 * additional "xattr" option. In that case, mount will succeed,
282 		 * but xattr won't be supported in the new mount anyway.
283 		 * Should be removed as soon as a fix committed to upstream.
284 		 *
285 		 * But not applicable for kernels >= 3.15 where xattr supported
286 		 * natively.
287 		 */
288 		if (hier != 0 && tst_kvercmp(3, 15, 0) < 0)
289 			continue;
290 
291 		int i, found = 0;
292 		for (i = 0; i < cgrp_opt_num; ++i) {
293 			if (cgrp_opt[i].hier == hier) {
294 				found = 1;
295 				break;
296 			}
297 		}
298 		if (!found) {
299 			i = cgrp_opt_num++;
300 			cgrp_opt[i].hier = hier;
301 		}
302 		char *str = cgrp_opt[i].str;
303 		if (str[0] == '\0')
304 			strcpy(str, "xattr");
305 		snprintf(str + strlen(str), MAX_OPTIONS_LEN - strlen(str),
306 			",%s", name);
307 	}
308 	fclose(fd);
309 
310 	int i, any_mounted = 0;
311 	for (i = 0; i < cgrp_opt_num; ++i) {
312 		char dir[MAX_DIR_NAME];
313 		struct cgrp_option *opt = &cgrp_opt[i];
314 		tst_resm(TINFO, "mount options %d: %s (hier = %d)",
315 			i, opt->str, opt->hier);
316 		snprintf(opt->dir, MAX_DIR_NAME, "cgx_%d", opt->hier);
317 		SAFE_MKDIR(cleanup, opt->dir, 0755);
318 
319 		if (mount(opt->dir, opt->dir, "cgroup", 0, opt->str) == -1) {
320 			tst_resm(TINFO, "Can't mount: %s", dir);
321 			continue;
322 		}
323 
324 		any_mounted = 1;
325 		opt->mounted = 1;
326 
327 		/* create new hierarchy */
328 		SAFE_CHDIR(cleanup, opt->dir);
329 		SAFE_MKDIR(cleanup, subdir_name, 0755);
330 		opt->subdir = 1;
331 		SAFE_CHDIR(cleanup, "..");
332 	}
333 	return any_mounted;
334 }
335 
set_xattrs(const char * file)336 static int set_xattrs(const char *file)
337 {
338 	unsigned int i, err, fail, res = 0;
339 
340 	for (i = 0; i < ARRAY_SIZE(tkeys); ++i) {
341 		err = setxattr(file, tkeys[i].name,
342 			(const void *)val.buf, val.size, 0) == -1;
343 
344 		fail = err && tkeys[i].good;
345 		res |= fail;
346 
347 		tst_resm((fail) ? TFAIL : TPASS,
348 			"Expect: %s set xattr key '%s' to file '%s'",
349 			(tkeys[i].good) ? "can" : "can't",
350 			tkeys[i].name, file);
351 
352 		if (verbose && tkeys[i].good)
353 			tst_resm_hexd(TINFO, val.buf, val.size, "value:");
354 	}
355 	return res;
356 }
357 
get_xattrs(const char * file)358 static int get_xattrs(const char *file)
359 {
360 	unsigned int i, fail, res = 0;
361 
362 	for (i = 0; i < ARRAY_SIZE(tkeys); ++i) {
363 		/* get value size */
364 		ssize_t size = getxattr(file, tkeys[i].name, NULL, 0);
365 		fail = (size == -1 && tkeys[i].good);
366 		res |= fail;
367 
368 		tst_resm((fail) ? TFAIL : TPASS,
369 			"Expect: %s read xattr %s of file '%s'",
370 			(tkeys[i].good) ? "can" : "can't",
371 			tkeys[i].name, file);
372 
373 		if (fail || size == -1)
374 			continue;
375 
376 		/* get xattr value */
377 		char xval[size];
378 		if (getxattr(file, tkeys[i].name, xval, size) == -1) {
379 			tst_brkm(TBROK, cleanup,
380 				"Can't get buffer of key %s",
381 				tkeys[i].name);
382 		}
383 		fail = val.size != (size_t)size ||
384 			strncmp(val.buf, xval, val.size) != 0;
385 		res |= fail;
386 
387 		tst_resm((fail) ? TFAIL : TPASS, "Expect: values equal");
388 
389 		if (verbose && fail) {
390 			tst_resm_hexd(TINFO, xval, size,
391 				"Read  xattr  value:");
392 			tst_resm_hexd(TINFO, val.buf, val.size,
393 				"Expect xattr value:");
394 		}
395 	}
396 	return res;
397 }
398 
cgrp_files_walking(const char * path,int (* xattr_operation)(const char *))399 static int cgrp_files_walking(const char *path,
400 	int (*xattr_operation)(const char *))
401 {
402 	int res = 0;
403 	struct dirent *entry;
404 	DIR *dir = opendir(path);
405 
406 	odir[odir_num] = dir;
407 	if (++odir_num >= MAX_OPEN_DIR) {
408 		tst_brkm(TBROK, cleanup,
409 			"Unexpected num of open dirs, max: %d", MAX_OPEN_DIR);
410 	}
411 
412 	SAFE_CHDIR(cleanup, path);
413 
414 	tst_resm(TINFO, "In dir %s", path);
415 
416 	errno = 0;
417 	while ((entry = readdir(dir)) != NULL) {
418 		const char *file = entry->d_name;
419 		/* skip current and up directories */
420 		if (!strcmp(file, "..") || !strcmp(file, "."))
421 			continue;
422 		struct stat stat_buf;
423 		TEST(lstat(file, &stat_buf));
424 		if (TEST_RETURN != -1 && S_ISDIR(stat_buf.st_mode)) {
425 			/* proceed to subdir */
426 			res |= cgrp_files_walking(file, xattr_operation);
427 			tst_resm(TINFO, "In dir %s", path);
428 		}
429 		memset(val.buf, id++, val.size);
430 		res |= xattr_operation(file);
431 		errno = 0;
432 	}
433 	if (errno && !entry) {
434 		tst_brkm(TWARN | TERRNO, cleanup,
435 			"Error while reading dir '%s'", path);
436 	}
437 	if (closedir(dir) == -1)
438 		tst_brkm(TWARN, cleanup, "Failed to close dir '%s'", path);
439 	else
440 		odir[--odir_num] = NULL;
441 
442 	if (strcmp(path, "."))
443 		SAFE_CHDIR(cleanup, "..");
444 	return res;
445 }
446