1 /* 2 * util.c --- miscellaneous utilities 3 * 4 * Copyright (C) 1993, 1994, 1995, 1996, 1997 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 #include <stdlib.h> 13 #include <stdio.h> 14 #include <unistd.h> 15 #include <string.h> 16 #include <ctype.h> 17 #ifdef __linux__ 18 #include <sys/utsname.h> 19 #endif 20 21 #ifdef HAVE_CONIO_H 22 #undef HAVE_TERMIOS_H 23 #include <conio.h> 24 #define read_a_char() getch() 25 #else 26 #ifdef HAVE_TERMIOS_H 27 #include <termios.h> 28 #endif 29 #endif 30 31 #ifdef HAVE_MALLOC_H 32 #include <malloc.h> 33 #endif 34 35 #ifdef HAVE_ERRNO_H 36 #include <errno.h> 37 #endif 38 39 #include "e2fsck.h" 40 41 extern e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */ 42 43 #include <sys/time.h> 44 #include <sys/resource.h> 45 46 void fatal_error(e2fsck_t ctx, const char *msg) 47 { 48 if (msg) 49 fprintf (stderr, "e2fsck: %s\n", msg); 50 if (ctx->fs && ctx->fs->io) { 51 if (ctx->fs->io->magic == EXT2_ET_MAGIC_IO_CHANNEL) 52 io_channel_flush(ctx->fs->io); 53 else 54 fprintf(stderr, "e2fsck: io manager magic bad!\n"); 55 } 56 ctx->flags |= E2F_FLAG_ABORT; 57 if (ctx->flags & E2F_FLAG_SETJMP_OK) 58 longjmp(ctx->abort_loc, 1); 59 exit(FSCK_ERROR); 60 } 61 62 void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size, 63 const char *description) 64 { 65 void *ret; 66 char buf[256]; 67 68 #ifdef DEBUG_ALLOCATE_MEMORY 69 printf("Allocating %u bytes for %s...\n", size, description); 70 #endif 71 ret = malloc(size); 72 if (!ret) { 73 sprintf(buf, "Can't allocate %s\n", description); 74 fatal_error(ctx, buf); 75 } 76 memset(ret, 0, size); 77 return ret; 78 } 79 80 char *string_copy(e2fsck_t ctx EXT2FS_ATTR((unused)), 81 const char *str, int len) 82 { 83 char *ret; 84 85 if (!str) 86 return NULL; 87 if (!len) 88 len = strlen(str); 89 ret = malloc(len+1); 90 if (ret) { 91 strncpy(ret, str, len); 92 ret[len] = 0; 93 } 94 return ret; 95 } 96 97 #ifndef HAVE_STRNLEN 98 /* 99 * Incredibly, libc5 doesn't appear to have strnlen. So we have to 100 * provide our own. 101 */ 102 int e2fsck_strnlen(const char * s, int count) 103 { 104 const char *cp = s; 105 106 while (count-- && *cp) 107 cp++; 108 return cp - s; 109 } 110 #endif 111 112 #ifndef HAVE_CONIO_H 113 static int read_a_char(void) 114 { 115 char c; 116 int r; 117 int fail = 0; 118 119 while(1) { 120 if (e2fsck_global_ctx && 121 (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) { 122 return 3; 123 } 124 r = read(0, &c, 1); 125 if (r == 1) 126 return c; 127 if (fail++ > 100) 128 break; 129 } 130 return EOF; 131 } 132 #endif 133 134 int ask_yn(const char * string, int def) 135 { 136 int c; 137 const char *defstr; 138 const char *short_yes = _("yY"); 139 const char *short_no = _("nN"); 140 141 #ifdef HAVE_TERMIOS_H 142 struct termios termios, tmp; 143 144 tcgetattr (0, &termios); 145 tmp = termios; 146 tmp.c_lflag &= ~(ICANON | ECHO); 147 tmp.c_cc[VMIN] = 1; 148 tmp.c_cc[VTIME] = 0; 149 tcsetattr (0, TCSANOW, &tmp); 150 #endif 151 152 if (def == 1) 153 defstr = _(_("<y>")); 154 else if (def == 0) 155 defstr = _(_("<n>")); 156 else 157 defstr = _(" (y/n)"); 158 printf("%s%s? ", string, defstr); 159 while (1) { 160 fflush (stdout); 161 if ((c = read_a_char()) == EOF) 162 break; 163 if (c == 3) { 164 #ifdef HAVE_TERMIOS_H 165 tcsetattr (0, TCSANOW, &termios); 166 #endif 167 if (e2fsck_global_ctx && 168 e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) { 169 puts("\n"); 170 longjmp(e2fsck_global_ctx->abort_loc, 1); 171 } 172 puts(_("cancelled!\n")); 173 return 0; 174 } 175 if (strchr(short_yes, (char) c)) { 176 def = 1; 177 break; 178 } 179 else if (strchr(short_no, (char) c)) { 180 def = 0; 181 break; 182 } 183 else if ((c == ' ' || c == '\n') && (def != -1)) 184 break; 185 } 186 if (def) 187 puts(_("yes\n")); 188 else 189 puts (_("no\n")); 190 #ifdef HAVE_TERMIOS_H 191 tcsetattr (0, TCSANOW, &termios); 192 #endif 193 return def; 194 } 195 196 int ask (e2fsck_t ctx, const char * string, int def) 197 { 198 if (ctx->options & E2F_OPT_NO) { 199 printf (_("%s? no\n\n"), string); 200 return 0; 201 } 202 if (ctx->options & E2F_OPT_YES) { 203 printf (_("%s? yes\n\n"), string); 204 return 1; 205 } 206 if (ctx->options & E2F_OPT_PREEN) { 207 printf ("%s? %s\n\n", string, def ? _("yes") : _("no")); 208 return def; 209 } 210 return ask_yn(string, def); 211 } 212 213 void e2fsck_read_bitmaps(e2fsck_t ctx) 214 { 215 ext2_filsys fs = ctx->fs; 216 errcode_t retval; 217 const char *old_op; 218 219 if (ctx->invalid_bitmaps) { 220 com_err(ctx->program_name, 0, 221 _("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"), 222 ctx->device_name); 223 fatal_error(ctx, 0); 224 } 225 226 old_op = ehandler_operation(_("reading inode and block bitmaps")); 227 retval = ext2fs_read_bitmaps(fs); 228 ehandler_operation(old_op); 229 if (retval) { 230 com_err(ctx->program_name, retval, 231 _("while retrying to read bitmaps for %s"), 232 ctx->device_name); 233 fatal_error(ctx, 0); 234 } 235 } 236 237 void e2fsck_write_bitmaps(e2fsck_t ctx) 238 { 239 ext2_filsys fs = ctx->fs; 240 errcode_t retval; 241 const char *old_op; 242 243 old_op = ehandler_operation(_("writing block and inode bitmaps")); 244 retval = ext2fs_write_bitmaps(fs); 245 ehandler_operation(old_op); 246 if (retval) { 247 com_err(ctx->program_name, retval, 248 _("while rewriting block and inode bitmaps for %s"), 249 ctx->device_name); 250 fatal_error(ctx, 0); 251 } 252 } 253 254 void preenhalt(e2fsck_t ctx) 255 { 256 ext2_filsys fs = ctx->fs; 257 258 if (!(ctx->options & E2F_OPT_PREEN)) 259 return; 260 fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; " 261 "RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"), 262 ctx->device_name); 263 ctx->flags |= E2F_FLAG_EXITING; 264 if (fs != NULL) { 265 fs->super->s_state |= EXT2_ERROR_FS; 266 ext2fs_mark_super_dirty(fs); 267 ext2fs_close(fs); 268 } 269 exit(FSCK_UNCORRECTED); 270 } 271 272 #ifdef RESOURCE_TRACK 273 void init_resource_track(struct resource_track *track, io_channel channel) 274 { 275 #ifdef HAVE_GETRUSAGE 276 struct rusage r; 277 #endif 278 io_stats io_start = 0; 279 280 track->brk_start = sbrk(0); 281 gettimeofday(&track->time_start, 0); 282 #ifdef HAVE_GETRUSAGE 283 #ifdef sun 284 memset(&r, 0, sizeof(struct rusage)); 285 #endif 286 getrusage(RUSAGE_SELF, &r); 287 track->user_start = r.ru_utime; 288 track->system_start = r.ru_stime; 289 #else 290 track->user_start.tv_sec = track->user_start.tv_usec = 0; 291 track->system_start.tv_sec = track->system_start.tv_usec = 0; 292 #endif 293 track->bytes_read = 0; 294 track->bytes_written = 0; 295 if (channel && channel->manager && channel->manager->get_stats) 296 channel->manager->get_stats(channel, &io_start); 297 if (io_start) { 298 track->bytes_read = io_start->bytes_read; 299 track->bytes_written = io_start->bytes_written; 300 } 301 } 302 303 #ifdef __GNUC__ 304 #define _INLINE_ __inline__ 305 #else 306 #define _INLINE_ 307 #endif 308 309 static _INLINE_ float timeval_subtract(struct timeval *tv1, 310 struct timeval *tv2) 311 { 312 return ((tv1->tv_sec - tv2->tv_sec) + 313 ((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000); 314 } 315 316 void print_resource_track(e2fsck_t ctx, const char *desc, 317 struct resource_track *track, io_channel channel) 318 { 319 #ifdef HAVE_GETRUSAGE 320 struct rusage r; 321 #endif 322 #ifdef HAVE_MALLINFO 323 struct mallinfo malloc_info; 324 #endif 325 struct timeval time_end; 326 327 if ((desc && !(ctx->options & E2F_OPT_TIME2)) || 328 (!desc && !(ctx->options & E2F_OPT_TIME))) 329 return; 330 331 e2fsck_clear_progbar(ctx); 332 gettimeofday(&time_end, 0); 333 334 if (desc) 335 printf("%s: ", desc); 336 337 #ifdef HAVE_MALLINFO 338 #define kbytes(x) (((unsigned long)(x) + 1023) / 1024) 339 340 malloc_info = mallinfo(); 341 printf(_("Memory used: %luk/%luk (%luk/%luk), "), 342 kbytes(malloc_info.arena), kbytes(malloc_info.hblkhd), 343 kbytes(malloc_info.uordblks), kbytes(malloc_info.fordblks)); 344 #else 345 printf(_("Memory used: %lu, "), 346 (unsigned long) (((char *) sbrk(0)) - 347 ((char *) track->brk_start))); 348 #endif 349 #ifdef HAVE_GETRUSAGE 350 getrusage(RUSAGE_SELF, &r); 351 352 printf(_("time: %5.2f/%5.2f/%5.2f\n"), 353 timeval_subtract(&time_end, &track->time_start), 354 timeval_subtract(&r.ru_utime, &track->user_start), 355 timeval_subtract(&r.ru_stime, &track->system_start)); 356 #else 357 printf(_("elapsed time: %6.3f\n"), 358 timeval_subtract(&time_end, &track->time_start)); 359 #endif 360 #define mbytes(x) (((x) + 1048575) / 1048576) 361 if (channel && channel->manager && channel->manager->get_stats) { 362 io_stats delta = 0; 363 unsigned long long bytes_read = 0; 364 unsigned long long bytes_written = 0; 365 366 if (desc) 367 printf("%s: ", desc); 368 369 channel->manager->get_stats(channel, &delta); 370 if (delta) { 371 bytes_read = delta->bytes_read - track->bytes_read; 372 bytes_written = delta->bytes_written - 373 track->bytes_written; 374 } 375 printf("I/O read: %lluMB, write: %lluMB, rate: %.2fMB/s\n", 376 mbytes(bytes_read), mbytes(bytes_written), 377 (double)mbytes(bytes_read + bytes_written) / 378 timeval_subtract(&time_end, &track->time_start)); 379 } 380 } 381 #endif /* RESOURCE_TRACK */ 382 383 void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino, 384 struct ext2_inode * inode, const char *proc) 385 { 386 int retval; 387 388 retval = ext2fs_read_inode(ctx->fs, ino, inode); 389 if (retval) { 390 com_err("ext2fs_read_inode", retval, 391 _("while reading inode %lu in %s"), ino, proc); 392 fatal_error(ctx, 0); 393 } 394 } 395 396 void e2fsck_read_inode_full(e2fsck_t ctx, unsigned long ino, 397 struct ext2_inode *inode, int bufsize, 398 const char *proc) 399 { 400 int retval; 401 402 retval = ext2fs_read_inode_full(ctx->fs, ino, inode, bufsize); 403 if (retval) { 404 com_err("ext2fs_read_inode_full", retval, 405 _("while reading inode %lu in %s"), ino, proc); 406 fatal_error(ctx, 0); 407 } 408 } 409 410 extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino, 411 struct ext2_inode * inode, int bufsize, 412 const char *proc) 413 { 414 int retval; 415 416 retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize); 417 if (retval) { 418 com_err("ext2fs_write_inode", retval, 419 _("while writing inode %lu in %s"), ino, proc); 420 fatal_error(ctx, 0); 421 } 422 } 423 424 extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino, 425 struct ext2_inode * inode, const char *proc) 426 { 427 int retval; 428 429 retval = ext2fs_write_inode(ctx->fs, ino, inode); 430 if (retval) { 431 com_err("ext2fs_write_inode", retval, 432 _("while writing inode %lu in %s"), ino, proc); 433 fatal_error(ctx, 0); 434 } 435 } 436 437 #ifdef MTRACE 438 void mtrace_print(char *mesg) 439 { 440 FILE *malloc_get_mallstream(); 441 FILE *f = malloc_get_mallstream(); 442 443 if (f) 444 fprintf(f, "============= %s\n", mesg); 445 } 446 #endif 447 448 blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name, 449 io_manager manager) 450 { 451 struct ext2_super_block *sb; 452 io_channel io = NULL; 453 void *buf = NULL; 454 int blocksize; 455 blk_t superblock, ret_sb = 8193; 456 457 if (fs && fs->super) { 458 ret_sb = (fs->super->s_blocks_per_group + 459 fs->super->s_first_data_block); 460 if (ctx) { 461 ctx->superblock = ret_sb; 462 ctx->blocksize = fs->blocksize; 463 } 464 return ret_sb; 465 } 466 467 if (ctx) { 468 if (ctx->blocksize) { 469 ret_sb = ctx->blocksize * 8; 470 if (ctx->blocksize == 1024) 471 ret_sb++; 472 ctx->superblock = ret_sb; 473 return ret_sb; 474 } 475 ctx->superblock = ret_sb; 476 ctx->blocksize = 1024; 477 } 478 479 if (!name || !manager) 480 goto cleanup; 481 482 if (manager->open(name, 0, &io) != 0) 483 goto cleanup; 484 485 if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf)) 486 goto cleanup; 487 sb = (struct ext2_super_block *) buf; 488 489 for (blocksize = EXT2_MIN_BLOCK_SIZE; 490 blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) { 491 superblock = blocksize*8; 492 if (blocksize == 1024) 493 superblock++; 494 io_channel_set_blksize(io, blocksize); 495 if (io_channel_read_blk(io, superblock, 496 -SUPERBLOCK_SIZE, buf)) 497 continue; 498 #ifdef WORDS_BIGENDIAN 499 if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) 500 ext2fs_swap_super(sb); 501 #endif 502 if ((sb->s_magic == EXT2_SUPER_MAGIC) && 503 (EXT2_BLOCK_SIZE(sb) == blocksize)) { 504 ret_sb = superblock; 505 if (ctx) { 506 ctx->superblock = superblock; 507 ctx->blocksize = blocksize; 508 } 509 break; 510 } 511 } 512 513 cleanup: 514 if (io) 515 io_channel_close(io); 516 if (buf) 517 ext2fs_free_mem(&buf); 518 return (ret_sb); 519 } 520 521 /* 522 * Given a mode, return the ext2 file type 523 */ 524 int ext2_file_type(unsigned int mode) 525 { 526 if (LINUX_S_ISREG(mode)) 527 return EXT2_FT_REG_FILE; 528 529 if (LINUX_S_ISDIR(mode)) 530 return EXT2_FT_DIR; 531 532 if (LINUX_S_ISCHR(mode)) 533 return EXT2_FT_CHRDEV; 534 535 if (LINUX_S_ISBLK(mode)) 536 return EXT2_FT_BLKDEV; 537 538 if (LINUX_S_ISLNK(mode)) 539 return EXT2_FT_SYMLINK; 540 541 if (LINUX_S_ISFIFO(mode)) 542 return EXT2_FT_FIFO; 543 544 if (LINUX_S_ISSOCK(mode)) 545 return EXT2_FT_SOCK; 546 547 return 0; 548 } 549 550 #define STRIDE_LENGTH 8 551 /* 552 * Helper function which zeros out _num_ blocks starting at _blk_. In 553 * case of an error, the details of the error is returned via _ret_blk_ 554 * and _ret_count_ if they are non-NULL pointers. Returns 0 on 555 * success, and an error code on an error. 556 * 557 * As a special case, if the first argument is NULL, then it will 558 * attempt to free the static zeroizing buffer. (This is to keep 559 * programs that check for memory leaks happy.) 560 */ 561 errcode_t e2fsck_zero_blocks(ext2_filsys fs, blk_t blk, int num, 562 blk_t *ret_blk, int *ret_count) 563 { 564 int j, count, next_update, next_update_incr; 565 static char *buf; 566 errcode_t retval; 567 568 /* If fs is null, clean up the static buffer and return */ 569 if (!fs) { 570 if (buf) { 571 free(buf); 572 buf = 0; 573 } 574 return 0; 575 } 576 /* Allocate the zeroizing buffer if necessary */ 577 if (!buf) { 578 buf = malloc(fs->blocksize * STRIDE_LENGTH); 579 if (!buf) { 580 com_err("malloc", ENOMEM, 581 _("while allocating zeroizing buffer")); 582 exit(1); 583 } 584 memset(buf, 0, fs->blocksize * STRIDE_LENGTH); 585 } 586 /* OK, do the write loop */ 587 next_update = 0; 588 next_update_incr = num / 100; 589 if (next_update_incr < 1) 590 next_update_incr = 1; 591 for (j = 0; j < num; j += STRIDE_LENGTH, blk += STRIDE_LENGTH) { 592 count = num - j; 593 if (count > STRIDE_LENGTH) 594 count = STRIDE_LENGTH; 595 retval = io_channel_write_blk(fs->io, blk, count, buf); 596 if (retval) { 597 if (ret_count) 598 *ret_count = count; 599 if (ret_blk) 600 *ret_blk = blk; 601 return retval; 602 } 603 } 604 return 0; 605 } 606 607 /* 608 * Check to see if a filesystem is in /proc/filesystems. 609 * Returns 1 if found, 0 if not 610 */ 611 int fs_proc_check(const char *fs_name) 612 { 613 FILE *f; 614 char buf[80], *cp, *t; 615 616 f = fopen("/proc/filesystems", "r"); 617 if (!f) 618 return (0); 619 while (!feof(f)) { 620 if (!fgets(buf, sizeof(buf), f)) 621 break; 622 cp = buf; 623 if (!isspace(*cp)) { 624 while (*cp && !isspace(*cp)) 625 cp++; 626 } 627 while (*cp && isspace(*cp)) 628 cp++; 629 if ((t = strchr(cp, '\n')) != NULL) 630 *t = 0; 631 if ((t = strchr(cp, '\t')) != NULL) 632 *t = 0; 633 if ((t = strchr(cp, ' ')) != NULL) 634 *t = 0; 635 if (!strcmp(fs_name, cp)) { 636 fclose(f); 637 return (1); 638 } 639 } 640 fclose(f); 641 return (0); 642 } 643 644 /* 645 * Check to see if a filesystem is available as a module 646 * Returns 1 if found, 0 if not 647 */ 648 int check_for_modules(const char *fs_name) 649 { 650 #ifdef __linux__ 651 struct utsname uts; 652 FILE *f; 653 char buf[1024], *cp, *t; 654 int i; 655 656 if (uname(&uts)) 657 return (0); 658 snprintf(buf, sizeof(buf), "/lib/modules/%s/modules.dep", uts.release); 659 660 f = fopen(buf, "r"); 661 if (!f) 662 return (0); 663 while (!feof(f)) { 664 if (!fgets(buf, sizeof(buf), f)) 665 break; 666 if ((cp = strchr(buf, ':')) != NULL) 667 *cp = 0; 668 else 669 continue; 670 if ((cp = strrchr(buf, '/')) != NULL) 671 cp++; 672 else 673 cp = buf; 674 i = strlen(cp); 675 if (i > 3) { 676 t = cp + i - 3; 677 if (!strcmp(t, ".ko")) 678 *t = 0; 679 } 680 if (!strcmp(cp, fs_name)) { 681 fclose(f); 682 return (1); 683 } 684 } 685 fclose(f); 686 #endif /* __linux__ */ 687 return (0); 688 } 689 690 /* 691 * Helper function that does the right thing if write returns a 692 * partial write, or an EGAIN/EINTR error. 693 */ 694 int write_all(int fd, char *buf, size_t count) 695 { 696 ssize_t ret; 697 int c = 0; 698 699 while (count > 0) { 700 ret = write(fd, buf, count); 701 if (ret < 0) { 702 if ((errno == EAGAIN) || (errno == EINTR)) 703 continue; 704 return -1; 705 } 706 count -= ret; 707 buf += ret; 708 c += ret; 709 } 710 return c; 711 } 712