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