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