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