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