• 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 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