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 41extern 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 46void 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 62void *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 80char *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 */ 102int 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 113static 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 134int 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 196int 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 213void 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 237void 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 254void 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 273void 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 309static _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 316void 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 383void 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 396void 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 410extern 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 424extern 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 438void 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 448blk_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 513cleanup: 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 */ 524int 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 */ 561errcode_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 */ 611int 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 */ 648int 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 */ 694int 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