1 /*
2  * feature.c --- convert between features and strings
3  *
4  * Copyright (C) 1999  Theodore Ts'o <tytso@mit.edu>
5  *
6  * %Begin-Header%
7  * This file may be redistributed under the terms of the GNU Library
8  * General Public License, version 2.
9  * %End-Header%
10  */
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <errno.h>
17 
18 #include "e2p.h"
19 #include <ext2fs/ext2fs.h>
20 #include <ext2fs/jfs_user.h>
21 
22 struct feature {
23 	int		compat;
24 	unsigned int	mask;
25 	const char	*string;
26 };
27 
28 static struct feature feature_list[] = {
29 	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_PREALLOC,
30 			"dir_prealloc" },
31 	{	E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL,
32 			"has_journal" },
33 	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_IMAGIC_INODES,
34 			"imagic_inodes" },
35 	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXT_ATTR,
36 			"ext_attr" },
37 	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX,
38 			"dir_index" },
39 	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_RESIZE_INODE,
40 			"resize_inode" },
41 	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_LAZY_BG,
42 			"lazy_bg" },
43 	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP,
44 			"snapshot_bitmap" },
45 
46 	{	E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER,
47 			"sparse_super" },
48 	{	E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_LARGE_FILE,
49 			"large_file" },
50 	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_HUGE_FILE,
51 			"huge_file" },
52 	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_GDT_CSUM,
53 			"uninit_bg" },
54 	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_GDT_CSUM,
55 			"uninit_groups" },
56 	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_DIR_NLINK,
57 			"dir_nlink" },
58 	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE,
59 			"extra_isize" },
60 	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_QUOTA,
61 			"quota" },
62 	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_BIGALLOC,
63 			"bigalloc"},
64 	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM,
65 			"metadata_csum"},
66 	{	E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_REPLICA,
67 			"replica" },
68 
69 	{	E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION,
70 			"compression" },
71 	{	E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_FILETYPE,
72 			"filetype" },
73 	{	E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_RECOVER,
74 			"needs_recovery" },
75 	{	E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_JOURNAL_DEV,
76 			"journal_dev" },
77 	{	E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_EXTENTS,
78 			"extent" },
79 	{	E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_EXTENTS,
80 			"extents" },
81 	{	E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_META_BG,
82 			"meta_bg" },
83 	{	E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_64BIT,
84 			"64bit" },
85 	{       E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_MMP,
86 			"mmp" },
87 	{       E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_FLEX_BG,
88 			"flex_bg"},
89 	{       E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_EA_INODE,
90 			"ea_inode"},
91 	{       E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_DIRDATA,
92 			"dirdata"},
93 	{       E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_LARGEDIR,
94 			"large_dir"},
95 	{       E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_INLINEDATA,
96 			"inline_data"},
97 	{	0, 0, 0 },
98 };
99 
100 static struct feature jrnl_feature_list[] = {
101        {       E2P_FEATURE_COMPAT, JFS_FEATURE_COMPAT_CHECKSUM,
102                        "journal_checksum" },
103 
104        {       E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_REVOKE,
105                        "journal_incompat_revoke" },
106        {       E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_64BIT,
107                        "journal_64bit" },
108        {       E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_ASYNC_COMMIT,
109                        "journal_async_commit" },
110        {       0, 0, 0 },
111 };
112 
e2p_feature2string(int compat,unsigned int mask)113 const char *e2p_feature2string(int compat, unsigned int mask)
114 {
115 	struct feature  *f;
116 	static char buf[20];
117 	char	fchar;
118 	int	fnum;
119 
120 	for (f = feature_list; f->string; f++) {
121 		if ((compat == f->compat) &&
122 		    (mask == f->mask))
123 			return f->string;
124 	}
125 	switch (compat) {
126 	case  E2P_FEATURE_COMPAT:
127 		fchar = 'C';
128 		break;
129 	case E2P_FEATURE_INCOMPAT:
130 		fchar = 'I';
131 		break;
132 	case E2P_FEATURE_RO_INCOMPAT:
133 		fchar = 'R';
134 		break;
135 	default:
136 		fchar = '?';
137 		break;
138 	}
139 	for (fnum = 0; mask >>= 1; fnum++);
140 	sprintf(buf, "FEATURE_%c%d", fchar, fnum);
141 	return buf;
142 }
143 
e2p_string2feature(char * string,int * compat_type,unsigned int * mask)144 int e2p_string2feature(char *string, int *compat_type, unsigned int *mask)
145 {
146 	struct feature  *f;
147 	char		*eptr;
148 	int		num;
149 
150 	for (f = feature_list; f->string; f++) {
151 		if (!strcasecmp(string, f->string)) {
152 			*compat_type = f->compat;
153 			*mask = f->mask;
154 			return 0;
155 		}
156 	}
157 	if (strncasecmp(string, "FEATURE_", 8))
158 		return 1;
159 
160 	switch (string[8]) {
161 	case 'c':
162 	case 'C':
163 		*compat_type = E2P_FEATURE_COMPAT;
164 		break;
165 	case 'i':
166 	case 'I':
167 		*compat_type = E2P_FEATURE_INCOMPAT;
168 		break;
169 	case 'r':
170 	case 'R':
171 		*compat_type = E2P_FEATURE_RO_INCOMPAT;
172 		break;
173 	default:
174 		return 1;
175 	}
176 	if (string[9] == 0)
177 		return 1;
178 	num = strtol(string+9, &eptr, 10);
179 	if (num > 32 || num < 0)
180 		return 1;
181 	if (*eptr)
182 		return 1;
183 	*mask = 1 << num;
184 	return 0;
185 }
186 
e2p_jrnl_feature2string(int compat,unsigned int mask)187 const char *e2p_jrnl_feature2string(int compat, unsigned int mask)
188 {
189 	struct feature  *f;
190 	static char buf[20];
191 	char	fchar;
192 	int	fnum;
193 
194 	for (f = jrnl_feature_list; f->string; f++) {
195 		if ((compat == f->compat) &&
196 		    (mask == f->mask))
197 			return f->string;
198 	}
199 	switch (compat) {
200 	case  E2P_FEATURE_COMPAT:
201 		fchar = 'C';
202 		break;
203 	case E2P_FEATURE_INCOMPAT:
204 		fchar = 'I';
205 		break;
206 	case E2P_FEATURE_RO_INCOMPAT:
207 		fchar = 'R';
208 		break;
209 	default:
210 		fchar = '?';
211 		break;
212 	}
213 	for (fnum = 0; mask >>= 1; fnum++);
214 	sprintf(buf, "FEATURE_%c%d", fchar, fnum);
215 	return buf;
216 }
217 
e2p_jrnl_string2feature(char * string,int * compat_type,unsigned int * mask)218 int e2p_jrnl_string2feature(char *string, int *compat_type, unsigned int *mask)
219 {
220 	struct feature  *f;
221 	char		*eptr;
222 	int		num;
223 
224 	for (f = jrnl_feature_list; f->string; f++) {
225 		if (!strcasecmp(string, f->string)) {
226 			*compat_type = f->compat;
227 			*mask = f->mask;
228 			return 0;
229 		}
230 	}
231 	if (strncasecmp(string, "FEATURE_", 8))
232 		return 1;
233 
234 	switch (string[8]) {
235 	case 'c':
236 	case 'C':
237 		*compat_type = E2P_FEATURE_COMPAT;
238 		break;
239 	case 'i':
240 	case 'I':
241 		*compat_type = E2P_FEATURE_INCOMPAT;
242 		break;
243 	case 'r':
244 	case 'R':
245 		*compat_type = E2P_FEATURE_RO_INCOMPAT;
246 		break;
247 	default:
248 		return 1;
249 	}
250 	if (string[9] == 0)
251 		return 1;
252 	num = strtol(string+9, &eptr, 10);
253 	if (num > 32 || num < 0)
254 		return 1;
255 	if (*eptr)
256 		return 1;
257 	*mask = 1 << num;
258 	return 0;
259 }
skip_over_blanks(char * cp)260 static char *skip_over_blanks(char *cp)
261 {
262 	while (*cp && isspace(*cp))
263 		cp++;
264 	return cp;
265 }
266 
skip_over_word(char * cp)267 static char *skip_over_word(char *cp)
268 {
269 	while (*cp && !isspace(*cp) && *cp != ',')
270 		cp++;
271 	return cp;
272 }
273 
274 /*
275  * Edit a feature set array as requested by the user.  The ok_array,
276  * if set, allows the application to limit what features the user is
277  * allowed to set or clear using this function.  If clear_ok_array is set,
278  * then use it tell whether or not it is OK to clear a filesystem feature.
279  */
e2p_edit_feature2(const char * str,__u32 * compat_array,__u32 * ok_array,__u32 * clear_ok_array,int * type_err,unsigned int * mask_err)280 int e2p_edit_feature2(const char *str, __u32 *compat_array, __u32 *ok_array,
281 		      __u32 *clear_ok_array, int *type_err,
282 		      unsigned int *mask_err)
283 {
284 	char		*cp, *buf, *next;
285 	int		neg;
286 	unsigned int	mask;
287 	int		compat_type;
288 	int		rc = 0;
289 
290 	if (!clear_ok_array)
291 		clear_ok_array = ok_array;
292 
293 	if (type_err)
294 		*type_err = 0;
295 	if (mask_err)
296 		*mask_err = 0;
297 
298 	buf = malloc(strlen(str)+1);
299 	if (!buf)
300 		return 1;
301 	strcpy(buf, str);
302 	for (cp = buf; cp && *cp; cp = next ? next+1 : 0) {
303 		neg = 0;
304 		cp = skip_over_blanks(cp);
305 		next = skip_over_word(cp);
306 
307 		if (*next == 0)
308 			next = 0;
309 		else
310 			*next = 0;
311 
312 		if ((strcasecmp(cp, "none") == 0) ||
313 		    (strcasecmp(cp, "clear") == 0)) {
314 			compat_array[0] = 0;
315 			compat_array[1] = 0;
316 			compat_array[2] = 0;
317 			continue;
318 		}
319 
320 		switch (*cp) {
321 		case '-':
322 		case '^':
323 			neg++;
324 			/* fallthrough */
325 		case '+':
326 			cp++;
327 			break;
328 		}
329 		if (e2p_string2feature(cp, &compat_type, &mask)) {
330 			rc = 1;
331 			break;
332 		}
333 		if (neg) {
334 			if (clear_ok_array &&
335 			    !(clear_ok_array[compat_type] & mask)) {
336 				rc = 1;
337 				if (type_err)
338 					*type_err = (compat_type |
339 						     E2P_FEATURE_NEGATE_FLAG);
340 				if (mask_err)
341 					*mask_err = mask;
342 				break;
343 			}
344 			compat_array[compat_type] &= ~mask;
345 		} else {
346 			if (ok_array && !(ok_array[compat_type] & mask)) {
347 				rc = 1;
348 				if (type_err)
349 					*type_err = compat_type;
350 				if (mask_err)
351 					*mask_err = mask;
352 				break;
353 			}
354 			compat_array[compat_type] |= mask;
355 		}
356 	}
357 	free(buf);
358 	return rc;
359 }
360 
e2p_edit_feature(const char * str,__u32 * compat_array,__u32 * ok_array)361 int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array)
362 {
363 	return e2p_edit_feature2(str, compat_array, ok_array, 0, 0, 0);
364 }
365 
366 #ifdef TEST_PROGRAM
main(int argc,char ** argv)367 int main(int argc, char **argv)
368 {
369 	int compat, compat2, i;
370 	unsigned int mask, mask2;
371 	const char *str;
372 	struct feature *f;
373 
374 	for (i = 0; i < 2; i++) {
375 		if (i == 0) {
376 			f = feature_list;
377 			printf("Feature list:\n");
378 		} else {
379 			printf("\nJournal feature list:\n");
380 			f = jrnl_feature_list;
381 		}
382 		for (; f->string; f++) {
383 			if (i == 0) {
384 				e2p_string2feature((char *)f->string, &compat,
385 						   &mask);
386 				str = e2p_feature2string(compat, mask);
387 			} else {
388 				e2p_jrnl_string2feature((char *)f->string,
389 							&compat, &mask);
390 				str = e2p_jrnl_feature2string(compat, mask);
391 			}
392 
393 			printf("\tCompat = %d, Mask = %u, %s\n",
394 			       compat, mask, f->string);
395 			if (strcmp(f->string, str)) {
396 				if (e2p_string2feature((char *) str, &compat2,
397 						       &mask2) ||
398 				    (compat2 != compat) ||
399 				    (mask2 != mask)) {
400 					fprintf(stderr, "Failure!\n");
401 					exit(1);
402 				}
403 			}
404 		}
405 	}
406 	exit(0);
407 }
408 #endif
409