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