/* * feature.c --- convert between features and strings * * Copyright (C) 1999 Theodore Ts'o <tytso@mit.edu> * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include "config.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <errno.h> #include "e2p.h" #include <ext2fs/ext2fs.h> #include <ext2fs/kernel-jbd.h> struct feature { int compat; unsigned int mask; const char *string; }; static struct feature feature_list[] = { { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_PREALLOC, "dir_prealloc" }, { E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL, "has_journal" }, { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_IMAGIC_INODES, "imagic_inodes" }, { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXT_ATTR, "ext_attr" }, { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX, "dir_index" }, { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_RESIZE_INODE, "resize_inode" }, { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_LAZY_BG, "lazy_bg" }, { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP, "snapshot_bitmap" }, { E2P_FEATURE_COMPAT, EXT4_FEATURE_COMPAT_SPARSE_SUPER2, "sparse_super2" }, { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER, "sparse_super" }, { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_LARGE_FILE, "large_file" }, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_HUGE_FILE, "huge_file" }, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_GDT_CSUM, "uninit_bg" }, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_GDT_CSUM, "uninit_groups" }, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_DIR_NLINK, "dir_nlink" }, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE, "extra_isize" }, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_QUOTA, "quota" }, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_BIGALLOC, "bigalloc"}, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM, "metadata_csum"}, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_REPLICA, "replica" }, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_READONLY, "read-only" }, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_PROJECT, "project"}, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS, "shared_blocks"}, { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_VERITY, "verity"}, { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION, "compression" }, { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_FILETYPE, "filetype" }, { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_RECOVER, "needs_recovery" }, { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_JOURNAL_DEV, "journal_dev" }, { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_EXTENTS, "extent" }, { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_EXTENTS, "extents" }, { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_META_BG, "meta_bg" }, { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_64BIT, "64bit" }, { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_MMP, "mmp" }, { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_FLEX_BG, "flex_bg"}, { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_EA_INODE, "ea_inode"}, { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_DIRDATA, "dirdata"}, { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_CSUM_SEED, "metadata_csum_seed"}, { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_LARGEDIR, "large_dir"}, { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_INLINE_DATA, "inline_data"}, { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_ENCRYPT, "encrypt"}, { 0, 0, 0 }, }; static struct feature jrnl_feature_list[] = { { E2P_FEATURE_COMPAT, JFS_FEATURE_COMPAT_CHECKSUM, "journal_checksum" }, { E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_REVOKE, "journal_incompat_revoke" }, { E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_64BIT, "journal_64bit" }, { E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_ASYNC_COMMIT, "journal_async_commit" }, { E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_CSUM_V2, "journal_checksum_v2" }, { E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_CSUM_V3, "journal_checksum_v3" }, { 0, 0, 0 }, }; const char *e2p_feature2string(int compat, unsigned int mask) { struct feature *f; static char buf[20]; char fchar; int fnum; for (f = feature_list; f->string; f++) { if ((compat == f->compat) && (mask == f->mask)) return f->string; } switch (compat) { case E2P_FEATURE_COMPAT: fchar = 'C'; break; case E2P_FEATURE_INCOMPAT: fchar = 'I'; break; case E2P_FEATURE_RO_INCOMPAT: fchar = 'R'; break; default: fchar = '?'; break; } for (fnum = 0; mask >>= 1; fnum++); sprintf(buf, "FEATURE_%c%d", fchar, fnum); return buf; } int e2p_string2feature(char *string, int *compat_type, unsigned int *mask) { struct feature *f; char *eptr; int num; for (f = feature_list; f->string; f++) { if (!strcasecmp(string, f->string)) { *compat_type = f->compat; *mask = f->mask; return 0; } } if (strncasecmp(string, "FEATURE_", 8)) return 1; switch (string[8]) { case 'c': case 'C': *compat_type = E2P_FEATURE_COMPAT; break; case 'i': case 'I': *compat_type = E2P_FEATURE_INCOMPAT; break; case 'r': case 'R': *compat_type = E2P_FEATURE_RO_INCOMPAT; break; default: return 1; } if (string[9] == 0) return 1; num = strtol(string+9, &eptr, 10); if (num > 31 || num < 0) return 1; if (*eptr) return 1; *mask = 1 << num; return 0; } const char *e2p_jrnl_feature2string(int compat, unsigned int mask) { struct feature *f; static char buf[20]; char fchar; int fnum; for (f = jrnl_feature_list; f->string; f++) { if ((compat == f->compat) && (mask == f->mask)) return f->string; } switch (compat) { case E2P_FEATURE_COMPAT: fchar = 'C'; break; case E2P_FEATURE_INCOMPAT: fchar = 'I'; break; case E2P_FEATURE_RO_INCOMPAT: fchar = 'R'; break; default: fchar = '?'; break; } for (fnum = 0; mask >>= 1; fnum++); sprintf(buf, "FEATURE_%c%d", fchar, fnum); return buf; } int e2p_jrnl_string2feature(char *string, int *compat_type, unsigned int *mask) { struct feature *f; char *eptr; int num; for (f = jrnl_feature_list; f->string; f++) { if (!strcasecmp(string, f->string)) { *compat_type = f->compat; *mask = f->mask; return 0; } } if (strncasecmp(string, "FEATURE_", 8)) return 1; switch (string[8]) { case 'c': case 'C': *compat_type = E2P_FEATURE_COMPAT; break; case 'i': case 'I': *compat_type = E2P_FEATURE_INCOMPAT; break; case 'r': case 'R': *compat_type = E2P_FEATURE_RO_INCOMPAT; break; default: return 1; } if (string[9] == 0) return 1; num = strtol(string+9, &eptr, 10); if (num > 31 || num < 0) return 1; if (*eptr) return 1; *mask = 1 << num; return 0; } static char *skip_over_blanks(char *cp) { while (*cp && isspace(*cp)) cp++; return cp; } static char *skip_over_word(char *cp) { while (*cp && !isspace(*cp) && *cp != ',') cp++; return cp; } /* * Edit a feature set array as requested by the user. The ok_array, * if set, allows the application to limit what features the user is * allowed to set or clear using this function. If clear_ok_array is set, * then use it tell whether or not it is OK to clear a filesystem feature. */ int e2p_edit_feature2(const char *str, __u32 *compat_array, __u32 *ok_array, __u32 *clear_ok_array, int *type_err, unsigned int *mask_err) { char *cp, *buf, *next; int neg; unsigned int mask; int compat_type; int rc = 0; if (!clear_ok_array) clear_ok_array = ok_array; if (type_err) *type_err = 0; if (mask_err) *mask_err = 0; buf = malloc(strlen(str)+1); if (!buf) return 1; strcpy(buf, str); for (cp = buf; cp && *cp; cp = next ? next+1 : 0) { neg = 0; cp = skip_over_blanks(cp); next = skip_over_word(cp); if (*next == 0) next = 0; else *next = 0; if ((strcasecmp(cp, "none") == 0) || (strcasecmp(cp, "clear") == 0)) { compat_array[0] = 0; compat_array[1] = 0; compat_array[2] = 0; continue; } switch (*cp) { case '-': case '^': neg++; /* fallthrough */ case '+': cp++; break; } if (e2p_string2feature(cp, &compat_type, &mask)) { rc = 1; break; } if (neg) { if (clear_ok_array && !(clear_ok_array[compat_type] & mask)) { rc = 1; if (type_err) *type_err = (compat_type | E2P_FEATURE_NEGATE_FLAG); if (mask_err) *mask_err = mask; break; } compat_array[compat_type] &= ~mask; } else { if (ok_array && !(ok_array[compat_type] & mask)) { rc = 1; if (type_err) *type_err = compat_type; if (mask_err) *mask_err = mask; break; } compat_array[compat_type] |= mask; } } free(buf); return rc; } int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array) { return e2p_edit_feature2(str, compat_array, ok_array, 0, 0, 0); } #ifdef TEST_PROGRAM int main(int argc, char **argv) { int compat, compat2, i; unsigned int mask, mask2; const char *str; struct feature *f; for (i = 0; i < 2; i++) { if (i == 0) { f = feature_list; printf("Feature list:\n"); } else { printf("\nJournal feature list:\n"); f = jrnl_feature_list; } for (; f->string; f++) { if (i == 0) { e2p_string2feature((char *)f->string, &compat, &mask); str = e2p_feature2string(compat, mask); } else { e2p_jrnl_string2feature((char *)f->string, &compat, &mask); str = e2p_jrnl_feature2string(compat, mask); } printf("\tCompat = %d, Mask = %u, %s\n", compat, mask, f->string); if (strcmp(f->string, str)) { if (e2p_string2feature((char *) str, &compat2, &mask2) || (compat2 != compat) || (mask2 != mask)) { fprintf(stderr, "Failure!\n"); exit(1); } } } } exit(0); } #endif