1 /*
2  * Copyright (C) 2012 Red Hat, Inc.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of version 2 of the GNU General Public
6  * License as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it would be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  *
12  * Further, this software is distributed without any warranty that it
13  * is free of the rightful claim of any third person regarding
14  * infringement or the like.  Any license provided herein, whether
15  * implied or otherwise, applies only to this software file.  Patent
16  * licenses, if any, provided herein do not apply to combinations of
17  * this program with other software, or any other product whatsoever.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301, USA.
23  */
24 
25 /*
26  * setxattr(2) to immutable and append-only files should get EPERM
27  *
28  * There are 2 test cases:
29  * 1. Set attribute to a immutable file, setxattr(2) should return -1
30  *    and set errno to EPERM
31  * 2. Set attribute to a append-only file, setxattr(2) should return
32  *    -1 and set errno to EPERM
33  */
34 
35 #include "config.h"
36 #include <sys/ioctl.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <signal.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #ifdef HAVE_SYS_XATTR_H
48 # include <sys/xattr.h>
49 #endif
50 #include <linux/fs.h>
51 
52 #include "test.h"
53 #include "safe_macros.h"
54 
55 char *TCID = "setxattr03";
56 
57 #if defined HAVE_SYS_XATTR_H && defined HAVE_FS_IOC_FLAGS
58 #define XATTR_TEST_KEY "user.testkey"
59 #define XATTR_TEST_VALUE "this is a test value"
60 #define XATTR_TEST_VALUE_SIZE (sizeof(XATTR_TEST_VALUE) - 1)
61 
62 #define IMMU_FILE "setxattr03immutable"
63 #define APPEND_FILE  "setxattr03appendonly"
64 
65 #define set_immutable_on(fd) fsetflag(fd, 1, 1)
66 #define set_immutable_off(fd) fsetflag(fd, 0, 1)
67 #define set_append_on(fd) fsetflag(fd, 1, 0)
68 #define set_append_off(fd) fsetflag(fd, 0, 0)
69 
70 struct test_case {
71 	char *desc;
72 	char *fname;
73 	char *key;
74 	char *value;
75 	size_t size;
76 	int flags;
77 	int exp_err;
78 };
79 static struct test_case tc[] = {
80 	{			/* case 00, set attr to immutable file */
81 	 .desc = "Set attr to immutable file",
82 	 .fname = IMMU_FILE,
83 	 .key = XATTR_TEST_KEY,
84 	 .value = XATTR_TEST_VALUE,
85 	 .size = XATTR_TEST_VALUE_SIZE,
86 	 .flags = XATTR_CREATE,
87 	 .exp_err = EPERM,
88 	 },
89 	{			/* case 01, set attr to append-only file */
90 	 .desc = "Set attr to append-only file",
91 	 .fname = APPEND_FILE,
92 	 .key = XATTR_TEST_KEY,
93 	 .value = XATTR_TEST_VALUE,
94 	 .size = XATTR_TEST_VALUE_SIZE,
95 	 .flags = XATTR_CREATE,
96 	 .exp_err = EPERM,
97 	 },
98 };
99 
100 static void setup(void);
101 static void cleanup(void);
102 
103 static int immu_fd;
104 static int append_fd;
105 
106 int TST_TOTAL = sizeof(tc) / sizeof(tc[0]);
107 
108 int main(int argc, char *argv[])
109 {
110 	int lc;
111 	int i;
112 
113 	tst_parse_opts(argc, argv, NULL, NULL);
114 
115 	setup();
116 
117 	for (lc = 0; TEST_LOOPING(lc); lc++) {
118 		tst_count = 0;
119 
120 		for (i = 0; i < TST_TOTAL; i++) {
121 			TEST(setxattr(tc[i].fname, tc[i].key, tc[i].value,
122 				      tc[i].size, tc[i].flags));
123 
124 			if (TEST_ERRNO == tc[i].exp_err) {
125 				tst_resm(TPASS | TTERRNO, "%s", tc[i].desc);
126 			} else {
127 				tst_resm(TFAIL | TTERRNO, "%s - expected errno"
128 					 " %d - Got", tc[i].desc,
129 					 tc[i].exp_err);
130 			}
131 		}
132 	}
133 
134 	cleanup();
135 	tst_exit();
136 }
137 
138 static int fsetflag(int fd, int on, int immutable)
139 {
140 	int fsflags = 0;
141 	int fsfl;
142 
143 	if (ioctl(fd, FS_IOC_GETFLAGS, &fsflags) < 0)
144 		return 1;
145 
146 	if (immutable)
147 		fsfl = FS_IMMUTABLE_FL;
148 	else
149 		fsfl = FS_APPEND_FL;
150 
151 	if (on)
152 		fsflags |= fsfl;
153 	else
154 		fsflags &= ~fsfl;
155 
156 	if (ioctl(fd, FS_IOC_SETFLAGS, &fsflags) < 0)
157 		return 1;
158 
159 	return 0;
160 }
161 
162 static void setup(void)
163 {
164 	int fd;
165 
166 	tst_require_root();
167 
168 	tst_tmpdir();
169 
170 	/* Test for xattr support */
171 	fd = SAFE_CREAT(cleanup, "testfile", 0644);
172 	close(fd);
173 	if (setxattr("testfile", "user.test", "test", 4, XATTR_CREATE) == -1)
174 		if (errno == ENOTSUP)
175 			tst_brkm(TCONF, cleanup, "No xattr support in fs or "
176 				 "fs mounted without user_xattr option");
177 	unlink("testfile");
178 
179 	/* Create test files and set file immutable or append-only */
180 	immu_fd = SAFE_CREAT(cleanup, IMMU_FILE, 0644);
181 	if (set_immutable_on(immu_fd))
182 		tst_brkm(TBROK | TERRNO, cleanup, "Set %s immutable failed",
183 			 IMMU_FILE);
184 
185 	append_fd = SAFE_CREAT(cleanup, APPEND_FILE, 0644);
186 	if (set_append_on(append_fd))
187 		tst_brkm(TBROK | TERRNO, cleanup, "Set %s append-only failed",
188 			 APPEND_FILE);
189 
190 	TEST_PAUSE;
191 }
192 
193 static void cleanup(void)
194 {
195 	if ((immu_fd > 0) && set_immutable_off(immu_fd))
196 		tst_resm(TWARN | TERRNO, "Unset %s immutable failed",
197 			 IMMU_FILE);
198 	if ((append_fd > 0) && set_append_off(append_fd))
199 		tst_resm(TWARN | TERRNO, "Unset %s append-only failed",
200 			 APPEND_FILE);
201 	close(immu_fd);
202 	close(append_fd);
203 
204 	tst_rmdir();
205 }
206 #else
207 int main(void)
208 {
209 	tst_brkm(TCONF, NULL, "<sys/xattr.h> not present or FS_IOC_FLAGS "
210 		 "missing in <linux/fs.h>");
211 }
212 #endif /* defined HAVE_SYS_XATTR_H && defined HAVE_FS_IOC_FLAGS */
213