Path: blob/main/sys/contrib/openzfs/tests/zfs-tests/cmd/xattrtest.c
48529 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/2122/*23* Copyright 2016 Lawrence Livermore National Security, LLC.24*/2526/*27* An extended attribute (xattr) correctness test. This program creates28* N files and sets M attrs on them of size S. Optionally is will verify29* a pattern stored in the xattr.30*/31#include <stdlib.h>32#include <stddef.h>33#include <stdio.h>34#include <string.h>35#include <errno.h>36#include <getopt.h>37#include <fcntl.h>38#include <time.h>39#include <unistd.h>40#include <sys/xattr.h>41#include <sys/types.h>42#include <sys/wait.h>43#include <sys/stat.h>44#include <sys/time.h>45#include <linux/limits.h>4647#define ERROR(fmt, ...) \48fprintf(stderr, "xattrtest: %s:%d: %s: " fmt "\n", \49__FILE__, __LINE__, \50__func__, ## __VA_ARGS__);5152static const char shortopts[] = "hvycdn:f:x:s:p:t:e:rRko:";53static const struct option longopts[] = {54{ "help", no_argument, 0, 'h' },55{ "verbose", no_argument, 0, 'v' },56{ "verify", no_argument, 0, 'y' },57{ "nth", required_argument, 0, 'n' },58{ "files", required_argument, 0, 'f' },59{ "xattrs", required_argument, 0, 'x' },60{ "size", required_argument, 0, 's' },61{ "path", required_argument, 0, 'p' },62{ "synccaches", no_argument, 0, 'c' },63{ "dropcaches", no_argument, 0, 'd' },64{ "script", required_argument, 0, 't' },65{ "seed", required_argument, 0, 'e' },66{ "random", no_argument, 0, 'r' },67{ "randomvalue", no_argument, 0, 'R' },68{ "keep", no_argument, 0, 'k' },69{ "only", required_argument, 0, 'o' },70{ 0, 0, 0, 0 }71};7273enum phases {74PHASE_ALL = 0,75PHASE_CREATE,76PHASE_SETXATTR,77PHASE_GETXATTR,78PHASE_UNLINK,79PHASE_INVAL80};8182static int verbose = 0;83static int verify = 0;84static int synccaches = 0;85static int dropcaches = 0;86static int nth = 0;87static int files = 1000;88static int xattrs = 1;89static int size = 6;90static int size_is_random = 0;91static int value_is_random = 0;92static int keep_files = 0;93static int phase = PHASE_ALL;94static const char *path = "/tmp/xattrtest";95static const char *script = "/bin/true";96static char xattrbytes[XATTR_SIZE_MAX];9798static int99usage(char *argv0)100{101fprintf(stderr,102"usage: %s [-hvycdrRk] [-n <nth>] [-f <files>] [-x <xattrs>]\n"103" [-s <bytes>] [-p <path>] [-t <script> ] [-o <phase>]\n",104argv0);105106fprintf(stderr,107" --help -h This help\n"108" --verbose -v Increase verbosity\n"109" --verify -y Verify xattr contents\n"110" --nth -n <nth> Print every nth file\n"111" --files -f <files> Set xattrs on N files\n"112" --xattrs -x <xattrs> Set N xattrs on each file\n"113" --size -s <bytes> Set N bytes per xattr\n"114" --path -p <path> Path to files\n"115" --synccaches -c Sync caches between phases\n"116" --dropcaches -d Drop caches between phases\n"117" --script -t <script> Exec script between phases\n"118" --seed -e <seed> Random seed value\n"119" --random -r Randomly sized xattrs [16-size]\n"120" --randomvalue -R Random xattr values\n"121" --keep -k Don't unlink files\n"122" --only -o <num> Only run phase N\n"123" 0=all, 1=create, 2=setxattr,\n"124" 3=getxattr, 4=unlink\n\n");125126return (1);127}128129static int130parse_args(int argc, char **argv)131{132long seed = time(NULL);133int c;134int rc = 0;135136while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {137switch (c) {138case 'h':139return (usage(argv[0]));140case 'v':141verbose++;142break;143case 'y':144verify = 1;145break;146case 'n':147nth = strtol(optarg, NULL, 0);148break;149case 'f':150files = strtol(optarg, NULL, 0);151break;152case 'x':153xattrs = strtol(optarg, NULL, 0);154break;155case 's':156size = strtol(optarg, NULL, 0);157if (size > XATTR_SIZE_MAX) {158fprintf(stderr, "Error: the -s value may not "159"be greater than %d\n", XATTR_SIZE_MAX);160rc = 1;161}162break;163case 'p':164path = optarg;165break;166case 'c':167synccaches = 1;168break;169case 'd':170dropcaches = 1;171break;172case 't':173script = optarg;174break;175case 'e':176seed = strtol(optarg, NULL, 0);177break;178case 'r':179size_is_random = 1;180break;181case 'R':182value_is_random = 1;183break;184case 'k':185keep_files = 1;186break;187case 'o':188phase = strtol(optarg, NULL, 0);189if (phase <= PHASE_ALL || phase >= PHASE_INVAL) {190fprintf(stderr, "Error: the -o value must be "191"greater than %d and less than %d\n",192PHASE_ALL, PHASE_INVAL);193rc = 1;194}195break;196default:197rc = 1;198break;199}200}201202if (rc != 0)203return (rc);204205srandom(seed);206207if (verbose) {208fprintf(stdout, "verbose: %d\n", verbose);209fprintf(stdout, "verify: %d\n", verify);210fprintf(stdout, "nth: %d\n", nth);211fprintf(stdout, "files: %d\n", files);212fprintf(stdout, "xattrs: %d\n", xattrs);213fprintf(stdout, "size: %d\n", size);214fprintf(stdout, "path: %s\n", path);215fprintf(stdout, "synccaches: %d\n", synccaches);216fprintf(stdout, "dropcaches: %d\n", dropcaches);217fprintf(stdout, "script: %s\n", script);218fprintf(stdout, "seed: %ld\n", seed);219fprintf(stdout, "random size: %d\n", size_is_random);220fprintf(stdout, "random value: %d\n", value_is_random);221fprintf(stdout, "keep: %d\n", keep_files);222fprintf(stdout, "only: %d\n", phase);223fprintf(stdout, "%s", "\n");224}225226return (rc);227}228229static int230drop_caches(void)231{232char file[] = "/proc/sys/vm/drop_caches";233int fd, rc;234235fd = open(file, O_WRONLY);236if (fd == -1) {237ERROR("Error %d: open(\"%s\", O_WRONLY)\n", errno, file);238return (errno);239}240241rc = write(fd, "3", 1);242if ((rc == -1) || (rc != 1)) {243ERROR("Error %d: write(%d, \"3\", 1)\n", errno, fd);244(void) close(fd);245return (errno);246}247248rc = close(fd);249if (rc == -1) {250ERROR("Error %d: close(%d)\n", errno, fd);251return (errno);252}253254return (0);255}256257static int258run_process(const char *path, char *argv[])259{260pid_t pid;261int rc, devnull_fd;262263pid = fork();264if (pid == 0) {265devnull_fd = open("/dev/null", O_WRONLY);266267if (devnull_fd < 0)268_exit(-1);269270(void) dup2(devnull_fd, STDOUT_FILENO);271(void) dup2(devnull_fd, STDERR_FILENO);272close(devnull_fd);273274(void) execvp(path, argv);275_exit(-1);276} else if (pid > 0) {277int status;278279while ((rc = waitpid(pid, &status, 0)) == -1 &&280errno == EINTR) { }281282if (rc < 0 || !WIFEXITED(status))283return (-1);284285return (WEXITSTATUS(status));286}287288return (-1);289}290291static int292post_hook(const char *phase)293{294char *argv[3] = { (char *)script, (char *)phase, NULL };295int rc;296297if (synccaches)298sync();299300if (dropcaches) {301rc = drop_caches();302if (rc)303return (rc);304}305306rc = run_process(script, argv);307if (rc)308return (rc);309310return (0);311}312313#define USEC_PER_SEC 1000000314315static void316timeval_normalize(struct timeval *tv, time_t sec, suseconds_t usec)317{318while (usec >= USEC_PER_SEC) {319usec -= USEC_PER_SEC;320sec++;321}322323while (usec < 0) {324usec += USEC_PER_SEC;325sec--;326}327328tv->tv_sec = sec;329tv->tv_usec = usec;330}331332static void333timeval_sub(struct timeval *delta, struct timeval *tv1, struct timeval *tv2)334{335timeval_normalize(delta,336tv1->tv_sec - tv2->tv_sec,337tv1->tv_usec - tv2->tv_usec);338}339340static double341timeval_sub_seconds(struct timeval *tv1, struct timeval *tv2)342{343struct timeval delta;344345timeval_sub(&delta, tv1, tv2);346return ((double)delta.tv_usec / USEC_PER_SEC + delta.tv_sec);347}348349static int350create_files(void)351{352int i, rc;353char *file = NULL;354struct timeval start, stop;355double seconds;356size_t fsize;357358fsize = PATH_MAX;359file = malloc(fsize);360if (file == NULL) {361rc = ENOMEM;362ERROR("Error %d: malloc(%d) bytes for file name\n", rc,363PATH_MAX);364goto out;365}366367(void) gettimeofday(&start, NULL);368369for (i = 1; i <= files; i++) {370if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {371rc = EINVAL;372ERROR("Error %d: path too long\n", rc);373goto out;374}375376if (nth && ((i % nth) == 0))377fprintf(stdout, "create: %s\n", file);378379rc = unlink(file);380if ((rc == -1) && (errno != ENOENT)) {381ERROR("Error %d: unlink(%s)\n", errno, file);382rc = errno;383goto out;384}385386rc = open(file, O_CREAT, 0644);387if (rc == -1) {388ERROR("Error %d: open(%s, O_CREATE, 0644)\n",389errno, file);390rc = errno;391goto out;392}393394rc = close(rc);395if (rc == -1) {396ERROR("Error %d: close(%d)\n", errno, rc);397rc = errno;398goto out;399}400}401402(void) gettimeofday(&stop, NULL);403seconds = timeval_sub_seconds(&stop, &start);404fprintf(stdout, "create: %f seconds %f creates/second\n",405seconds, files / seconds);406407rc = post_hook("post");408out:409if (file)410free(file);411412return (rc);413}414415static int416get_random_bytes(char *buf, size_t bytes)417{418int rand;419ssize_t bytes_read = 0;420421rand = open("/dev/urandom", O_RDONLY);422423if (rand < 0)424return (rand);425426while (bytes_read < bytes) {427ssize_t rc = read(rand, buf + bytes_read, bytes - bytes_read);428if (rc < 0)429break;430bytes_read += rc;431}432433(void) close(rand);434435return (bytes_read);436}437438static int439setxattrs(void)440{441int i, j, rnd_size = size, shift, rc = 0;442char name[XATTR_NAME_MAX];443char *value = NULL;444char *file = NULL;445struct timeval start, stop;446double seconds;447size_t fsize;448449value = malloc(XATTR_SIZE_MAX);450if (value == NULL) {451rc = ENOMEM;452ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc,453XATTR_SIZE_MAX);454goto out;455}456457fsize = PATH_MAX;458file = malloc(fsize);459if (file == NULL) {460rc = ENOMEM;461ERROR("Error %d: malloc(%d) bytes for file name\n", rc,462PATH_MAX);463goto out;464}465466(void) gettimeofday(&start, NULL);467468for (i = 1; i <= files; i++) {469if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {470rc = EINVAL;471ERROR("Error %d: path too long\n", rc);472goto out;473}474475if (nth && ((i % nth) == 0))476fprintf(stdout, "setxattr: %s\n", file);477478for (j = 1; j <= xattrs; j++) {479if (size_is_random)480rnd_size = (random() % (size - 16)) + 16;481482(void) sprintf(name, "user.%d", j);483shift = sprintf(value, "size=%d ", rnd_size);484memcpy(value + shift, xattrbytes,485sizeof (xattrbytes) - shift);486487rc = lsetxattr(file, name, value, rnd_size, 0);488if (rc == -1) {489ERROR("Error %d: lsetxattr(%s, %s, ..., %d)\n",490errno, file, name, rnd_size);491goto out;492}493}494}495496(void) gettimeofday(&stop, NULL);497seconds = timeval_sub_seconds(&stop, &start);498fprintf(stdout, "setxattr: %f seconds %f setxattrs/second\n",499seconds, (files * xattrs) / seconds);500501rc = post_hook("post");502out:503if (file)504free(file);505506if (value)507free(value);508509return (rc);510}511512static int513getxattrs(void)514{515int i, j, rnd_size, shift, rc = 0;516char name[XATTR_NAME_MAX];517char *verify_value = NULL;518const char *verify_string;519char *value = NULL;520const char *value_string;521char *file = NULL;522struct timeval start, stop;523double seconds;524size_t fsize;525526verify_value = malloc(XATTR_SIZE_MAX);527if (verify_value == NULL) {528rc = ENOMEM;529ERROR("Error %d: malloc(%d) bytes for xattr verify\n", rc,530XATTR_SIZE_MAX);531goto out;532}533534value = malloc(XATTR_SIZE_MAX);535if (value == NULL) {536rc = ENOMEM;537ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc,538XATTR_SIZE_MAX);539goto out;540}541542verify_string = value_is_random ? "<random>" : verify_value;543value_string = value_is_random ? "<random>" : value;544545fsize = PATH_MAX;546file = malloc(fsize);547548if (file == NULL) {549rc = ENOMEM;550ERROR("Error %d: malloc(%d) bytes for file name\n", rc,551PATH_MAX);552goto out;553}554555(void) gettimeofday(&start, NULL);556557for (i = 1; i <= files; i++) {558if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {559rc = EINVAL;560ERROR("Error %d: path too long\n", rc);561goto out;562}563564if (nth && ((i % nth) == 0))565fprintf(stdout, "getxattr: %s\n", file);566567for (j = 1; j <= xattrs; j++) {568(void) sprintf(name, "user.%d", j);569570rc = lgetxattr(file, name, value, XATTR_SIZE_MAX);571if (rc == -1) {572ERROR("Error %d: lgetxattr(%s, %s, ..., %d)\n",573errno, file, name, XATTR_SIZE_MAX);574goto out;575}576577if (!verify)578continue;579580sscanf(value, "size=%d [a-z]", &rnd_size);581shift = sprintf(verify_value, "size=%d ",582rnd_size);583memcpy(verify_value + shift, xattrbytes,584sizeof (xattrbytes) - shift);585586if (rnd_size != rc ||587memcmp(verify_value, value, rnd_size)) {588ERROR("Error %d: verify failed\n "589"verify: %s\n value: %s\n", EINVAL,590verify_string, value_string);591rc = 1;592goto out;593}594}595}596597(void) gettimeofday(&stop, NULL);598seconds = timeval_sub_seconds(&stop, &start);599fprintf(stdout, "getxattr: %f seconds %f getxattrs/second\n",600seconds, (files * xattrs) / seconds);601602rc = post_hook("post");603out:604if (file)605free(file);606607if (value)608free(value);609610if (verify_value)611free(verify_value);612613return (rc);614}615616static int617unlink_files(void)618{619int i, rc;620char *file = NULL;621struct timeval start, stop;622double seconds;623size_t fsize;624625fsize = PATH_MAX;626file = malloc(fsize);627if (file == NULL) {628rc = ENOMEM;629ERROR("Error %d: malloc(%d) bytes for file name\n",630rc, PATH_MAX);631goto out;632}633634(void) gettimeofday(&start, NULL);635636for (i = 1; i <= files; i++) {637if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {638rc = EINVAL;639ERROR("Error %d: path too long\n", rc);640goto out;641}642643if (nth && ((i % nth) == 0))644fprintf(stdout, "unlink: %s\n", file);645646rc = unlink(file);647if ((rc == -1) && (errno != ENOENT)) {648ERROR("Error %d: unlink(%s)\n", errno, file);649free(file);650return (errno);651}652}653654(void) gettimeofday(&stop, NULL);655seconds = timeval_sub_seconds(&stop, &start);656fprintf(stdout, "unlink: %f seconds %f unlinks/second\n",657seconds, files / seconds);658659rc = post_hook("post");660out:661if (file)662free(file);663664return (rc);665}666667int668main(int argc, char **argv)669{670int rc;671672rc = parse_args(argc, argv);673if (rc)674return (rc);675676if (value_is_random) {677size_t rndsz = sizeof (xattrbytes);678679rc = get_random_bytes(xattrbytes, rndsz);680if (rc < rndsz) {681ERROR("Error %d: get_random_bytes() wanted %zd "682"got %d\n", errno, rndsz, rc);683return (rc);684}685} else {686memset(xattrbytes, 'x', sizeof (xattrbytes));687}688689if (phase == PHASE_ALL || phase == PHASE_CREATE) {690rc = create_files();691if (rc)692return (rc);693}694695if (phase == PHASE_ALL || phase == PHASE_SETXATTR) {696rc = setxattrs();697if (rc)698return (rc);699}700701if (phase == PHASE_ALL || phase == PHASE_GETXATTR) {702rc = getxattrs();703if (rc)704return (rc);705}706707if (!keep_files && (phase == PHASE_ALL || phase == PHASE_UNLINK)) {708rc = unlink_files();709if (rc)710return (rc);711}712713return (0);714}715716717