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 { 0, 0, 0 }, 98 }; 99 100 static struct feature jrnl_feature_list[] = { 101 { E2P_FEATURE_COMPAT, JFS_FEATURE_COMPAT_CHECKSUM, 102 "journal_checksum" }, 103 104 { E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_REVOKE, 105 "journal_incompat_revoke" }, 106 { E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_64BIT, 107 "journal_64bit" }, 108 { E2P_FEATURE_INCOMPAT, JFS_FEATURE_INCOMPAT_ASYNC_COMMIT, 109 "journal_async_commit" }, 110 { 0, 0, 0 }, 111 }; 112 113 const char *e2p_feature2string(int compat, unsigned int mask) 114 { 115 struct feature *f; 116 static char buf[20]; 117 char fchar; 118 int fnum; 119 120 for (f = feature_list; f->string; f++) { 121 if ((compat == f->compat) && 122 (mask == f->mask)) 123 return f->string; 124 } 125 switch (compat) { 126 case E2P_FEATURE_COMPAT: 127 fchar = 'C'; 128 break; 129 case E2P_FEATURE_INCOMPAT: 130 fchar = 'I'; 131 break; 132 case E2P_FEATURE_RO_INCOMPAT: 133 fchar = 'R'; 134 break; 135 default: 136 fchar = '?'; 137 break; 138 } 139 for (fnum = 0; mask >>= 1; fnum++); 140 sprintf(buf, "FEATURE_%c%d", fchar, fnum); 141 return buf; 142 } 143 144 int e2p_string2feature(char *string, int *compat_type, unsigned int *mask) 145 { 146 struct feature *f; 147 char *eptr; 148 int num; 149 150 for (f = feature_list; f->string; f++) { 151 if (!strcasecmp(string, f->string)) { 152 *compat_type = f->compat; 153 *mask = f->mask; 154 return 0; 155 } 156 } 157 if (strncasecmp(string, "FEATURE_", 8)) 158 return 1; 159 160 switch (string[8]) { 161 case 'c': 162 case 'C': 163 *compat_type = E2P_FEATURE_COMPAT; 164 break; 165 case 'i': 166 case 'I': 167 *compat_type = E2P_FEATURE_INCOMPAT; 168 break; 169 case 'r': 170 case 'R': 171 *compat_type = E2P_FEATURE_RO_INCOMPAT; 172 break; 173 default: 174 return 1; 175 } 176 if (string[9] == 0) 177 return 1; 178 num = strtol(string+9, &eptr, 10); 179 if (num > 32 || num < 0) 180 return 1; 181 if (*eptr) 182 return 1; 183 *mask = 1 << num; 184 return 0; 185 } 186 187 const char *e2p_jrnl_feature2string(int compat, unsigned int mask) 188 { 189 struct feature *f; 190 static char buf[20]; 191 char fchar; 192 int fnum; 193 194 for (f = jrnl_feature_list; f->string; f++) { 195 if ((compat == f->compat) && 196 (mask == f->mask)) 197 return f->string; 198 } 199 switch (compat) { 200 case E2P_FEATURE_COMPAT: 201 fchar = 'C'; 202 break; 203 case E2P_FEATURE_INCOMPAT: 204 fchar = 'I'; 205 break; 206 case E2P_FEATURE_RO_INCOMPAT: 207 fchar = 'R'; 208 break; 209 default: 210 fchar = '?'; 211 break; 212 } 213 for (fnum = 0; mask >>= 1; fnum++); 214 sprintf(buf, "FEATURE_%c%d", fchar, fnum); 215 return buf; 216 } 217 218 int e2p_jrnl_string2feature(char *string, int *compat_type, unsigned int *mask) 219 { 220 struct feature *f; 221 char *eptr; 222 int num; 223 224 for (f = jrnl_feature_list; f->string; f++) { 225 if (!strcasecmp(string, f->string)) { 226 *compat_type = f->compat; 227 *mask = f->mask; 228 return 0; 229 } 230 } 231 if (strncasecmp(string, "FEATURE_", 8)) 232 return 1; 233 234 switch (string[8]) { 235 case 'c': 236 case 'C': 237 *compat_type = E2P_FEATURE_COMPAT; 238 break; 239 case 'i': 240 case 'I': 241 *compat_type = E2P_FEATURE_INCOMPAT; 242 break; 243 case 'r': 244 case 'R': 245 *compat_type = E2P_FEATURE_RO_INCOMPAT; 246 break; 247 default: 248 return 1; 249 } 250 if (string[9] == 0) 251 return 1; 252 num = strtol(string+9, &eptr, 10); 253 if (num > 32 || num < 0) 254 return 1; 255 if (*eptr) 256 return 1; 257 *mask = 1 << num; 258 return 0; 259 } 260 static char *skip_over_blanks(char *cp) 261 { 262 while (*cp && isspace(*cp)) 263 cp++; 264 return cp; 265 } 266 267 static char *skip_over_word(char *cp) 268 { 269 while (*cp && !isspace(*cp) && *cp != ',') 270 cp++; 271 return cp; 272 } 273 274 /* 275 * Edit a feature set array as requested by the user. The ok_array, 276 * if set, allows the application to limit what features the user is 277 * allowed to set or clear using this function. If clear_ok_array is set, 278 * then use it tell whether or not it is OK to clear a filesystem feature. 279 */ 280 int e2p_edit_feature2(const char *str, __u32 *compat_array, __u32 *ok_array, 281 __u32 *clear_ok_array, int *type_err, 282 unsigned int *mask_err) 283 { 284 char *cp, *buf, *next; 285 int neg; 286 unsigned int mask; 287 int compat_type; 288 int rc = 0; 289 290 if (!clear_ok_array) 291 clear_ok_array = ok_array; 292 293 if (type_err) 294 *type_err = 0; 295 if (mask_err) 296 *mask_err = 0; 297 298 buf = malloc(strlen(str)+1); 299 if (!buf) 300 return 1; 301 strcpy(buf, str); 302 for (cp = buf; cp && *cp; cp = next ? next+1 : 0) { 303 neg = 0; 304 cp = skip_over_blanks(cp); 305 next = skip_over_word(cp); 306 307 if (*next == 0) 308 next = 0; 309 else 310 *next = 0; 311 312 if ((strcasecmp(cp, "none") == 0) || 313 (strcasecmp(cp, "clear") == 0)) { 314 compat_array[0] = 0; 315 compat_array[1] = 0; 316 compat_array[2] = 0; 317 continue; 318 } 319 320 switch (*cp) { 321 case '-': 322 case '^': 323 neg++; 324 /* fallthrough */ 325 case '+': 326 cp++; 327 break; 328 } 329 if (e2p_string2feature(cp, &compat_type, &mask)) { 330 rc = 1; 331 break; 332 } 333 if (neg) { 334 if (clear_ok_array && 335 !(clear_ok_array[compat_type] & mask)) { 336 rc = 1; 337 if (type_err) 338 *type_err = (compat_type | 339 E2P_FEATURE_NEGATE_FLAG); 340 if (mask_err) 341 *mask_err = mask; 342 break; 343 } 344 compat_array[compat_type] &= ~mask; 345 } else { 346 if (ok_array && !(ok_array[compat_type] & mask)) { 347 rc = 1; 348 if (type_err) 349 *type_err = compat_type; 350 if (mask_err) 351 *mask_err = mask; 352 break; 353 } 354 compat_array[compat_type] |= mask; 355 } 356 } 357 free(buf); 358 return rc; 359 } 360 361 int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array) 362 { 363 return e2p_edit_feature2(str, compat_array, ok_array, 0, 0, 0); 364 } 365 366 #ifdef TEST_PROGRAM 367 int main(int argc, char **argv) 368 { 369 int compat, compat2, i; 370 unsigned int mask, mask2; 371 const char *str; 372 struct feature *f; 373 374 for (i = 0; i < 2; i++) { 375 if (i == 0) { 376 f = feature_list; 377 printf("Feature list:\n"); 378 } else { 379 printf("\nJournal feature list:\n"); 380 f = jrnl_feature_list; 381 } 382 for (; f->string; f++) { 383 if (i == 0) { 384 e2p_string2feature((char *)f->string, &compat, 385 &mask); 386 str = e2p_feature2string(compat, mask); 387 } else { 388 e2p_jrnl_string2feature((char *)f->string, 389 &compat, &mask); 390 str = e2p_jrnl_feature2string(compat, mask); 391 } 392 393 printf("\tCompat = %d, Mask = %u, %s\n", 394 compat, mask, f->string); 395 if (strcmp(f->string, str)) { 396 if (e2p_string2feature((char *) str, &compat2, 397 &mask2) || 398 (compat2 != compat) || 399 (mask2 != mask)) { 400 fprintf(stderr, "Failure!\n"); 401 exit(1); 402 } 403 } 404 } 405 } 406 exit(0); 407 } 408 #endif 409