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