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