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