• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2018 Linaro Limited. All rights reserved.
4  * Author: Rafael David Tinoco <rafael.tinoco@linaro.org>
5  */
6 
7 /*
8  * In the user.* namespace, only regular files and directories can
9  * have extended attributes. Otherwise fgetxattr(2) will return -1
10  * and set proper errno.
11  *
12  * There are 7 test cases:
13  *
14  * 1. Get attribute from a regular file:
15  *    - fgetxattr(2) should succeed
16  *    - checks returned value to be the same as we set
17  * 2. Get attribute from a directory:
18  *    - fgetxattr(2) should succeed
19  *    - checks returned value to be the same as we set
20  * 3. Get attribute from a symlink which points to the regular file:
21  *    - fgetxattr(2) should succeed
22  *    - checks returned value to be the same as we set
23  * 4. Get attribute from a FIFO:
24  *    - fgetxattr(2) should return -1 and set errno to ENODATA
25  * 5. Get attribute from a char special file:
26  *    - fgetxattr(2) should return -1 and set errno to ENODATA
27  * 6. Get attribute from a block special file:
28  *    - fgetxattr(2) should return -1 and set errno to ENODATA
29  * 7. Get attribute from a UNIX domain socket:
30  *    - fgetxattr(2) should return -1 and set errno to ENODATA
31  */
32 
33 #include "config.h"
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/sysmacros.h>
37 #include <sys/wait.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <signal.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <sys/socket.h>
46 #include <sys/un.h>
47 #ifdef HAVE_SYS_XATTR_H
48 # include <sys/xattr.h>
49 #endif
50 #include "tst_test.h"
51 
52 #ifdef HAVE_SYS_XATTR_H
53 #define XATTR_TEST_KEY "user.testkey"
54 #define XATTR_TEST_VALUE "this is a test value"
55 #define XATTR_TEST_VALUE_SIZE 20
56 
57 #define MNTPOINT "mntpoint"
58 #define OFFSET    11
59 #define FILENAME "fgetxattr02testfile"
60 #define DIRNAME  "fgetxattr02testdir"
61 #define SYMLINK  "fgetxattr02symlink"
62 #define SYMLINKF "fgetxattr02symlinkfile"
63 #define FIFO     MNTPOINT"/fgetxattr02fifo"
64 #define CHR      MNTPOINT"/fgetxattr02chr"
65 #define BLK      MNTPOINT"/fgetxattr02blk"
66 #define SOCK     "fgetxattr02sock"
67 
68 struct test_case {
69 	char *fname;
70 	int fd;
71 	int fflags;
72 	char *key;
73 	char *value;
74 	size_t size;
75 	char *ret_value;
76 	int flags;
77 	int exp_err;
78 	int exp_ret;
79 	int issocket;
80 };
81 static struct test_case tc[] = {
82 	{			/* case 00, get attr from reg */
83 	 .fname = FILENAME,
84 	 .fflags = O_RDONLY,
85 	 .key = XATTR_TEST_KEY,
86 	 .value = XATTR_TEST_VALUE,
87 	 .size = XATTR_TEST_VALUE_SIZE,
88 	 .ret_value = NULL,
89 	 .flags = XATTR_CREATE,
90 	 .exp_err = 0,
91 	 .exp_ret = XATTR_TEST_VALUE_SIZE,
92 	 },
93 	{			/* case 01, get attr from dir */
94 	 .fname = DIRNAME,
95 	 .fflags = O_RDONLY,
96 	 .key = XATTR_TEST_KEY,
97 	 .value = XATTR_TEST_VALUE,
98 	 .size = XATTR_TEST_VALUE_SIZE,
99 	 .ret_value = NULL,
100 	 .flags = XATTR_CREATE,
101 	 .exp_err = 0,
102 	 .exp_ret = XATTR_TEST_VALUE_SIZE,
103 	 },
104 	{			/* case 02, get attr from symlink */
105 	 .fname = SYMLINK,
106 	 .fflags = O_RDONLY,
107 	 .key = XATTR_TEST_KEY,
108 	 .value = XATTR_TEST_VALUE,
109 	 .size = XATTR_TEST_VALUE_SIZE,
110 	 .ret_value = NULL,
111 	 .flags = XATTR_CREATE,
112 	 .exp_err = 0,
113 	 .exp_ret = XATTR_TEST_VALUE_SIZE,
114 	 },
115 	{			/* case 03, get attr from fifo */
116 	 .fname = FIFO,
117 	 .fflags = (O_RDONLY | O_NONBLOCK),
118 	 .key = XATTR_TEST_KEY,
119 	 .value = XATTR_TEST_VALUE,
120 	 .size = XATTR_TEST_VALUE_SIZE,
121 	 .flags = XATTR_CREATE,
122 	 .exp_err = ENODATA,
123 	 .exp_ret = -1,
124 	 },
125 	{			/* case 04, get attr from character special */
126 	 .fname = CHR,
127 	 .fflags = O_RDONLY,
128 	 .key = XATTR_TEST_KEY,
129 	 .value = XATTR_TEST_VALUE,
130 	 .size = XATTR_TEST_VALUE_SIZE,
131 	 .ret_value = NULL,
132 	 .flags = XATTR_CREATE,
133 	 .exp_err = ENODATA,
134 	 .exp_ret = -1,
135 	 },
136 	{			/* case 05, get attr from block special */
137 	 .fname = BLK,
138 	 .fflags = O_RDONLY,
139 	 .key = XATTR_TEST_KEY,
140 	 .value = XATTR_TEST_VALUE,
141 	 .size = XATTR_TEST_VALUE_SIZE,
142 	 .ret_value = NULL,
143 	 .flags = XATTR_CREATE,
144 	 .exp_err = ENODATA,
145 	 .exp_ret = -1,
146 	 },
147 	{			/* case 06, get attr from socket */
148 	 .fname = SOCK,
149 	 .fflags = O_RDONLY,
150 	 .key = XATTR_TEST_KEY,
151 	 .value = XATTR_TEST_VALUE,
152 	 .size = XATTR_TEST_VALUE_SIZE,
153 	 .ret_value = NULL,
154 	 .flags = XATTR_CREATE,
155 	 .exp_err = ENODATA,
156 	 .exp_ret = -1,
157 	 .issocket = 1,
158 	 },
159 };
160 
verify_fgetxattr(unsigned int i)161 static void verify_fgetxattr(unsigned int i)
162 {
163 	const char *fname = strstr(tc[i].fname, "fgetxattr02") + OFFSET;
164 
165 	TEST(fgetxattr(tc[i].fd, tc[i].key, tc[i].ret_value, tc[i].size));
166 
167 	if (TST_RET == -1 && TST_ERR == EOPNOTSUPP)
168 		tst_brk(TCONF, "fgetxattr(2) not supported");
169 
170 	if (TST_RET >= 0) {
171 
172 		if (tc[i].exp_ret == TST_RET) {
173 			tst_res(TPASS, "fgetxattr(2) on %s passed",
174 					fname);
175 		} else {
176 			tst_res(TFAIL,
177 				"fgetxattr(2) on %s passed unexpectedly %ld",
178 				fname, TST_RET);
179 		}
180 
181 		if (strncmp(tc[i].ret_value, XATTR_TEST_VALUE,
182 				XATTR_TEST_VALUE_SIZE)) {
183 			tst_res(TFAIL, "wrong value, expect \"%s\" got \"%s\"",
184 					 XATTR_TEST_VALUE, tc[i].ret_value);
185 		}
186 
187 		tst_res(TPASS, "fgetxattr(2) on %s got the right value",
188 				fname);
189 	}
190 
191 	if (tc[i].exp_err == TST_ERR) {
192 		tst_res(TPASS | TTERRNO, "fgetxattr(2) on %s passed",
193 				fname);
194 		return;
195 	}
196 
197 	tst_res(TFAIL | TTERRNO, "fgetxattr(2) failed on %s", fname);
198 }
199 
setup(void)200 static void setup(void)
201 {
202 	size_t i = 0;
203 	struct sockaddr_un sun;
204 
205 	dev_t chr_dev = makedev(1, 3);
206 	dev_t blk_dev = makedev(7, 3);
207 
208 	SAFE_TOUCH(FILENAME, 0644, NULL);
209 	SAFE_TOUCH(SYMLINKF, 0644, NULL);
210 	SAFE_MKDIR(DIRNAME, 0644);
211 	SAFE_SYMLINK(SYMLINKF, SYMLINK);
212 
213 	/* root: mknod(2) needs it to create something other than a file */
214 	SAFE_MKNOD(FIFO, S_IFIFO | 0777, 0);
215 	SAFE_MKNOD(CHR, S_IFCHR | 0777, chr_dev);
216 	SAFE_MKNOD(BLK, S_IFBLK | 0777, blk_dev);
217 
218 	for (i = 0; i < ARRAY_SIZE(tc); i++) {
219 
220 		tc[i].ret_value = SAFE_MALLOC(tc[i].size);
221 		memset(tc[i].ret_value, 0, tc[i].size);
222 
223 		if (tc[i].issocket) {
224 			/* differently than getxattr(2) calls, when dealing with
225 			 * sockets, mknod(2) isn't enough to test fgetxattr(2).
226 			 * we have to get a real unix socket in order for
227 			 * open(2) to get a file desc.
228 			 */
229 			tc[i].fd = SAFE_SOCKET(AF_UNIX, SOCK_STREAM, 0);
230 
231 			memset(&sun, 0, sizeof(struct sockaddr_un));
232 			sun.sun_family = AF_UNIX;
233 			strncpy(sun.sun_path, tc[i].fname,
234 					sizeof(sun.sun_path) - 1);
235 
236 			SAFE_BIND(tc[i].fd, (const struct sockaddr *) &sun,
237 					sizeof(struct sockaddr_un));
238 		} else {
239 			tc[i].fd = SAFE_OPEN(tc[i].fname, tc[i].fflags);
240 		}
241 
242 		if (tc[i].exp_ret >= 0) {
243 			SAFE_FSETXATTR(tc[i].fd, tc[i].key, tc[i].value,
244 					tc[i].size, tc[i].flags);
245 		}
246 	}
247 }
248 
cleanup(void)249 static void cleanup(void)
250 {
251 	size_t i = 0;
252 
253 	for (i = 0; i < ARRAY_SIZE(tc); i++) {
254 		free(tc[i].ret_value);
255 
256 		if (tc[i].fd > 0)
257 			SAFE_CLOSE(tc[i].fd);
258 	}
259 }
260 
261 static struct tst_test test = {
262 	.setup = setup,
263 	.test = verify_fgetxattr,
264 	.cleanup = cleanup,
265 	.tcnt = ARRAY_SIZE(tc),
266 	.needs_devfs = 1,
267 	.mntpoint = MNTPOINT,
268 	.needs_root = 1,
269 };
270 
271 #else /* HAVE_SYS_XATTR_H */
272 TST_TEST_TCONF("<sys/xattr.h> does not exist");
273 #endif
274