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