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 if (closedir(odir[i]) == -1) 230 tst_brkm(TBROK, NULL, "Failed to close dir\n"); 231 } 232 233 char *cwd = tst_get_tmpdir(); 234 SAFE_CHDIR(NULL, cwd); 235 free(cwd); 236 237 for (i = 0; i < cgrp_opt_num; ++i) { 238 if (cgrp_opt[i].subdir) { 239 SAFE_CHDIR(NULL, cgrp_opt[i].dir); 240 if (rmdir(subdir_name) == -1) { 241 tst_brkm(TBROK | TERRNO, NULL, 242 "Can't remove dir"); 243 } 244 SAFE_CHDIR(NULL, ".."); 245 } 246 if (cgrp_opt[i].mounted) { 247 if (umount(cgrp_opt[i].dir) == -1) { 248 tst_brkm(TBROK | TERRNO, NULL, 249 "Can't unmount: %s", cgrp_opt[i].dir); 250 } 251 } 252 } 253 254 tst_rmdir(); 255 } 256 257 int mount_cgroup(void) 258 { 259 FILE *fd = fopen("/proc/cgroups", "r"); 260 if (fd == NULL) 261 tst_brkm(TBROK, cleanup, "Failed to read /proc/cgroups"); 262 char str[MAX_DIR_NAME], name[MAX_DIR_NAME]; 263 int hier = 0, num = 0, enabled = 0, first = 1; 264 /* make mount options */ 265 while ((fgets(str, MAX_DIR_NAME, fd)) != NULL) { 266 /* skip first line */ 267 if (first) { 268 first = 0; 269 continue; 270 } 271 if (sscanf(str, "%s\t%d\t%d\t%d", 272 name, &hier, &num, &enabled) != 4) 273 tst_brkm(TBROK, cleanup, "Can't parse /proc/cgroups"); 274 if (!enabled) 275 continue; 276 277 /* BUG WORKAROUND 278 * Only mount those subsystems, which are not mounted yet. 279 * It's a workaround to a bug when mount doesn't return any err 280 * code while mounting already mounted subsystems, but with 281 * additional "xattr" option. In that case, mount will succeed, 282 * but xattr won't be supported in the new mount anyway. 283 * Should be removed as soon as a fix committed to upstream. 284 * 285 * But not applicable for kernels >= 3.15 where xattr supported 286 * natively. 287 */ 288 if (hier != 0 && tst_kvercmp(3, 15, 0) < 0) 289 continue; 290 291 int i, found = 0; 292 for (i = 0; i < cgrp_opt_num; ++i) { 293 if (cgrp_opt[i].hier == hier) { 294 found = 1; 295 break; 296 } 297 } 298 if (!found) { 299 i = cgrp_opt_num++; 300 cgrp_opt[i].hier = hier; 301 } 302 char *str = cgrp_opt[i].str; 303 if (str[0] == '\0') 304 strcpy(str, "xattr"); 305 snprintf(str + strlen(str), MAX_OPTIONS_LEN - strlen(str), 306 ",%s", name); 307 } 308 fclose(fd); 309 310 int i, any_mounted = 0; 311 for (i = 0; i < cgrp_opt_num; ++i) { 312 char dir[MAX_DIR_NAME]; 313 struct cgrp_option *opt = &cgrp_opt[i]; 314 tst_resm(TINFO, "mount options %d: %s (hier = %d)", 315 i, opt->str, opt->hier); 316 snprintf(opt->dir, MAX_DIR_NAME, "cgx_%d", opt->hier); 317 SAFE_MKDIR(cleanup, opt->dir, 0755); 318 319 if (mount(opt->dir, opt->dir, "cgroup", 0, opt->str) == -1) { 320 tst_resm(TINFO, "Can't mount: %s", dir); 321 continue; 322 } 323 324 any_mounted = 1; 325 opt->mounted = 1; 326 327 /* create new hierarchy */ 328 SAFE_CHDIR(cleanup, opt->dir); 329 SAFE_MKDIR(cleanup, subdir_name, 0755); 330 opt->subdir = 1; 331 SAFE_CHDIR(cleanup, ".."); 332 } 333 return any_mounted; 334 } 335 336 static int set_xattrs(const char *file) 337 { 338 unsigned int i, err, fail, res = 0; 339 340 for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { 341 err = setxattr(file, tkeys[i].name, 342 (const void *)val.buf, val.size, 0) == -1; 343 344 fail = err && tkeys[i].good; 345 res |= fail; 346 347 tst_resm((fail) ? TFAIL : TPASS, 348 "Expect: %s set xattr key '%s' to file '%s'", 349 (tkeys[i].good) ? "can" : "can't", 350 tkeys[i].name, file); 351 352 if (verbose && tkeys[i].good) 353 tst_resm_hexd(TINFO, val.buf, val.size, "value:"); 354 } 355 return res; 356 } 357 358 static int get_xattrs(const char *file) 359 { 360 unsigned int i, fail, res = 0; 361 362 for (i = 0; i < ARRAY_SIZE(tkeys); ++i) { 363 /* get value size */ 364 ssize_t size = getxattr(file, tkeys[i].name, NULL, 0); 365 fail = (size == -1 && tkeys[i].good); 366 res |= fail; 367 368 tst_resm((fail) ? TFAIL : TPASS, 369 "Expect: %s read xattr %s of file '%s'", 370 (tkeys[i].good) ? "can" : "can't", 371 tkeys[i].name, file); 372 373 if (fail || size == -1) 374 continue; 375 376 /* get xattr value */ 377 char xval[size]; 378 if (getxattr(file, tkeys[i].name, xval, size) == -1) { 379 tst_brkm(TBROK, cleanup, 380 "Can't get buffer of key %s", 381 tkeys[i].name); 382 } 383 fail = val.size != (size_t)size || 384 strncmp(val.buf, xval, val.size) != 0; 385 res |= fail; 386 387 tst_resm((fail) ? TFAIL : TPASS, "Expect: values equal"); 388 389 if (verbose && fail) { 390 tst_resm_hexd(TINFO, xval, size, 391 "Read xattr value:"); 392 tst_resm_hexd(TINFO, val.buf, val.size, 393 "Expect xattr value:"); 394 } 395 } 396 return res; 397 } 398 399 static int cgrp_files_walking(const char *path, 400 int (*xattr_operation)(const char *)) 401 { 402 int res = 0; 403 struct dirent *entry; 404 DIR *dir = opendir(path); 405 406 odir[odir_num] = dir; 407 if (++odir_num >= MAX_OPEN_DIR) { 408 tst_brkm(TBROK, cleanup, 409 "Unexpected num of open dirs, max: %d", MAX_OPEN_DIR); 410 } 411 412 SAFE_CHDIR(cleanup, path); 413 414 tst_resm(TINFO, "In dir %s", path); 415 416 errno = 0; 417 while ((entry = readdir(dir)) != NULL) { 418 const char *file = entry->d_name; 419 /* skip current and up directories */ 420 if (!strcmp(file, "..") || !strcmp(file, ".")) 421 continue; 422 struct stat stat_buf; 423 TEST(lstat(file, &stat_buf)); 424 if (TEST_RETURN != -1 && S_ISDIR(stat_buf.st_mode)) { 425 /* proceed to subdir */ 426 res |= cgrp_files_walking(file, xattr_operation); 427 tst_resm(TINFO, "In dir %s", path); 428 } 429 memset(val.buf, id++, val.size); 430 res |= xattr_operation(file); 431 errno = 0; 432 } 433 if (errno && !entry) { 434 tst_brkm(TWARN | TERRNO, cleanup, 435 "Error while reading dir '%s'", path); 436 } 437 if (closedir(dir) == -1) 438 tst_brkm(TWARN, cleanup, "Failed to close dir '%s'", path); 439 else 440 odir[--odir_num] = NULL; 441 442 if (strcmp(path, ".")) 443 SAFE_CHDIR(cleanup, ".."); 444 return res; 445 } 446