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