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