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 fsetxattr(2) will return -1
10 * and set errno to EPERM.
11 *
12 * There are 7 test cases:
13 * 1. Set attribute to a regular file, fsetxattr(2) should succeed
14 * 2. Set attribute to a directory, fsetxattr(2) should succeed
15 * 3. Set attribute to a symlink which points to the regular file,
16 * fsetxattr(2) should return -1 and set errno to EEXIST
17 * 4. Set attribute to a FIFO, fsetxattr(2) should return -1 and set
18 * errno to EPERM
19 * 5. Set attribute to a char special file, fsetxattr(2) should
20 * return -1 and set errno to EPERM
21 * 6. Set attribute to a block special file, fsetxattr(2) should
22 * return -1 and set errno to EPERM
23 * 7. Set attribute to a UNIX domain socket, fsetxattr(2) should
24 * return -1 and set errno to EPERM
25 */
26
27 #include "config.h"
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/sysmacros.h>
31 #include <sys/wait.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <signal.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/socket.h>
40 #include <sys/un.h>
41 #ifdef HAVE_SYS_XATTR_H
42 # include <sys/xattr.h>
43 #endif
44 #include "tst_test.h"
45
46 #ifdef HAVE_SYS_XATTR_H
47 #define XATTR_TEST_KEY "user.testkey"
48 #define XATTR_TEST_VALUE "this is a test value"
49 #define XATTR_TEST_VALUE_SIZE 20
50
51 #define MNTPOINT "mntpoint"
52 #define OFFSET 11
53 #define FILENAME "fsetxattr02testfile"
54 #define DIRNAME "fsetxattr02testdir"
55 #define SYMLINK "fsetxattr02symlink"
56 #define FIFO MNTPOINT"/fsetxattr02fifo"
57 #define CHR MNTPOINT"/fsetxattr02chr"
58 #define BLK MNTPOINT"/fsetxattr02blk"
59 #define SOCK "fsetxattr02sock"
60
61 struct test_case {
62 char *fname;
63 int fd;
64 int fflags;
65 char *key;
66 char *value;
67 size_t size;
68 int flags;
69 int exp_err;
70 int issocket;
71 int needskeyset;
72 };
73 static struct test_case tc[] = {
74 { /* case 00, set attr to reg */
75 .fname = FILENAME,
76 .fflags = O_RDONLY,
77 .key = XATTR_TEST_KEY,
78 .value = XATTR_TEST_VALUE,
79 .size = XATTR_TEST_VALUE_SIZE,
80 .flags = XATTR_CREATE,
81 .exp_err = 0,
82 },
83 { /* case 01, set attr to dir */
84 .fname = DIRNAME,
85 .fflags = O_RDONLY,
86 .key = XATTR_TEST_KEY,
87 .value = XATTR_TEST_VALUE,
88 .size = XATTR_TEST_VALUE_SIZE,
89 .flags = XATTR_CREATE,
90 .exp_err = 0,
91 },
92 { /* case 02, set attr to symlink */
93 .fname = SYMLINK,
94 .fflags = O_RDONLY,
95 .key = XATTR_TEST_KEY,
96 .value = XATTR_TEST_VALUE,
97 .size = XATTR_TEST_VALUE_SIZE,
98 .flags = XATTR_CREATE,
99 .exp_err = EEXIST,
100 .needskeyset = 1,
101 },
102 { /* case 03, set attr to fifo */
103 .fname = FIFO,
104 .fflags = (O_RDONLY | O_NONBLOCK),
105 .key = XATTR_TEST_KEY,
106 .value = XATTR_TEST_VALUE,
107 .size = XATTR_TEST_VALUE_SIZE,
108 .flags = XATTR_CREATE,
109 .exp_err = EPERM,
110 },
111 { /* case 04, set attr to character special */
112 .fname = CHR,
113 .fflags = O_RDONLY,
114 .key = XATTR_TEST_KEY,
115 .value = XATTR_TEST_VALUE,
116 .size = XATTR_TEST_VALUE_SIZE,
117 .flags = XATTR_CREATE,
118 .exp_err = EPERM,
119 },
120 { /* case 05, set attr to block special */
121 .fname = BLK,
122 .fflags = O_RDONLY,
123 .key = XATTR_TEST_KEY,
124 .value = XATTR_TEST_VALUE,
125 .size = XATTR_TEST_VALUE_SIZE,
126 .flags = XATTR_CREATE,
127 .exp_err = EPERM,
128 },
129 { /* case 06, set attr to socket */
130 .fname = SOCK,
131 .fflags = O_RDONLY,
132 .key = XATTR_TEST_KEY,
133 .value = XATTR_TEST_VALUE,
134 .size = XATTR_TEST_VALUE_SIZE,
135 .flags = XATTR_CREATE,
136 .exp_err = EPERM,
137 .issocket = 1,
138 },
139 };
140
verify_fsetxattr(unsigned int i)141 static void verify_fsetxattr(unsigned int i)
142 {
143 const char *fname = strstr(tc[i].fname, "fsetxattr02") + OFFSET;
144
145 /* some tests might require existing keys for each iteration */
146 if (tc[i].needskeyset) {
147 SAFE_FSETXATTR(tc[i].fd, tc[i].key, tc[i].value, tc[i].size,
148 XATTR_CREATE);
149 }
150
151 TEST(fsetxattr(tc[i].fd, tc[i].key, tc[i].value, tc[i].size,
152 tc[i].flags));
153
154 if (TST_RET == -1 && TST_ERR == EOPNOTSUPP)
155 tst_brk(TCONF, "fsetxattr(2) not supported");
156
157 /* success */
158
159 if (!tc[i].exp_err) {
160 if (TST_RET) {
161 tst_res(TFAIL | TTERRNO,
162 "fsetxattr(2) on %s failed with %li",
163 fname, TST_RET);
164 return;
165 }
166
167 /* this is needed for subsequent iterations */
168 SAFE_FREMOVEXATTR(tc[i].fd, tc[i].key);
169
170 tst_res(TPASS, "fsetxattr(2) on %s passed", fname);
171 return;
172 }
173
174 if (TST_RET == 0) {
175 tst_res(TFAIL, "fsetxattr(2) on %s passed unexpectedly", fname);
176 return;
177 }
178
179 /* fail */
180
181 if (tc[i].exp_err != TST_ERR) {
182 tst_res(TFAIL | TTERRNO,
183 "fsetxattr(2) on %s should have failed with %s",
184 fname, tst_strerrno(tc[i].exp_err));
185 return;
186 }
187
188 /* key might have been added AND test might have failed, remove it */
189 if (tc[i].needskeyset)
190 SAFE_FREMOVEXATTR(tc[i].fd, tc[i].key);
191
192 tst_res(TPASS | TTERRNO, "fsetxattr(2) on %s failed", fname);
193 }
194
setup(void)195 static void setup(void)
196 {
197 size_t i = 0;
198 struct sockaddr_un sun;
199
200 dev_t dev = makedev(1, 3);
201
202 SAFE_TOUCH(FILENAME, 0644, NULL);
203 SAFE_MKDIR(DIRNAME, 0644);
204 SAFE_SYMLINK(FILENAME, SYMLINK);
205
206 /* root: mknod(2) needs it to create something other than a file */
207 SAFE_MKNOD(FIFO, S_IFIFO | 0777, 0);
208 SAFE_MKNOD(CHR, S_IFCHR | 0777, dev);
209 SAFE_MKNOD(BLK, S_IFBLK | 0777, dev);
210
211 for (i = 0; i < ARRAY_SIZE(tc); i++) {
212
213 if (!tc[i].issocket) {
214 tc[i].fd = SAFE_OPEN(tc[i].fname, tc[i].fflags, NULL);
215 continue;
216 }
217
218 /* differently than setxattr calls, when dealing with
219 * sockets, mknod() isn't enough to test fsetxattr(2).
220 * we have to get a real unix socket in order for open()
221 * to get a file desc.
222 */
223 tc[i].fd = SAFE_SOCKET(AF_UNIX, SOCK_STREAM, 0);
224
225 memset(&sun, 0, sizeof(struct sockaddr_un));
226 sun.sun_family = AF_UNIX;
227 strncpy(sun.sun_path, tc[i].fname, sizeof(sun.sun_path) - 1);
228
229 SAFE_BIND(tc[i].fd, (const struct sockaddr *) &sun,
230 sizeof(struct sockaddr_un));
231 }
232 }
233
cleanup(void)234 static void cleanup(void)
235 {
236 size_t i = 0;
237
238 for (i = 0; i < ARRAY_SIZE(tc); i++) {
239 if (tc[i].fd > 0)
240 SAFE_CLOSE(tc[i].fd);
241 }
242 }
243
244 static struct tst_test test = {
245 .setup = setup,
246 .test = verify_fsetxattr,
247 .cleanup = cleanup,
248 .tcnt = ARRAY_SIZE(tc),
249 .needs_devfs = 1,
250 .mntpoint = MNTPOINT,
251 .needs_root = 1,
252 .needs_drivers = (const char *const[]) {
253 "brd",
254 NULL,
255 },
256 };
257
258 #else /* HAVE_SYS_XATTR_H */
259 TST_TEST_TCONF("<sys/xattr.h> does not exist");
260 #endif
261