Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/tests/zfs-tests/cmd/xattrtest.c
48529 views
1
// SPDX-License-Identifier: CDDL-1.0
2
/*
3
* CDDL HEADER START
4
*
5
* The contents of this file are subject to the terms of the
6
* Common Development and Distribution License (the "License").
7
* You may not use this file except in compliance with the License.
8
*
9
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10
* or https://opensource.org/licenses/CDDL-1.0.
11
* See the License for the specific language governing permissions
12
* and limitations under the License.
13
*
14
* When distributing Covered Code, include this CDDL HEADER in each
15
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16
* If applicable, add the following below this CDDL HEADER, with the
17
* fields enclosed by brackets "[]" replaced with your own identifying
18
* information: Portions Copyright [yyyy] [name of copyright owner]
19
*
20
* CDDL HEADER END
21
*/
22
23
/*
24
* Copyright 2016 Lawrence Livermore National Security, LLC.
25
*/
26
27
/*
28
* An extended attribute (xattr) correctness test. This program creates
29
* N files and sets M attrs on them of size S. Optionally is will verify
30
* a pattern stored in the xattr.
31
*/
32
#include <stdlib.h>
33
#include <stddef.h>
34
#include <stdio.h>
35
#include <string.h>
36
#include <errno.h>
37
#include <getopt.h>
38
#include <fcntl.h>
39
#include <time.h>
40
#include <unistd.h>
41
#include <sys/xattr.h>
42
#include <sys/types.h>
43
#include <sys/wait.h>
44
#include <sys/stat.h>
45
#include <sys/time.h>
46
#include <linux/limits.h>
47
48
#define ERROR(fmt, ...) \
49
fprintf(stderr, "xattrtest: %s:%d: %s: " fmt "\n", \
50
__FILE__, __LINE__, \
51
__func__, ## __VA_ARGS__);
52
53
static const char shortopts[] = "hvycdn:f:x:s:p:t:e:rRko:";
54
static const struct option longopts[] = {
55
{ "help", no_argument, 0, 'h' },
56
{ "verbose", no_argument, 0, 'v' },
57
{ "verify", no_argument, 0, 'y' },
58
{ "nth", required_argument, 0, 'n' },
59
{ "files", required_argument, 0, 'f' },
60
{ "xattrs", required_argument, 0, 'x' },
61
{ "size", required_argument, 0, 's' },
62
{ "path", required_argument, 0, 'p' },
63
{ "synccaches", no_argument, 0, 'c' },
64
{ "dropcaches", no_argument, 0, 'd' },
65
{ "script", required_argument, 0, 't' },
66
{ "seed", required_argument, 0, 'e' },
67
{ "random", no_argument, 0, 'r' },
68
{ "randomvalue", no_argument, 0, 'R' },
69
{ "keep", no_argument, 0, 'k' },
70
{ "only", required_argument, 0, 'o' },
71
{ 0, 0, 0, 0 }
72
};
73
74
enum phases {
75
PHASE_ALL = 0,
76
PHASE_CREATE,
77
PHASE_SETXATTR,
78
PHASE_GETXATTR,
79
PHASE_UNLINK,
80
PHASE_INVAL
81
};
82
83
static int verbose = 0;
84
static int verify = 0;
85
static int synccaches = 0;
86
static int dropcaches = 0;
87
static int nth = 0;
88
static int files = 1000;
89
static int xattrs = 1;
90
static int size = 6;
91
static int size_is_random = 0;
92
static int value_is_random = 0;
93
static int keep_files = 0;
94
static int phase = PHASE_ALL;
95
static const char *path = "/tmp/xattrtest";
96
static const char *script = "/bin/true";
97
static char xattrbytes[XATTR_SIZE_MAX];
98
99
static int
100
usage(char *argv0)
101
{
102
fprintf(stderr,
103
"usage: %s [-hvycdrRk] [-n <nth>] [-f <files>] [-x <xattrs>]\n"
104
" [-s <bytes>] [-p <path>] [-t <script> ] [-o <phase>]\n",
105
argv0);
106
107
fprintf(stderr,
108
" --help -h This help\n"
109
" --verbose -v Increase verbosity\n"
110
" --verify -y Verify xattr contents\n"
111
" --nth -n <nth> Print every nth file\n"
112
" --files -f <files> Set xattrs on N files\n"
113
" --xattrs -x <xattrs> Set N xattrs on each file\n"
114
" --size -s <bytes> Set N bytes per xattr\n"
115
" --path -p <path> Path to files\n"
116
" --synccaches -c Sync caches between phases\n"
117
" --dropcaches -d Drop caches between phases\n"
118
" --script -t <script> Exec script between phases\n"
119
" --seed -e <seed> Random seed value\n"
120
" --random -r Randomly sized xattrs [16-size]\n"
121
" --randomvalue -R Random xattr values\n"
122
" --keep -k Don't unlink files\n"
123
" --only -o <num> Only run phase N\n"
124
" 0=all, 1=create, 2=setxattr,\n"
125
" 3=getxattr, 4=unlink\n\n");
126
127
return (1);
128
}
129
130
static int
131
parse_args(int argc, char **argv)
132
{
133
long seed = time(NULL);
134
int c;
135
int rc = 0;
136
137
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
138
switch (c) {
139
case 'h':
140
return (usage(argv[0]));
141
case 'v':
142
verbose++;
143
break;
144
case 'y':
145
verify = 1;
146
break;
147
case 'n':
148
nth = strtol(optarg, NULL, 0);
149
break;
150
case 'f':
151
files = strtol(optarg, NULL, 0);
152
break;
153
case 'x':
154
xattrs = strtol(optarg, NULL, 0);
155
break;
156
case 's':
157
size = strtol(optarg, NULL, 0);
158
if (size > XATTR_SIZE_MAX) {
159
fprintf(stderr, "Error: the -s value may not "
160
"be greater than %d\n", XATTR_SIZE_MAX);
161
rc = 1;
162
}
163
break;
164
case 'p':
165
path = optarg;
166
break;
167
case 'c':
168
synccaches = 1;
169
break;
170
case 'd':
171
dropcaches = 1;
172
break;
173
case 't':
174
script = optarg;
175
break;
176
case 'e':
177
seed = strtol(optarg, NULL, 0);
178
break;
179
case 'r':
180
size_is_random = 1;
181
break;
182
case 'R':
183
value_is_random = 1;
184
break;
185
case 'k':
186
keep_files = 1;
187
break;
188
case 'o':
189
phase = strtol(optarg, NULL, 0);
190
if (phase <= PHASE_ALL || phase >= PHASE_INVAL) {
191
fprintf(stderr, "Error: the -o value must be "
192
"greater than %d and less than %d\n",
193
PHASE_ALL, PHASE_INVAL);
194
rc = 1;
195
}
196
break;
197
default:
198
rc = 1;
199
break;
200
}
201
}
202
203
if (rc != 0)
204
return (rc);
205
206
srandom(seed);
207
208
if (verbose) {
209
fprintf(stdout, "verbose: %d\n", verbose);
210
fprintf(stdout, "verify: %d\n", verify);
211
fprintf(stdout, "nth: %d\n", nth);
212
fprintf(stdout, "files: %d\n", files);
213
fprintf(stdout, "xattrs: %d\n", xattrs);
214
fprintf(stdout, "size: %d\n", size);
215
fprintf(stdout, "path: %s\n", path);
216
fprintf(stdout, "synccaches: %d\n", synccaches);
217
fprintf(stdout, "dropcaches: %d\n", dropcaches);
218
fprintf(stdout, "script: %s\n", script);
219
fprintf(stdout, "seed: %ld\n", seed);
220
fprintf(stdout, "random size: %d\n", size_is_random);
221
fprintf(stdout, "random value: %d\n", value_is_random);
222
fprintf(stdout, "keep: %d\n", keep_files);
223
fprintf(stdout, "only: %d\n", phase);
224
fprintf(stdout, "%s", "\n");
225
}
226
227
return (rc);
228
}
229
230
static int
231
drop_caches(void)
232
{
233
char file[] = "/proc/sys/vm/drop_caches";
234
int fd, rc;
235
236
fd = open(file, O_WRONLY);
237
if (fd == -1) {
238
ERROR("Error %d: open(\"%s\", O_WRONLY)\n", errno, file);
239
return (errno);
240
}
241
242
rc = write(fd, "3", 1);
243
if ((rc == -1) || (rc != 1)) {
244
ERROR("Error %d: write(%d, \"3\", 1)\n", errno, fd);
245
(void) close(fd);
246
return (errno);
247
}
248
249
rc = close(fd);
250
if (rc == -1) {
251
ERROR("Error %d: close(%d)\n", errno, fd);
252
return (errno);
253
}
254
255
return (0);
256
}
257
258
static int
259
run_process(const char *path, char *argv[])
260
{
261
pid_t pid;
262
int rc, devnull_fd;
263
264
pid = fork();
265
if (pid == 0) {
266
devnull_fd = open("/dev/null", O_WRONLY);
267
268
if (devnull_fd < 0)
269
_exit(-1);
270
271
(void) dup2(devnull_fd, STDOUT_FILENO);
272
(void) dup2(devnull_fd, STDERR_FILENO);
273
close(devnull_fd);
274
275
(void) execvp(path, argv);
276
_exit(-1);
277
} else if (pid > 0) {
278
int status;
279
280
while ((rc = waitpid(pid, &status, 0)) == -1 &&
281
errno == EINTR) { }
282
283
if (rc < 0 || !WIFEXITED(status))
284
return (-1);
285
286
return (WEXITSTATUS(status));
287
}
288
289
return (-1);
290
}
291
292
static int
293
post_hook(const char *phase)
294
{
295
char *argv[3] = { (char *)script, (char *)phase, NULL };
296
int rc;
297
298
if (synccaches)
299
sync();
300
301
if (dropcaches) {
302
rc = drop_caches();
303
if (rc)
304
return (rc);
305
}
306
307
rc = run_process(script, argv);
308
if (rc)
309
return (rc);
310
311
return (0);
312
}
313
314
#define USEC_PER_SEC 1000000
315
316
static void
317
timeval_normalize(struct timeval *tv, time_t sec, suseconds_t usec)
318
{
319
while (usec >= USEC_PER_SEC) {
320
usec -= USEC_PER_SEC;
321
sec++;
322
}
323
324
while (usec < 0) {
325
usec += USEC_PER_SEC;
326
sec--;
327
}
328
329
tv->tv_sec = sec;
330
tv->tv_usec = usec;
331
}
332
333
static void
334
timeval_sub(struct timeval *delta, struct timeval *tv1, struct timeval *tv2)
335
{
336
timeval_normalize(delta,
337
tv1->tv_sec - tv2->tv_sec,
338
tv1->tv_usec - tv2->tv_usec);
339
}
340
341
static double
342
timeval_sub_seconds(struct timeval *tv1, struct timeval *tv2)
343
{
344
struct timeval delta;
345
346
timeval_sub(&delta, tv1, tv2);
347
return ((double)delta.tv_usec / USEC_PER_SEC + delta.tv_sec);
348
}
349
350
static int
351
create_files(void)
352
{
353
int i, rc;
354
char *file = NULL;
355
struct timeval start, stop;
356
double seconds;
357
size_t fsize;
358
359
fsize = PATH_MAX;
360
file = malloc(fsize);
361
if (file == NULL) {
362
rc = ENOMEM;
363
ERROR("Error %d: malloc(%d) bytes for file name\n", rc,
364
PATH_MAX);
365
goto out;
366
}
367
368
(void) gettimeofday(&start, NULL);
369
370
for (i = 1; i <= files; i++) {
371
if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
372
rc = EINVAL;
373
ERROR("Error %d: path too long\n", rc);
374
goto out;
375
}
376
377
if (nth && ((i % nth) == 0))
378
fprintf(stdout, "create: %s\n", file);
379
380
rc = unlink(file);
381
if ((rc == -1) && (errno != ENOENT)) {
382
ERROR("Error %d: unlink(%s)\n", errno, file);
383
rc = errno;
384
goto out;
385
}
386
387
rc = open(file, O_CREAT, 0644);
388
if (rc == -1) {
389
ERROR("Error %d: open(%s, O_CREATE, 0644)\n",
390
errno, file);
391
rc = errno;
392
goto out;
393
}
394
395
rc = close(rc);
396
if (rc == -1) {
397
ERROR("Error %d: close(%d)\n", errno, rc);
398
rc = errno;
399
goto out;
400
}
401
}
402
403
(void) gettimeofday(&stop, NULL);
404
seconds = timeval_sub_seconds(&stop, &start);
405
fprintf(stdout, "create: %f seconds %f creates/second\n",
406
seconds, files / seconds);
407
408
rc = post_hook("post");
409
out:
410
if (file)
411
free(file);
412
413
return (rc);
414
}
415
416
static int
417
get_random_bytes(char *buf, size_t bytes)
418
{
419
int rand;
420
ssize_t bytes_read = 0;
421
422
rand = open("/dev/urandom", O_RDONLY);
423
424
if (rand < 0)
425
return (rand);
426
427
while (bytes_read < bytes) {
428
ssize_t rc = read(rand, buf + bytes_read, bytes - bytes_read);
429
if (rc < 0)
430
break;
431
bytes_read += rc;
432
}
433
434
(void) close(rand);
435
436
return (bytes_read);
437
}
438
439
static int
440
setxattrs(void)
441
{
442
int i, j, rnd_size = size, shift, rc = 0;
443
char name[XATTR_NAME_MAX];
444
char *value = NULL;
445
char *file = NULL;
446
struct timeval start, stop;
447
double seconds;
448
size_t fsize;
449
450
value = malloc(XATTR_SIZE_MAX);
451
if (value == NULL) {
452
rc = ENOMEM;
453
ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc,
454
XATTR_SIZE_MAX);
455
goto out;
456
}
457
458
fsize = PATH_MAX;
459
file = malloc(fsize);
460
if (file == NULL) {
461
rc = ENOMEM;
462
ERROR("Error %d: malloc(%d) bytes for file name\n", rc,
463
PATH_MAX);
464
goto out;
465
}
466
467
(void) gettimeofday(&start, NULL);
468
469
for (i = 1; i <= files; i++) {
470
if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
471
rc = EINVAL;
472
ERROR("Error %d: path too long\n", rc);
473
goto out;
474
}
475
476
if (nth && ((i % nth) == 0))
477
fprintf(stdout, "setxattr: %s\n", file);
478
479
for (j = 1; j <= xattrs; j++) {
480
if (size_is_random)
481
rnd_size = (random() % (size - 16)) + 16;
482
483
(void) sprintf(name, "user.%d", j);
484
shift = sprintf(value, "size=%d ", rnd_size);
485
memcpy(value + shift, xattrbytes,
486
sizeof (xattrbytes) - shift);
487
488
rc = lsetxattr(file, name, value, rnd_size, 0);
489
if (rc == -1) {
490
ERROR("Error %d: lsetxattr(%s, %s, ..., %d)\n",
491
errno, file, name, rnd_size);
492
goto out;
493
}
494
}
495
}
496
497
(void) gettimeofday(&stop, NULL);
498
seconds = timeval_sub_seconds(&stop, &start);
499
fprintf(stdout, "setxattr: %f seconds %f setxattrs/second\n",
500
seconds, (files * xattrs) / seconds);
501
502
rc = post_hook("post");
503
out:
504
if (file)
505
free(file);
506
507
if (value)
508
free(value);
509
510
return (rc);
511
}
512
513
static int
514
getxattrs(void)
515
{
516
int i, j, rnd_size, shift, rc = 0;
517
char name[XATTR_NAME_MAX];
518
char *verify_value = NULL;
519
const char *verify_string;
520
char *value = NULL;
521
const char *value_string;
522
char *file = NULL;
523
struct timeval start, stop;
524
double seconds;
525
size_t fsize;
526
527
verify_value = malloc(XATTR_SIZE_MAX);
528
if (verify_value == NULL) {
529
rc = ENOMEM;
530
ERROR("Error %d: malloc(%d) bytes for xattr verify\n", rc,
531
XATTR_SIZE_MAX);
532
goto out;
533
}
534
535
value = malloc(XATTR_SIZE_MAX);
536
if (value == NULL) {
537
rc = ENOMEM;
538
ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc,
539
XATTR_SIZE_MAX);
540
goto out;
541
}
542
543
verify_string = value_is_random ? "<random>" : verify_value;
544
value_string = value_is_random ? "<random>" : value;
545
546
fsize = PATH_MAX;
547
file = malloc(fsize);
548
549
if (file == NULL) {
550
rc = ENOMEM;
551
ERROR("Error %d: malloc(%d) bytes for file name\n", rc,
552
PATH_MAX);
553
goto out;
554
}
555
556
(void) gettimeofday(&start, NULL);
557
558
for (i = 1; i <= files; i++) {
559
if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
560
rc = EINVAL;
561
ERROR("Error %d: path too long\n", rc);
562
goto out;
563
}
564
565
if (nth && ((i % nth) == 0))
566
fprintf(stdout, "getxattr: %s\n", file);
567
568
for (j = 1; j <= xattrs; j++) {
569
(void) sprintf(name, "user.%d", j);
570
571
rc = lgetxattr(file, name, value, XATTR_SIZE_MAX);
572
if (rc == -1) {
573
ERROR("Error %d: lgetxattr(%s, %s, ..., %d)\n",
574
errno, file, name, XATTR_SIZE_MAX);
575
goto out;
576
}
577
578
if (!verify)
579
continue;
580
581
sscanf(value, "size=%d [a-z]", &rnd_size);
582
shift = sprintf(verify_value, "size=%d ",
583
rnd_size);
584
memcpy(verify_value + shift, xattrbytes,
585
sizeof (xattrbytes) - shift);
586
587
if (rnd_size != rc ||
588
memcmp(verify_value, value, rnd_size)) {
589
ERROR("Error %d: verify failed\n "
590
"verify: %s\n value: %s\n", EINVAL,
591
verify_string, value_string);
592
rc = 1;
593
goto out;
594
}
595
}
596
}
597
598
(void) gettimeofday(&stop, NULL);
599
seconds = timeval_sub_seconds(&stop, &start);
600
fprintf(stdout, "getxattr: %f seconds %f getxattrs/second\n",
601
seconds, (files * xattrs) / seconds);
602
603
rc = post_hook("post");
604
out:
605
if (file)
606
free(file);
607
608
if (value)
609
free(value);
610
611
if (verify_value)
612
free(verify_value);
613
614
return (rc);
615
}
616
617
static int
618
unlink_files(void)
619
{
620
int i, rc;
621
char *file = NULL;
622
struct timeval start, stop;
623
double seconds;
624
size_t fsize;
625
626
fsize = PATH_MAX;
627
file = malloc(fsize);
628
if (file == NULL) {
629
rc = ENOMEM;
630
ERROR("Error %d: malloc(%d) bytes for file name\n",
631
rc, PATH_MAX);
632
goto out;
633
}
634
635
(void) gettimeofday(&start, NULL);
636
637
for (i = 1; i <= files; i++) {
638
if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
639
rc = EINVAL;
640
ERROR("Error %d: path too long\n", rc);
641
goto out;
642
}
643
644
if (nth && ((i % nth) == 0))
645
fprintf(stdout, "unlink: %s\n", file);
646
647
rc = unlink(file);
648
if ((rc == -1) && (errno != ENOENT)) {
649
ERROR("Error %d: unlink(%s)\n", errno, file);
650
free(file);
651
return (errno);
652
}
653
}
654
655
(void) gettimeofday(&stop, NULL);
656
seconds = timeval_sub_seconds(&stop, &start);
657
fprintf(stdout, "unlink: %f seconds %f unlinks/second\n",
658
seconds, files / seconds);
659
660
rc = post_hook("post");
661
out:
662
if (file)
663
free(file);
664
665
return (rc);
666
}
667
668
int
669
main(int argc, char **argv)
670
{
671
int rc;
672
673
rc = parse_args(argc, argv);
674
if (rc)
675
return (rc);
676
677
if (value_is_random) {
678
size_t rndsz = sizeof (xattrbytes);
679
680
rc = get_random_bytes(xattrbytes, rndsz);
681
if (rc < rndsz) {
682
ERROR("Error %d: get_random_bytes() wanted %zd "
683
"got %d\n", errno, rndsz, rc);
684
return (rc);
685
}
686
} else {
687
memset(xattrbytes, 'x', sizeof (xattrbytes));
688
}
689
690
if (phase == PHASE_ALL || phase == PHASE_CREATE) {
691
rc = create_files();
692
if (rc)
693
return (rc);
694
}
695
696
if (phase == PHASE_ALL || phase == PHASE_SETXATTR) {
697
rc = setxattrs();
698
if (rc)
699
return (rc);
700
}
701
702
if (phase == PHASE_ALL || phase == PHASE_GETXATTR) {
703
rc = getxattrs();
704
if (rc)
705
return (rc);
706
}
707
708
if (!keep_files && (phase == PHASE_ALL || phase == PHASE_UNLINK)) {
709
rc = unlink_files();
710
if (rc)
711
return (rc);
712
}
713
714
return (0);
715
}
716
717