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