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