1 /*
2  * Copyright (c) 2017 Fujitsu Ltd.
3  * Author: Xiao Yang <yangx.jy@cn.fujitsu.com>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.*
17  */
18 
19 /*
20  * This is a regression test for the race between getting an existing
21  * xattr and setting/removing a large xattr.  This bug leads to that
22  * getxattr() fails to get an existing xattr and returns ENOATTR in xfs
23  * filesystem.
24  *
25  * Thie bug has been fixed in:
26  *
27  * commit 5a93790d4e2df73e30c965ec6e49be82fc3ccfce
28  * Author: Brian Foster <bfoster@redhat.com>
29  * Date:   Wed Jan 25 07:53:43 2017 -0800
30  *
31  * xfs: remove racy hasattr check from attr ops
32  */
33 
34 #include "config.h"
35 #include <errno.h>
36 #include <sys/types.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <signal.h>
40 
41 #ifdef HAVE_SYS_XATTR_H
42 # include <sys/xattr.h>
43 #endif
44 
45 #include "tst_test.h"
46 
47 #ifdef HAVE_SYS_XATTR_H
48 
49 #define MNTPOINT	"mntpoint"
50 #define TEST_FILE	MNTPOINT "/file"
51 #define TRUSTED_BIG	"trusted.big"
52 #define TRUSTED_SMALL	"trusted.small"
53 
54 static volatile int end;
55 static char big_value[512];
56 static char small_value[32];
57 
58 static void sigproc(int sig)
59 {
60 	end = sig;
61 }
62 
63 static void loop_getxattr(void)
64 {
65 	int res;
66 
67 	while (!end) {
68 		res = getxattr(TEST_FILE, TRUSTED_SMALL, NULL, 0);
69 		if (res == -1) {
70 			if (errno == ENODATA) {
71 				tst_res(TFAIL, "getxattr() failed to get an "
72 					"existing attribute");
73 			} else {
74 				tst_res(TFAIL | TERRNO,
75 					"getxattr() failed without ENOATTR");
76 			}
77 
78 			exit(0);
79 		}
80 	}
81 
82 	tst_res(TPASS, "getxattr() succeeded to get an existing attribute");
83 	exit(0);
84 }
85 
86 static void verify_getxattr(void)
87 {
88 	pid_t pid;
89 	int n;
90 
91 	end = 0;
92 
93 	pid = SAFE_FORK();
94 	if (!pid)
95 		loop_getxattr();
96 
97 	for (n = 0; n < 99; n++) {
98 		SAFE_SETXATTR(TEST_FILE, TRUSTED_BIG, big_value,
99 				sizeof(big_value), XATTR_CREATE);
100 		SAFE_REMOVEXATTR(TEST_FILE, TRUSTED_BIG);
101 	}
102 
103 	kill(pid, SIGUSR1);
104 	tst_reap_children();
105 }
106 
107 static void setup(void)
108 {
109 	SAFE_SIGNAL(SIGUSR1, sigproc);
110 
111 	SAFE_TOUCH(TEST_FILE, 0644, NULL);
112 
113 	memset(big_value, 'a', sizeof(big_value));
114 	memset(small_value, 'a', sizeof(small_value));
115 
116 	SAFE_SETXATTR(TEST_FILE, TRUSTED_SMALL, small_value,
117 			sizeof(small_value), XATTR_CREATE);
118 }
119 
120 static struct tst_test test = {
121 	.needs_tmpdir = 1,
122 	.needs_root = 1,
123 	.mount_device = 1,
124 	.dev_fs_type = "xfs",
125 	.mntpoint = MNTPOINT,
126 	.forks_child = 1,
127 	.test_all = verify_getxattr,
128 	.setup = setup
129 };
130 
131 #else /* HAVE_SYS_XATTR_H */
132 	TST_TEST_TCONF("<sys/xattr.h> does not exist.");
133 #endif
134