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