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 <[email protected]>
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 for (i = 0; i < ARRAY_SIZE(tkeys); ++i) {
154 if (!strcmp(tkeys[i].name, "security.")) {
155 tkeys[i].good = tst_kvercmp(3, 15, 0) < 0;
156 } else if (!strcmp(tkeys[i].name, "trusted.")) {
157 tkeys[i].good = tst_kvercmp(4, 5, 0) < 0;
158 }
159 }
160
161 int value_size = DEFAULT_VALUE_SIZE;
162 if (nflag) {
163 if (sscanf(narg, "%i", &value_size) != 1)
164 tst_brkm(TBROK, NULL, "-n option arg is not a number");
165 if (value_size <= 0)
166 tst_brkm(TBROK, NULL, "-n option arg is less than 1");
167 }
168
169 /* initialize test value */
170 val.size = value_size;
171 val.buf = SAFE_MALLOC(NULL, value_size);
172
173 tst_sig(FORK, DEF_HANDLER, cleanup);
174
175 tst_tmpdir();
176
177 if (!mount_cgroup())
178 tst_brkm(TCONF, cleanup, "Nothing mounted");
179 }
180
test_run(void)181 static void test_run(void)
182 {
183 int i, set_res = 0, get_res = 0;
184
185 for (i = 0; i < cgrp_opt_num; ++i) {
186 if (!cgrp_opt[i].mounted)
187 continue;
188
189 SAFE_CHDIR(cleanup, cgrp_opt[i].dir);
190 /* reset value */
191 id = 0;
192 /* set xattr to each file in cgroup fs */
193 set_res |= cgrp_files_walking(".", set_xattrs);
194
195 id = 0;
196 /* get & check xattr */
197 get_res |= cgrp_files_walking(".", get_xattrs);
198 SAFE_CHDIR(cleanup, "..");
199 }
200
201 /* final results */
202 tst_resm(TINFO, "All test-cases have been completed, summary:");
203 tst_resm(TINFO, "Set tests result: %s", (set_res) ? "FAIL" : "PASS");
204 tst_resm(TINFO, "Get tests result: %s", (get_res) ? "FAIL" : "PASS");
205 }
206
cleanup(void)207 static void cleanup(void)
208 {
209 if (val.buf != NULL)
210 free(val.buf);
211
212 if (skip_cleanup)
213 return;
214
215 /*
216 * Kernels 3.7 can crash while unmounting cgroups with xattr,
217 * call tst_old_flush() to make sure all buffered data written
218 * before it happens
219 */
220 tst_old_flush();
221
222 int i;
223 for (i = 0; i < odir_num; ++i) {
224 SAFE_CLOSEDIR(NULL, odir[i]);
225 }
226
227 char *cwd = tst_get_tmpdir();
228 SAFE_CHDIR(NULL, cwd);
229 free(cwd);
230
231 for (i = 0; i < cgrp_opt_num; ++i) {
232 if (cgrp_opt[i].subdir) {
233 SAFE_CHDIR(NULL, cgrp_opt[i].dir);
234 SAFE_RMDIR(NULL, subdir_name);
235 SAFE_CHDIR(NULL, "..");
236 }
237 if (cgrp_opt[i].mounted) {
238 SAFE_UMOUNT(NULL, cgrp_opt[i].dir);
239 }
240 }
241
242 tst_rmdir();
243 }
244
mount_cgroup(void)245 int mount_cgroup(void)
246 {
247 FILE *fd = fopen("/proc/cgroups", "r");
248 if (fd == NULL)
249 tst_brkm(TBROK, cleanup, "Failed to read /proc/cgroups");
250 char str[MAX_DIR_NAME], name[MAX_DIR_NAME];
251 int hier = 0, num = 0, enabled = 0, first = 1;
252 /* make mount options */
253 while ((fgets(str, MAX_DIR_NAME, fd)) != NULL) {
254 /* skip first line */
255 if (first) {
256 first = 0;
257 continue;
258 }
259 if (sscanf(str, "%s\t%d\t%d\t%d",
260 name, &hier, &num, &enabled) != 4)
261 tst_brkm(TBROK, cleanup, "Can't parse /proc/cgroups");
262 if (!enabled)
263 continue;
264
265 /* BUG WORKAROUND
266 * Only mount those subsystems, which are not mounted yet.
267 * It's a workaround to a bug when mount doesn't return any err
268 * code while mounting already mounted subsystems, but with
269 * additional "xattr" option. In that case, mount will succeed,
270 * but xattr won't be supported in the new mount anyway.
271 * Should be removed as soon as a fix committed to upstream.
272 *
273 * But not applicable for kernels >= 3.15 where xattr supported
274 * natively.
275 */
276 if (hier != 0 && tst_kvercmp(3, 15, 0) < 0)
277 continue;
278
279 int i, found = 0;
280 for (i = 0; i < cgrp_opt_num; ++i) {
281 if (cgrp_opt[i].hier == hier) {
282 found = 1;
283 break;
284 }
285 }
286 if (!found) {
287 i = cgrp_opt_num++;
288 cgrp_opt[i].hier = hier;
289 }
290 char *str = cgrp_opt[i].str;
291 if (str[0] == '\0')
292 strcpy(str, "xattr");
293 snprintf(str + strlen(str), MAX_OPTIONS_LEN - strlen(str),
294 ",%s", name);
295 }
296 fclose(fd);
297
298 int i, any_mounted = 0;
299 for (i = 0; i < cgrp_opt_num; ++i) {
300 char dir[MAX_DIR_NAME];
301 struct cgrp_option *opt = &cgrp_opt[i];
302 tst_resm(TINFO, "mount options %d: %s (hier = %d)",
303 i, opt->str, opt->hier);
304 snprintf(opt->dir, MAX_DIR_NAME, "cgx_%d", opt->hier);
305 SAFE_MKDIR(cleanup, opt->dir, 0755);
306
307 if (mount(opt->dir, opt->dir, "cgroup", 0, opt->str) == -1) {
308 tst_resm(TINFO, "Can't mount: %s", dir);
309 continue;
310 }
311
312 any_mounted = 1;
313 opt->mounted = 1;
314
315 /* create new hierarchy */
316 SAFE_CHDIR(cleanup, opt->dir);
317 SAFE_MKDIR(cleanup, subdir_name, 0755);
318 opt->subdir = 1;
319 SAFE_CHDIR(cleanup, "..");
320 }
321 return any_mounted;
322 }
323
set_xattrs(const char * file)324 static int set_xattrs(const char *file)
325 {
326 unsigned int i, err, fail, res = 0;
327
328 for (i = 0; i < ARRAY_SIZE(tkeys); ++i) {
329 err = setxattr(file, tkeys[i].name,
330 (const void *)val.buf, val.size, 0) == -1;
331
332 fail = err && tkeys[i].good;
333 res |= fail;
334
335 tst_resm((fail) ? TFAIL : TPASS,
336 "Expect: %s set xattr key '%s' to file '%s'",
337 (tkeys[i].good) ? "can" : "can't",
338 tkeys[i].name, file);
339
340 if (verbose && tkeys[i].good)
341 tst_resm_hexd(TINFO, val.buf, val.size, "value:");
342 }
343 return res;
344 }
345
get_xattrs(const char * file)346 static int get_xattrs(const char *file)
347 {
348 unsigned int i, fail, res = 0;
349
350 for (i = 0; i < ARRAY_SIZE(tkeys); ++i) {
351 /* get value size */
352 ssize_t size = getxattr(file, tkeys[i].name, NULL, 0);
353 fail = (size == -1 && tkeys[i].good);
354 res |= fail;
355
356 tst_resm((fail) ? TFAIL : TPASS,
357 "Expect: %s read xattr %s of file '%s'",
358 (tkeys[i].good) ? "can" : "can't",
359 tkeys[i].name, file);
360
361 if (fail || size == -1)
362 continue;
363
364 /* get xattr value */
365 char xval[size];
366 if (getxattr(file, tkeys[i].name, xval, size) == -1) {
367 tst_brkm(TBROK, cleanup,
368 "Can't get buffer of key %s",
369 tkeys[i].name);
370 }
371 fail = val.size != (size_t)size ||
372 strncmp(val.buf, xval, val.size) != 0;
373 res |= fail;
374
375 tst_resm((fail) ? TFAIL : TPASS, "Expect: values equal");
376
377 if (verbose && fail) {
378 tst_resm_hexd(TINFO, xval, size,
379 "Read xattr value:");
380 tst_resm_hexd(TINFO, val.buf, val.size,
381 "Expect xattr value:");
382 }
383 }
384 return res;
385 }
386
cgrp_files_walking(const char * path,int (* xattr_operation)(const char *))387 static int cgrp_files_walking(const char *path,
388 int (*xattr_operation)(const char *))
389 {
390 int res = 0;
391 struct dirent *entry;
392 DIR *dir = opendir(path);
393
394 odir[odir_num] = dir;
395 if (++odir_num >= MAX_OPEN_DIR) {
396 tst_brkm(TBROK, cleanup,
397 "Unexpected num of open dirs, max: %d", MAX_OPEN_DIR);
398 }
399
400 SAFE_CHDIR(cleanup, path);
401
402 tst_resm(TINFO, "In dir %s", path);
403
404 errno = 0;
405 while ((entry = readdir(dir)) != NULL) {
406 const char *file = entry->d_name;
407 /* skip current and up directories */
408 if (!strcmp(file, "..") || !strcmp(file, "."))
409 continue;
410 struct stat stat_buf;
411 TEST(lstat(file, &stat_buf));
412 if (TEST_RETURN != -1 && S_ISDIR(stat_buf.st_mode)) {
413 /* proceed to subdir */
414 res |= cgrp_files_walking(file, xattr_operation);
415 tst_resm(TINFO, "In dir %s", path);
416 }
417 memset(val.buf, id++, val.size);
418 res |= xattr_operation(file);
419 errno = 0;
420 }
421 if (errno && !entry) {
422 tst_brkm(TWARN | TERRNO, cleanup,
423 "Error while reading dir '%s'", path);
424 }
425 if (closedir(dir) == -1)
426 tst_brkm(TWARN, cleanup, "Failed to close dir '%s'", path);
427 else
428 odir[--odir_num] = NULL;
429
430 if (strcmp(path, "."))
431 SAFE_CHDIR(cleanup, "..");
432 return res;
433 }
434