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