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 /*
192 * Before kernel 3.0.0, fgetxattr(2) will set errno with 'EPERM'
193 * when the file is not a regular file and directory, refer to
194 * commitid 55b23bd
195 */
196 if (tc[i].exp_err == ENODATA && tst_kvercmp(3, 0, 0) < 0)
197 tc[i].exp_err = EPERM;
198
199 if (tc[i].exp_err == TST_ERR) {
200 tst_res(TPASS | TTERRNO, "fgetxattr(2) on %s passed",
201 fname);
202 return;
203 }
204
205 tst_res(TFAIL | TTERRNO, "fgetxattr(2) failed on %s", fname);
206 }
207
setup(void)208 static void setup(void)
209 {
210 size_t i = 0;
211 struct sockaddr_un sun;
212
213 dev_t chr_dev = makedev(1, 3);
214 dev_t blk_dev = makedev(7, 3);
215
216 SAFE_TOUCH(FILENAME, 0644, NULL);
217 SAFE_TOUCH(SYMLINKF, 0644, NULL);
218 SAFE_MKDIR(DIRNAME, 0644);
219 SAFE_SYMLINK(SYMLINKF, SYMLINK);
220
221 /* root: mknod(2) needs it to create something other than a file */
222 SAFE_MKNOD(FIFO, S_IFIFO | 0777, 0);
223 SAFE_MKNOD(CHR, S_IFCHR | 0777, chr_dev);
224 SAFE_MKNOD(BLK, S_IFBLK | 0777, blk_dev);
225
226 for (i = 0; i < ARRAY_SIZE(tc); i++) {
227
228 tc[i].ret_value = SAFE_MALLOC(tc[i].size);
229 memset(tc[i].ret_value, 0, tc[i].size);
230
231 if (tc[i].issocket) {
232 /* differently than getxattr(2) calls, when dealing with
233 * sockets, mknod(2) isn't enough to test fgetxattr(2).
234 * we have to get a real unix socket in order for
235 * open(2) to get a file desc.
236 */
237 tc[i].fd = SAFE_SOCKET(AF_UNIX, SOCK_STREAM, 0);
238
239 memset(&sun, 0, sizeof(struct sockaddr_un));
240 sun.sun_family = AF_UNIX;
241 strncpy(sun.sun_path, tc[i].fname,
242 sizeof(sun.sun_path) - 1);
243
244 SAFE_BIND(tc[i].fd, (const struct sockaddr *) &sun,
245 sizeof(struct sockaddr_un));
246 } else {
247 tc[i].fd = SAFE_OPEN(tc[i].fname, tc[i].fflags, NULL);
248 }
249
250 if (tc[i].exp_ret >= 0) {
251 SAFE_FSETXATTR(tc[i].fd, tc[i].key, tc[i].value,
252 tc[i].size, tc[i].flags);
253 }
254 }
255 }
256
cleanup(void)257 static void cleanup(void)
258 {
259 size_t i = 0;
260
261 for (i = 0; i < ARRAY_SIZE(tc); i++) {
262 free(tc[i].ret_value);
263
264 if (tc[i].fd > 0)
265 SAFE_CLOSE(tc[i].fd);
266 }
267 }
268
269 static struct tst_test test = {
270 .setup = setup,
271 .test = verify_fgetxattr,
272 .cleanup = cleanup,
273 .tcnt = ARRAY_SIZE(tc),
274 .needs_devfs = 1,
275 .mntpoint = MNTPOINT,
276 .needs_root = 1,
277 };
278
279 #else /* HAVE_SYS_XATTR_H */
280 TST_TEST_TCONF("<sys/xattr.h> does not exist");
281 #endif
282