1 /* 2 * set_fields.c --- set a superblock value 3 * 4 * Copyright (C) 2000, 2001, 2002, 2003, 2004 by Theodore Ts'o. 5 * 6 * %Begin-Header% 7 * This file may be redistributed under the terms of the GNU Public 8 * License. 9 * %End-Header% 10 */ 11 12 #define _XOPEN_SOURCE 600 /* for inclusion of strptime() and strtoull */ 13 14 #ifdef HAVE_STRTOULL 15 #define STRTOULL strtoull 16 #else 17 #define STRTOULL strtoul 18 #endif 19 20 #include <stdio.h> 21 #include <unistd.h> 22 #include <stdlib.h> 23 #include <ctype.h> 24 #include <string.h> 25 #include <strings.h> 26 #include <time.h> 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 #ifdef HAVE_ERRNO_H 30 #include <errno.h> 31 #endif 32 #if HAVE_STRINGS_H 33 #include <strings.h> 34 #endif 35 #include <fcntl.h> 36 #include <utime.h> 37 38 #include "debugfs.h" 39 #include "uuid/uuid.h" 40 #include "e2p/e2p.h" 41 42 static struct ext2_super_block set_sb; 43 static struct ext2_inode set_inode; 44 static struct ext2_group_desc set_gd; 45 static dgrp_t set_bg; 46 static ext2_ino_t set_ino; 47 static int array_idx; 48 49 #define FLAG_ARRAY 0x0001 50 51 struct field_set_info { 52 const char *name; 53 void *ptr; 54 unsigned int size; 55 errcode_t (*func)(struct field_set_info *info, char *arg); 56 int flags; 57 int max_idx; 58 }; 59 60 static errcode_t parse_uint(struct field_set_info *info, char *arg); 61 static errcode_t parse_int(struct field_set_info *info, char *arg); 62 static errcode_t parse_string(struct field_set_info *info, char *arg); 63 static errcode_t parse_uuid(struct field_set_info *info, char *arg); 64 static errcode_t parse_hashalg(struct field_set_info *info, char *arg); 65 static errcode_t parse_time(struct field_set_info *info, char *arg); 66 static errcode_t parse_bmap(struct field_set_info *info, char *arg); 67 static errcode_t parse_gd_csum(struct field_set_info *info, char *arg); 68 69 static struct field_set_info super_fields[] = { 70 { "inodes_count", &set_sb.s_inodes_count, 4, parse_uint }, 71 { "blocks_count", &set_sb.s_blocks_count, 4, parse_uint }, 72 { "r_blocks_count", &set_sb.s_r_blocks_count, 4, parse_uint }, 73 { "free_blocks_count", &set_sb.s_free_blocks_count, 4, parse_uint }, 74 { "free_inodes_count", &set_sb.s_free_inodes_count, 4, parse_uint }, 75 { "first_data_block", &set_sb.s_first_data_block, 4, parse_uint }, 76 { "log_block_size", &set_sb.s_log_block_size, 4, parse_uint }, 77 { "log_frag_size", &set_sb.s_log_frag_size, 4, parse_int }, 78 { "blocks_per_group", &set_sb.s_blocks_per_group, 4, parse_uint }, 79 { "frags_per_group", &set_sb.s_frags_per_group, 4, parse_uint }, 80 { "inodes_per_group", &set_sb.s_inodes_per_group, 4, parse_uint }, 81 { "mtime", &set_sb.s_mtime, 4, parse_time }, 82 { "wtime", &set_sb.s_wtime, 4, parse_time }, 83 { "mnt_count", &set_sb.s_mnt_count, 2, parse_uint }, 84 { "max_mnt_count", &set_sb.s_max_mnt_count, 2, parse_int }, 85 /* s_magic */ 86 { "state", &set_sb.s_state, 2, parse_uint }, 87 { "errors", &set_sb.s_errors, 2, parse_uint }, 88 { "minor_rev_level", &set_sb.s_minor_rev_level, 2, parse_uint }, 89 { "lastcheck", &set_sb.s_lastcheck, 4, parse_time }, 90 { "checkinterval", &set_sb.s_checkinterval, 4, parse_uint }, 91 { "creator_os", &set_sb.s_creator_os, 4, parse_uint }, 92 { "rev_level", &set_sb.s_rev_level, 4, parse_uint }, 93 { "def_resuid", &set_sb.s_def_resuid, 2, parse_uint }, 94 { "def_resgid", &set_sb.s_def_resgid, 2, parse_uint }, 95 { "first_ino", &set_sb.s_first_ino, 4, parse_uint }, 96 { "inode_size", &set_sb.s_inode_size, 2, parse_uint }, 97 { "block_group_nr", &set_sb.s_block_group_nr, 2, parse_uint }, 98 { "feature_compat", &set_sb.s_feature_compat, 4, parse_uint }, 99 { "feature_incompat", &set_sb.s_feature_incompat, 4, parse_uint }, 100 { "feature_ro_compat", &set_sb.s_feature_ro_compat, 4, parse_uint }, 101 { "uuid", &set_sb.s_uuid, 16, parse_uuid }, 102 { "volume_name", &set_sb.s_volume_name, 16, parse_string }, 103 { "last_mounted", &set_sb.s_last_mounted, 64, parse_string }, 104 { "lastcheck", &set_sb.s_lastcheck, 4, parse_uint }, 105 { "algorithm_usage_bitmap", &set_sb.s_algorithm_usage_bitmap, 106 4, parse_uint }, 107 { "prealloc_blocks", &set_sb.s_prealloc_blocks, 1, parse_uint }, 108 { "prealloc_dir_blocks", &set_sb.s_prealloc_dir_blocks, 1, 109 parse_uint }, 110 { "reserved_gdt_blocks", &set_sb.s_reserved_gdt_blocks, 2, 111 parse_uint }, 112 { "journal_uuid", &set_sb.s_journal_uuid, 16, parse_uuid }, 113 { "journal_inum", &set_sb.s_journal_inum, 4, parse_uint }, 114 { "journal_dev", &set_sb.s_journal_dev, 4, parse_uint }, 115 { "last_orphan", &set_sb.s_last_orphan, 4, parse_uint }, 116 { "hash_seed", &set_sb.s_hash_seed, 16, parse_uuid }, 117 { "def_hash_version", &set_sb.s_def_hash_version, 1, parse_hashalg }, 118 { "jnl_backup_type", &set_sb.s_jnl_backup_type, 1, parse_uint }, 119 { "desc_size", &set_sb.s_desc_size, 2, parse_uint }, 120 { "default_mount_opts", &set_sb.s_default_mount_opts, 4, parse_uint }, 121 { "first_meta_bg", &set_sb.s_first_meta_bg, 4, parse_uint }, 122 { "mkfs_time", &set_sb.s_mkfs_time, 4, parse_time }, 123 { "jnl_blocks", &set_sb.s_jnl_blocks[0], 4, parse_uint, FLAG_ARRAY, 124 17 }, 125 { "blocks_count_hi", &set_sb.s_blocks_count_hi, 4, parse_uint }, 126 { "r_blocks_count_hi", &set_sb.s_r_blocks_count_hi, 4, parse_uint }, 127 { "min_extra_isize", &set_sb.s_min_extra_isize, 2, parse_uint }, 128 { "want_extra_isize", &set_sb.s_want_extra_isize, 2, parse_uint }, 129 { "flags", &set_sb.s_flags, 4, parse_uint }, 130 { "raid_stride", &set_sb.s_raid_stride, 2, parse_uint }, 131 { "min_extra_isize", &set_sb.s_min_extra_isize, 4, parse_uint }, 132 { "mmp_interval", &set_sb.s_mmp_interval, 2, parse_uint }, 133 { "mmp_block", &set_sb.s_mmp_block, 8, parse_uint }, 134 { "raid_stripe_width", &set_sb.s_raid_stripe_width, 4, parse_uint }, 135 { "log_groups_per_flex", &set_sb.s_log_groups_per_flex, 1, parse_uint }, 136 { "kbytes_written", &set_sb.s_kbytes_written, 8, parse_uint }, 137 { "snapshot_inum", &set_sb.s_snapshot_inum, 4, parse_uint }, 138 { "snapshot_id", &set_sb.s_snapshot_id, 4, parse_uint }, 139 { "snapshot_r_blocks_count", &set_sb.s_snapshot_r_blocks_count, 8, 140 parse_uint }, 141 { "snapshot_list", &set_sb.s_snapshot_list, 4, parse_uint }, 142 { "mount_opts", &set_sb.s_mount_opts, 64, parse_string }, 143 { 0, 0, 0, 0 } 144 }; 145 146 static struct field_set_info inode_fields[] = { 147 { "inodes_count", &set_sb.s_inodes_count, 4, parse_uint }, 148 { "mode", &set_inode.i_mode, 2, parse_uint }, 149 { "uid", &set_inode.i_uid, 2, parse_uint }, 150 { "size", &set_inode.i_size, 4, parse_uint }, 151 { "atime", &set_inode.i_atime, 4, parse_time }, 152 { "ctime", &set_inode.i_ctime, 4, parse_time }, 153 { "mtime", &set_inode.i_mtime, 4, parse_time }, 154 { "dtime", &set_inode.i_dtime, 4, parse_time }, 155 { "gid", &set_inode.i_gid, 2, parse_uint }, 156 { "links_count", &set_inode.i_links_count, 2, parse_uint }, 157 { "blocks", &set_inode.i_blocks, 4, parse_uint }, 158 { "flags", &set_inode.i_flags, 4, parse_uint }, 159 { "version", &set_inode.osd1.linux1.l_i_version, 4, parse_uint }, 160 { "translator", &set_inode.osd1.hurd1.h_i_translator, 4, parse_uint }, 161 { "block", &set_inode.i_block[0], 4, parse_uint, FLAG_ARRAY, 162 EXT2_NDIR_BLOCKS }, 163 { "block[IND]", &set_inode.i_block[EXT2_IND_BLOCK], 4, parse_uint }, 164 { "block[DIND]", &set_inode.i_block[EXT2_DIND_BLOCK], 4, parse_uint }, 165 { "block[TIND]", &set_inode.i_block[EXT2_TIND_BLOCK], 4, parse_uint }, 166 { "generation", &set_inode.i_generation, 4, parse_uint }, 167 { "file_acl", &set_inode.i_file_acl, 4, parse_uint }, 168 { "file_acl_high", &set_inode.osd2.linux2.l_i_file_acl_high, 2, 169 parse_uint }, 170 { "dir_acl", &set_inode.i_dir_acl, 4, parse_uint }, 171 { "size_high", &set_inode.i_size_high, 4, parse_uint }, 172 { "faddr", &set_inode.i_faddr, 4, parse_uint }, 173 { "blocks_hi", &set_inode.osd2.linux2.l_i_blocks_hi, 2, parse_uint }, 174 { "frag", &set_inode.osd2.hurd2.h_i_frag, 1, parse_uint }, 175 { "fsize", &set_inode.osd2.hurd2.h_i_fsize, 1, parse_uint }, 176 { "uid_high", &set_inode.osd2.linux2.l_i_uid_high, 2, parse_uint }, 177 { "gid_high", &set_inode.osd2.linux2.l_i_gid_high, 2, parse_uint }, 178 { "author", &set_inode.osd2.hurd2.h_i_author, 4, parse_uint }, 179 { "bmap", NULL, 4, parse_bmap, FLAG_ARRAY }, 180 { 0, 0, 0, 0 } 181 }; 182 183 static struct field_set_info ext2_bg_fields[] = { 184 { "block_bitmap", &set_gd.bg_block_bitmap, 4, parse_uint }, 185 { "inode_bitmap", &set_gd.bg_inode_bitmap, 4, parse_uint }, 186 { "inode_table", &set_gd.bg_inode_table, 4, parse_uint }, 187 { "free_blocks_count", &set_gd.bg_free_blocks_count, 2, parse_uint }, 188 { "free_inodes_count", &set_gd.bg_free_inodes_count, 2, parse_uint }, 189 { "used_dirs_count", &set_gd.bg_used_dirs_count, 2, parse_uint }, 190 { "flags", &set_gd.bg_flags, 2, parse_uint }, 191 { "reserved", &set_gd.bg_reserved, 2, parse_uint, FLAG_ARRAY, 2 }, 192 { "itable_unused", &set_gd.bg_itable_unused, 2, parse_uint }, 193 { "checksum", &set_gd.bg_checksum, 2, parse_gd_csum }, 194 { 0, 0, 0, 0 } 195 }; 196 197 198 static struct field_set_info *find_field(struct field_set_info *fields, 199 char *field) 200 { 201 struct field_set_info *ss; 202 const char *prefix; 203 char *arg, *delim, *idx, *tmp; 204 int prefix_len; 205 206 if (fields == super_fields) 207 prefix = "s_"; 208 else if (fields == inode_fields) 209 prefix = "i_"; 210 else 211 prefix = "bg_"; 212 prefix_len = strlen(prefix); 213 if (strncmp(field, prefix, prefix_len) == 0) 214 field += prefix_len; 215 216 arg = malloc(strlen(field)+1); 217 if (!arg) 218 return NULL; 219 strcpy(arg, field); 220 221 idx = strchr(arg, '['); 222 if (idx) { 223 *idx++ = 0; 224 delim = idx + strlen(idx) - 1; 225 if (!*idx || *delim != ']') 226 idx = 0; 227 else 228 *delim = 0; 229 } 230 /* 231 * Can we parse the number? 232 */ 233 if (idx) { 234 array_idx = strtol(idx, &tmp, 0); 235 if (*tmp) 236 idx = 0; 237 } 238 239 for (ss = fields ; ss->name ; ss++) { 240 if (ss->flags & FLAG_ARRAY) { 241 if (!idx || (strcmp(ss->name, arg) != 0)) 242 continue; 243 if (ss->max_idx > 0 && array_idx >= ss->max_idx) 244 continue; 245 } else { 246 if (strcmp(ss->name, field) != 0) 247 continue; 248 } 249 free(arg); 250 return ss; 251 } 252 free(arg); 253 return NULL; 254 } 255 256 static errcode_t parse_uint(struct field_set_info *info, char *arg) 257 { 258 unsigned long long num, limit; 259 char *tmp; 260 union { 261 __u64 *ptr64; 262 __u32 *ptr32; 263 __u16 *ptr16; 264 __u8 *ptr8; 265 } u; 266 267 u.ptr8 = (__u8 *) info->ptr; 268 if (info->flags & FLAG_ARRAY) 269 u.ptr8 += array_idx * info->size; 270 271 errno = 0; 272 num = STRTOULL(arg, &tmp, 0); 273 if (*tmp || errno) { 274 fprintf(stderr, "Couldn't parse '%s' for field %s.\n", 275 arg, info->name); 276 return EINVAL; 277 } 278 limit = ~0ULL >> ((8 - info->size) * 8); 279 if (num > limit) { 280 fprintf(stderr, "Value '%s' exceeds field %s maximum %llu.\n", 281 arg, info->name, limit); 282 return EINVAL; 283 } 284 switch (info->size) { 285 case 8: 286 *u.ptr64 = num; 287 break; 288 case 4: 289 *u.ptr32 = num; 290 break; 291 case 2: 292 *u.ptr16 = num; 293 break; 294 case 1: 295 *u.ptr8 = num; 296 break; 297 } 298 return 0; 299 } 300 301 static errcode_t parse_int(struct field_set_info *info, char *arg) 302 { 303 long num; 304 char *tmp; 305 __s32 *ptr32; 306 __s16 *ptr16; 307 __s8 *ptr8; 308 309 num = strtol(arg, &tmp, 0); 310 if (*tmp) { 311 fprintf(stderr, "Couldn't parse '%s' for field %s.\n", 312 arg, info->name); 313 return EINVAL; 314 } 315 switch (info->size) { 316 case 4: 317 ptr32 = (__s32 *) info->ptr; 318 *ptr32 = num; 319 break; 320 case 2: 321 ptr16 = (__s16 *) info->ptr; 322 *ptr16 = num; 323 break; 324 case 1: 325 ptr8 = (__s8 *) info->ptr; 326 *ptr8 = num; 327 break; 328 } 329 return 0; 330 } 331 332 static errcode_t parse_string(struct field_set_info *info, char *arg) 333 { 334 char *cp = (char *) info->ptr; 335 336 if (strlen(arg) >= info->size) { 337 fprintf(stderr, "Error maximum size for %s is %d.\n", 338 info->name, info->size); 339 return EINVAL; 340 } 341 strcpy(cp, arg); 342 return 0; 343 } 344 345 static errcode_t parse_time(struct field_set_info *info, char *arg) 346 { 347 time_t t; 348 __u32 *ptr32; 349 350 ptr32 = (__u32 *) info->ptr; 351 352 t = string_to_time(arg); 353 354 if (t == ((time_t) -1)) { 355 fprintf(stderr, "Couldn't parse '%s' for field %s.\n", 356 arg, info->name); 357 return EINVAL; 358 } 359 *ptr32 = t; 360 return 0; 361 } 362 363 static errcode_t parse_uuid(struct field_set_info *info, char *arg) 364 { 365 unsigned char * p = (unsigned char *) info->ptr; 366 367 if ((strcasecmp(arg, "null") == 0) || 368 (strcasecmp(arg, "clear") == 0)) { 369 uuid_clear(p); 370 } else if (strcasecmp(arg, "time") == 0) { 371 uuid_generate_time(p); 372 } else if (strcasecmp(arg, "random") == 0) { 373 uuid_generate(p); 374 } else if (uuid_parse(arg, p)) { 375 fprintf(stderr, "Invalid UUID format: %s\n", arg); 376 return EINVAL; 377 } 378 return 0; 379 } 380 381 static errcode_t parse_hashalg(struct field_set_info *info, char *arg) 382 { 383 int hashv; 384 unsigned char *p = (unsigned char *) info->ptr; 385 386 hashv = e2p_string2hash(arg); 387 if (hashv < 0) { 388 fprintf(stderr, "Invalid hash algorithm: %s\n", arg); 389 return EINVAL; 390 } 391 *p = hashv; 392 return 0; 393 } 394 395 static errcode_t parse_bmap(struct field_set_info *info, char *arg) 396 { 397 unsigned long num; 398 blk_t blk; 399 errcode_t retval; 400 char *tmp; 401 402 num = strtoul(arg, &tmp, 0); 403 if (*tmp) { 404 fprintf(stderr, "Couldn't parse '%s' for field %s.\n", 405 arg, info->name); 406 return EINVAL; 407 } 408 blk = num; 409 410 retval = ext2fs_bmap(current_fs, set_ino, &set_inode, 0, BMAP_SET, 411 array_idx, &blk); 412 if (retval) { 413 com_err("set_inode", retval, "while setting block map"); 414 } 415 return retval; 416 } 417 418 static errcode_t parse_gd_csum(struct field_set_info *info, char *arg) 419 { 420 421 if (strcmp(arg, "calc") == 0) { 422 ext2fs_group_desc_csum_set(current_fs, set_bg); 423 set_gd = current_fs->group_desc[set_bg]; 424 printf("Checksum set to 0x%04x\n", 425 current_fs->group_desc[set_bg].bg_checksum); 426 return 0; 427 } 428 429 return parse_uint(info, arg); 430 } 431 432 static void print_possible_fields(struct field_set_info *fields) 433 { 434 struct field_set_info *ss; 435 const char *type, *cmd; 436 FILE *f; 437 char name[40], idx[40]; 438 439 if (fields == super_fields) { 440 type = "Superblock"; 441 cmd = "set_super_value"; 442 } else if (fields == inode_fields) { 443 type = "Inode"; 444 cmd = "set_inode"; 445 } else { 446 type = "Block group descriptor"; 447 cmd = "set_block_group"; 448 } 449 f = open_pager(); 450 451 fprintf(f, "%s fields supported by the %s command:\n", type, cmd); 452 453 for (ss = fields ; ss->name ; ss++) { 454 type = "unknown"; 455 if (ss->func == parse_string) 456 type = "string"; 457 else if (ss->func == parse_int) 458 type = "integer"; 459 else if (ss->func == parse_uint) 460 type = "unsigned integer"; 461 else if (ss->func == parse_uuid) 462 type = "UUID"; 463 else if (ss->func == parse_hashalg) 464 type = "hash algorithm"; 465 else if (ss->func == parse_time) 466 type = "date/time"; 467 else if (ss->func == parse_bmap) 468 type = "set physical->logical block map"; 469 strcpy(name, ss->name); 470 if (ss->flags & FLAG_ARRAY) { 471 if (ss->max_idx > 0) 472 sprintf(idx, "[%d]", ss->max_idx); 473 else 474 strcpy(idx, "[]"); 475 strcat(name, idx); 476 } 477 fprintf(f, "\t%-20s\t%s\n", name, type); 478 } 479 close_pager(f); 480 } 481 482 483 void do_set_super(int argc, char *argv[]) 484 { 485 const char *usage = "<field> <value>\n" 486 "\t\"set_super_value -l\" will list the names of " 487 "superblock fields\n\twhich can be set."; 488 static struct field_set_info *ss; 489 490 if ((argc == 2) && !strcmp(argv[1], "-l")) { 491 print_possible_fields(super_fields); 492 return; 493 } 494 495 if (common_args_process(argc, argv, 3, 3, "set_super_value", 496 usage, CHECK_FS_RW)) 497 return; 498 499 if ((ss = find_field(super_fields, argv[1])) == 0) { 500 com_err(argv[0], 0, "invalid field specifier: %s", argv[1]); 501 return; 502 } 503 set_sb = *current_fs->super; 504 if (ss->func(ss, argv[2]) == 0) { 505 *current_fs->super = set_sb; 506 ext2fs_mark_super_dirty(current_fs); 507 } 508 } 509 510 void do_set_inode(int argc, char *argv[]) 511 { 512 const char *usage = "<inode> <field> <value>\n" 513 "\t\"set_inode_field -l\" will list the names of " 514 "the fields in an ext2 inode\n\twhich can be set."; 515 static struct field_set_info *ss; 516 517 if ((argc == 2) && !strcmp(argv[1], "-l")) { 518 print_possible_fields(inode_fields); 519 return; 520 } 521 522 if (common_args_process(argc, argv, 4, 4, "set_inode", 523 usage, CHECK_FS_RW)) 524 return; 525 526 if ((ss = find_field(inode_fields, argv[2])) == 0) { 527 com_err(argv[0], 0, "invalid field specifier: %s", argv[2]); 528 return; 529 } 530 531 set_ino = string_to_inode(argv[1]); 532 if (!set_ino) 533 return; 534 535 if (debugfs_read_inode(set_ino, &set_inode, argv[1])) 536 return; 537 538 if (ss->func(ss, argv[3]) == 0) { 539 if (debugfs_write_inode(set_ino, &set_inode, argv[1])) 540 return; 541 } 542 } 543 544 void do_set_block_group_descriptor(int argc, char *argv[]) 545 { 546 const char *usage = "<bg number> <field> <value>\n" 547 "\t\"set_block_group_descriptor -l\" will list the names of " 548 "the fields in a block group descriptor\n\twhich can be set."; 549 struct field_set_info *ss; 550 char *end; 551 552 if ((argc == 2) && !strcmp(argv[1], "-l")) { 553 print_possible_fields(ext2_bg_fields); 554 return; 555 } 556 557 if (common_args_process(argc, argv, 4, 4, "set_block_group_descriptor", 558 usage, CHECK_FS_RW)) 559 return; 560 561 set_bg = strtoul(argv[1], &end, 0); 562 if (*end) { 563 com_err(argv[0], 0, "invalid block group number: %s", argv[1]); 564 return; 565 } 566 567 if (set_bg >= current_fs->group_desc_count) { 568 com_err(argv[0], 0, "block group number too big: %d", set_bg); 569 return; 570 } 571 572 573 if ((ss = find_field(ext2_bg_fields, argv[2])) == 0) { 574 com_err(argv[0], 0, "invalid field specifier: %s", argv[2]); 575 return; 576 } 577 578 set_gd = current_fs->group_desc[set_bg]; 579 580 if (ss->func(ss, argv[3]) == 0) { 581 current_fs->group_desc[set_bg] = set_gd; 582 ext2fs_mark_super_dirty(current_fs); 583 } 584 } 585