1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2011 Red Hat, Inc.
4  */
5 
6 /*
7  * In the user.* namespace, only regular files and directories can
8  * have extended attributes. Otherwise setxattr(2) will return -1
9  * and set errno to EPERM.
10  *
11  * There are 7 test cases:
12  * 1. Set attribute to a regular file, setxattr(2) should succeed
13  * 2. Set attribute to a directory, setxattr(2) should succeed
14  * 3. Set attribute to a symlink which points to the regular file,
15  *    setxattr(2) should return -1 and set errno to EEXIST
16  * 4. Set attribute to a FIFO, setxattr(2) should return -1 and set
17  *    errno to EPERM
18  * 5. Set attribute to a char special file, setxattr(2) should
19  *    return -1 and set errno to EPERM
20  * 6. Set attribute to a block special file, setxattr(2) should
21  *    return -1 and set errno to EPERM
22  * 7. Set attribute to a UNIX domain socket, setxattr(2) should
23  *    return -1 and set errno to EPERM
24  */
25 
26 #include "config.h"
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/sysmacros.h>
30 #include <sys/wait.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <signal.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #ifdef HAVE_SYS_XATTR_H
39 # include <sys/xattr.h>
40 #endif
41 #include "tst_test.h"
42 
43 #ifdef HAVE_SYS_XATTR_H
44 #define XATTR_TEST_KEY "user.testkey"
45 #define XATTR_TEST_VALUE "this is a test value"
46 #define XATTR_TEST_VALUE_SIZE 20
47 
48 #define OFFSET    10
49 #define FILENAME "setxattr02testfile"
50 #define DIRNAME  "setxattr02testdir"
51 #define SYMLINK  "setxattr02symlink"
52 #define FIFO     "setxattr02fifo"
53 #define CHR      "setxattr02chr"
54 #define BLK      "setxattr02blk"
55 #define SOCK     "setxattr02sock"
56 
57 struct test_case {
58 	char *fname;
59 	char *key;
60 	char *value;
61 	size_t size;
62 	int flags;
63 	int exp_err;
64 	int needskeyset;
65 };
66 static struct test_case tc[] = {
67 	{			/* case 00, set attr to reg */
68 	 .fname = FILENAME,
69 	 .key = XATTR_TEST_KEY,
70 	 .value = XATTR_TEST_VALUE,
71 	 .size = XATTR_TEST_VALUE_SIZE,
72 	 .flags = XATTR_CREATE,
73 	 .exp_err = 0,
74 	 },
75 	{			/* case 01, set attr to dir */
76 	 .fname = DIRNAME,
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 02, set attr to symlink */
84 	 .fname = SYMLINK,
85 	 .key = XATTR_TEST_KEY,
86 	 .value = XATTR_TEST_VALUE,
87 	 .size = XATTR_TEST_VALUE_SIZE,
88 	 .flags = XATTR_CREATE,
89 	 .exp_err = EEXIST,
90 	 .needskeyset = 1,
91 	 },
92 	{			/* case 03, set attr to fifo */
93 	 .fname = FIFO,
94 	 .key = XATTR_TEST_KEY,
95 	 .value = XATTR_TEST_VALUE,
96 	 .size = XATTR_TEST_VALUE_SIZE,
97 	 .flags = XATTR_CREATE,
98 	 .exp_err = EPERM,
99 	 },
100 	{			/* case 04, set attr to character special */
101 	 .fname = CHR,
102 	 .key = XATTR_TEST_KEY,
103 	 .value = XATTR_TEST_VALUE,
104 	 .size = XATTR_TEST_VALUE_SIZE,
105 	 .flags = XATTR_CREATE,
106 	 .exp_err = EPERM,
107 	 },
108 	{			/* case 05, set attr to block special */
109 	 .fname = BLK,
110 	 .key = XATTR_TEST_KEY,
111 	 .value = XATTR_TEST_VALUE,
112 	 .size = XATTR_TEST_VALUE_SIZE,
113 	 .flags = XATTR_CREATE,
114 	 .exp_err = EPERM,
115 	 },
116 	{			/* case 06, set attr to socket */
117 	 .fname = SOCK,
118 	 .key = XATTR_TEST_KEY,
119 	 .value = XATTR_TEST_VALUE,
120 	 .size = XATTR_TEST_VALUE_SIZE,
121 	 .flags = XATTR_CREATE,
122 	 .exp_err = EPERM,
123 	 },
124 };
125 
verify_setxattr(unsigned int i)126 static void verify_setxattr(unsigned int i)
127 {
128 	/* some tests might require existing keys for each iteration */
129 	if (tc[i].needskeyset) {
130 		SAFE_SETXATTR(tc[i].fname, tc[i].key, tc[i].value, tc[i].size,
131 				XATTR_CREATE);
132 	}
133 
134 	TEST(setxattr(tc[i].fname, tc[i].key, tc[i].value, tc[i].size,
135 			tc[i].flags));
136 
137 	if (TST_RET == -1 && TST_ERR == EOPNOTSUPP)
138 		tst_brk(TCONF, "setxattr(2) not supported");
139 
140 	/* success */
141 
142 	if (!tc[i].exp_err) {
143 		if (TST_RET) {
144 			tst_res(TFAIL | TTERRNO,
145 				"setxattr(2) on %s failed with %li",
146 				tc[i].fname + OFFSET, TST_RET);
147 			return;
148 		}
149 
150 		/* this is needed for subsequent iterations */
151 		SAFE_REMOVEXATTR(tc[i].fname, tc[i].key);
152 
153 		tst_res(TPASS, "setxattr(2) on %s passed",
154 				tc[i].fname + OFFSET);
155 		return;
156 	}
157 
158 	if (TST_RET == 0) {
159 		tst_res(TFAIL, "setxattr(2) on %s passed unexpectedly",
160 				tc[i].fname + OFFSET);
161 		return;
162 	}
163 
164 	/* fail */
165 
166 	if (tc[i].exp_err != TST_ERR) {
167 		tst_res(TFAIL | TTERRNO,
168 				"setxattr(2) on %s should have failed with %s",
169 				tc[i].fname + OFFSET,
170 				tst_strerrno(tc[i].exp_err));
171 		return;
172 	}
173 
174 	/* key might have been added AND test might have failed, remove it */
175 	if (tc[i].needskeyset)
176 		SAFE_REMOVEXATTR(tc[i].fname, tc[i].key);
177 
178 	tst_res(TPASS | TTERRNO, "setxattr(2) on %s failed",
179 			tc[i].fname + OFFSET);
180 }
181 
setup(void)182 static void setup(void)
183 {
184 	dev_t dev = makedev(1, 3);
185 
186 	SAFE_TOUCH(FILENAME, 0644, NULL);
187 	SAFE_MKDIR(DIRNAME, 0644);
188 	SAFE_SYMLINK(FILENAME, SYMLINK);
189 	SAFE_MKNOD(FIFO, S_IFIFO | 0777, 0);
190 	SAFE_MKNOD(CHR, S_IFCHR | 0777, dev);
191 	SAFE_MKNOD(BLK, S_IFBLK | 0777, 0);
192 	SAFE_MKNOD(SOCK, S_IFSOCK | 0777, 0);
193 }
194 
195 static struct tst_test test = {
196 	.setup = setup,
197 	.test = verify_setxattr,
198 	.tcnt = ARRAY_SIZE(tc),
199 	.needs_tmpdir = 1,
200 	.needs_root = 1,
201 };
202 
203 #else /* HAVE_SYS_XATTR_H */
204 TST_TEST_TCONF("<sys/xattr.h> does not exist");
205 #endif
206