message.c revision 4dbfd79d1458ce1259b951377e341aeb6197f8c1
1/* 2 * message.c --- print e2fsck messages (with compression) 3 * 4 * Copyright 1996, 1997 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 * print_e2fsck_message() prints a message to the user, using 12 * compression techniques and expansions of abbreviations. 13 * 14 * The following % expansions are supported: 15 * 16 * %b <blk> block number 17 * %B <blkcount> interpret blkcount as blkcount 18 * %c <blk2> block number 19 * %Di <dirent>->ino inode number 20 * %Dn <dirent>->name string 21 * %Dr <dirent>->rec_len 22 * %Dl <dirent>->name_len 23 * %Dt <dirent>->filetype 24 * %d <dir> inode number 25 * %g <group> integer 26 * %i <ino> inode number 27 * %Is <inode> -> i_size 28 * %IS <inode> -> i_extra_isize 29 * %Ib <inode> -> i_blocks 30 * %Il <inode> -> i_links_count 31 * %Im <inode> -> i_mode 32 * %IM <inode> -> i_mtime 33 * %IF <inode> -> i_faddr 34 * %If <inode> -> i_file_acl 35 * %Id <inode> -> i_dir_acl 36 * %Iu <inode> -> i_uid 37 * %Ig <inode> -> i_gid 38 * %It <inode type> 39 * %j <ino2> inode number 40 * %m <com_err error message> 41 * %N <num> 42 * %p ext2fs_get_pathname of directory <ino> 43 * %P ext2fs_get_pathname of <dirent>->ino with <ino2> as 44 * the containing directory. (If dirent is NULL 45 * then return the pathname of directory <ino2>) 46 * %q ext2fs_get_pathname of directory <dir> 47 * %Q ext2fs_get_pathname of directory <ino> with <dir> as 48 * the containing directory. 49 * %r <blkcount> interpret blkcount as refcount 50 * %s <str> miscellaneous string 51 * %S backup superblock 52 * %X <num> hexadecimal format 53 * 54 * The following '@' expansions are supported: 55 * 56 * @a extended attribute 57 * @A error allocating 58 * @b block 59 * @B bitmap 60 * @c compress 61 * @C conflicts with some other fs block 62 * @D deleted 63 * @d directory 64 * @e entry 65 * @E Entry '%Dn' in %p (%i) 66 * @f filesystem 67 * @F for @i %i (%Q) is 68 * @g group 69 * @h HTREE directory inode 70 * @i inode 71 * @I illegal 72 * @j journal 73 * @l lost+found 74 * @L is a link 75 * @m multiply-claimed 76 * @n invalid 77 * @o orphaned 78 * @p problem in 79 * @q quota 80 * @r root inode 81 * @s should be 82 * @S superblock 83 * @u unattached 84 * @v device 85 * @x extent 86 * @z zero-length 87 */ 88 89#include "config.h" 90#include <stdlib.h> 91#include <unistd.h> 92#include <string.h> 93#include <ctype.h> 94#include <termios.h> 95 96#include "e2fsck.h" 97 98#include "problem.h" 99 100#ifdef __GNUC__ 101#define _INLINE_ __inline__ 102#else 103#define _INLINE_ 104#endif 105 106/* 107 * This structure defines the abbreviations used by the text strings 108 * below. The first character in the string is the index letter. An 109 * abbreviation of the form '@<i>' is expanded by looking up the index 110 * letter <i> in the table below. 111 */ 112static const char *abbrevs[] = { 113 N_("aextended attribute"), 114 N_("Aerror allocating"), 115 N_("bblock"), 116 N_("Bbitmap"), 117 N_("ccompress"), 118 N_("Cconflicts with some other fs @b"), 119 N_("iinode"), 120 N_("Iillegal"), 121 N_("jjournal"), 122 N_("Ddeleted"), 123 N_("ddirectory"), 124 N_("eentry"), 125 N_("E@e '%Dn' in %p (%i)"), 126 N_("ffilesystem"), 127 N_("Ffor @i %i (%Q) is"), 128 N_("ggroup"), 129 N_("hHTREE @d @i"), 130 N_("llost+found"), 131 N_("Lis a link"), 132 N_("mmultiply-claimed"), 133 N_("ninvalid"), 134 N_("oorphaned"), 135 N_("pproblem in"), 136 N_("qquota"), 137 N_("rroot @i"), 138 N_("sshould be"), 139 N_("Ssuper@b"), 140 N_("uunattached"), 141 N_("vdevice"), 142 N_("xextent"), 143 N_("zzero-length"), 144 "@@", 145 0 146 }; 147 148/* 149 * Give more user friendly names to the "special" inodes. 150 */ 151#define num_special_inodes 11 152static const char *special_inode_name[] = 153{ 154 N_("<The NULL inode>"), /* 0 */ 155 N_("<The bad blocks inode>"), /* 1 */ 156 "/", /* 2 */ 157 N_("<The user quota inode>"), /* 3 */ 158 N_("<The group quota inode>"), /* 4 */ 159 N_("<The boot loader inode>"), /* 5 */ 160 N_("<The undelete directory inode>"), /* 6 */ 161 N_("<The group descriptor inode>"), /* 7 */ 162 N_("<The journal inode>"), /* 8 */ 163 N_("<Reserved inode 9>"), /* 9 */ 164 N_("<Reserved inode 10>"), /* 10 */ 165}; 166 167/* 168 * This function does "safe" printing. It will convert non-printable 169 * ASCII characters using '^' and M- notation. 170 */ 171static void safe_print(FILE *f, const char *cp, int len) 172{ 173 unsigned char ch; 174 175 if (len < 0) 176 len = strlen(cp); 177 178 while (len--) { 179 ch = *cp++; 180 if (ch > 128) { 181 fputs("M-", f); 182 ch -= 128; 183 } 184 if ((ch < 32) || (ch == 0x7f)) { 185 fputc('^', f); 186 ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */ 187 } 188 fputc(ch, f); 189 } 190} 191 192 193/* 194 * This function prints a pathname, using the ext2fs_get_pathname 195 * function 196 */ 197static void print_pathname(FILE *f, ext2_filsys fs, ext2_ino_t dir, 198 ext2_ino_t ino) 199{ 200 errcode_t retval = 0; 201 char *path; 202 203 if (!dir && (ino < num_special_inodes)) { 204 fputs(_(special_inode_name[ino]), f); 205 return; 206 } 207 208 if (fs) 209 retval = ext2fs_get_pathname(fs, dir, ino, &path); 210 if (!fs || retval) 211 fputs("???", f); 212 else { 213 safe_print(f, path, -1); 214 ext2fs_free_mem(&path); 215 } 216} 217 218static void print_time(FILE *f, time_t t) 219{ 220 const char * time_str; 221 static int do_gmt = -1; 222 223#ifdef __dietlibc__ 224 /* The diet libc doesn't respect the TZ environemnt variable */ 225 if (do_gmt == -1) { 226 time_str = getenv("TZ"); 227 if (!time_str) 228 time_str = ""; 229 do_gmt = !strcmp(time_str, "GMT0"); 230 } 231#endif 232 time_str = asctime((do_gmt > 0) ? gmtime(&t) : localtime(&t)); 233 fprintf(f, "%.24s", time_str); 234} 235 236/* 237 * This function handles the '@' expansion. We allow recursive 238 * expansion; an @ expression can contain further '@' and '%' 239 * expressions. 240 */ 241static _INLINE_ void expand_at_expression(FILE *f, e2fsck_t ctx, char ch, 242 struct problem_context *pctx, 243 int *first, int recurse) 244{ 245 const char **cpp, *str; 246 247 /* Search for the abbreviation */ 248 for (cpp = abbrevs; *cpp; cpp++) { 249 if (ch == *cpp[0]) 250 break; 251 } 252 if (*cpp && recurse < 10) { 253 str = _(*cpp) + 1; 254 if (*first && islower(*str)) { 255 *first = 0; 256 fputc(toupper(*str++), f); 257 } 258 print_e2fsck_message(f, ctx, str, pctx, *first, recurse+1); 259 } else 260 fprintf(f, "@%c", ch); 261} 262 263/* 264 * This function expands '%IX' expressions 265 */ 266static _INLINE_ void expand_inode_expression(FILE *f, ext2_filsys fs, char ch, 267 struct problem_context *ctx) 268{ 269 struct ext2_inode *inode; 270 struct ext2_inode_large *large_inode; 271 272 if (!ctx || !ctx->inode) 273 goto no_inode; 274 275 inode = ctx->inode; 276 large_inode = (struct ext2_inode_large *) inode; 277 278 switch (ch) { 279 case 's': 280 if (LINUX_S_ISDIR(inode->i_mode)) 281 fprintf(f, "%u", inode->i_size); 282 else { 283#ifdef EXT2_NO_64_TYPE 284 if (inode->i_size_high) 285 fprintf(f, "0x%x%08x", inode->i_size_high, 286 inode->i_size); 287 else 288 fprintf(f, "%u", inode->i_size); 289#else 290 fprintf(f, "%llu", EXT2_I_SIZE(inode)); 291#endif 292 } 293 break; 294 case 'S': 295 fprintf(f, "%u", large_inode->i_extra_isize); 296 break; 297 case 'b': 298 if (fs->super->s_feature_ro_compat & 299 EXT4_FEATURE_RO_COMPAT_HUGE_FILE) 300 fprintf(f, "%llu", inode->i_blocks + 301 (((long long) inode->osd2.linux2.l_i_blocks_hi) 302 << 32)); 303 else 304 fprintf(f, "%u", inode->i_blocks); 305 break; 306 case 'l': 307 fprintf(f, "%d", inode->i_links_count); 308 break; 309 case 'm': 310 fprintf(f, "0%o", inode->i_mode); 311 break; 312 case 'M': 313 print_time(f, inode->i_mtime); 314 break; 315 case 'F': 316 fprintf(f, "%u", inode->i_faddr); 317 break; 318 case 'f': 319 fprintf(f, "%llu", ext2fs_file_acl_block(fs, inode)); 320 break; 321 case 'd': 322 fprintf(f, "%u", (LINUX_S_ISDIR(inode->i_mode) ? 323 inode->i_dir_acl : 0)); 324 break; 325 case 'u': 326 fprintf(f, "%d", inode_uid(*inode)); 327 break; 328 case 'g': 329 fprintf(f, "%d", inode_gid(*inode)); 330 break; 331 case 't': 332 if (LINUX_S_ISREG(inode->i_mode)) 333 fputs(_("regular file"), f); 334 else if (LINUX_S_ISDIR(inode->i_mode)) 335 fputs(_("directory"), f); 336 else if (LINUX_S_ISCHR(inode->i_mode)) 337 fputs(_("character device"), f); 338 else if (LINUX_S_ISBLK(inode->i_mode)) 339 fputs(_("block device"), f); 340 else if (LINUX_S_ISFIFO(inode->i_mode)) 341 fputs(_("named pipe"), f); 342 else if (LINUX_S_ISLNK(inode->i_mode)) 343 fputs(_("symbolic link"), f); 344 else if (LINUX_S_ISSOCK(inode->i_mode)) 345 fputs(_("socket"), f); 346 else 347 fprintf(f, _("unknown file type with mode 0%o"), 348 inode->i_mode); 349 break; 350 default: 351 no_inode: 352 fprintf(f, "%%I%c", ch); 353 break; 354 } 355} 356 357/* 358 * This function expands '%dX' expressions 359 */ 360static _INLINE_ void expand_dirent_expression(FILE *f, ext2_filsys fs, char ch, 361 struct problem_context *ctx) 362{ 363 struct ext2_dir_entry *dirent; 364 unsigned int rec_len, len; 365 366 if (!ctx || !ctx->dirent) 367 goto no_dirent; 368 369 dirent = ctx->dirent; 370 371 switch (ch) { 372 case 'i': 373 fprintf(f, "%u", dirent->inode); 374 break; 375 case 'n': 376 len = dirent->name_len & 0xFF; 377 if ((ext2fs_get_rec_len(fs, dirent, &rec_len) == 0) && 378 (len > rec_len)) 379 len = rec_len; 380 safe_print(f, dirent->name, len); 381 break; 382 case 'r': 383 (void) ext2fs_get_rec_len(fs, dirent, &rec_len); 384 fprintf(f, "%u", rec_len); 385 break; 386 case 'l': 387 fprintf(f, "%u", dirent->name_len & 0xFF); 388 break; 389 case 't': 390 fprintf(f, "%u", dirent->name_len >> 8); 391 break; 392 default: 393 no_dirent: 394 fprintf(f, "%%D%c", ch); 395 break; 396 } 397} 398 399static _INLINE_ void expand_percent_expression(FILE *f, ext2_filsys fs, 400 char ch, int width, int *first, 401 struct problem_context *ctx) 402{ 403 e2fsck_t e2fsck_ctx = fs ? (e2fsck_t) fs->priv_data : NULL; 404 const char *m; 405 406 if (!ctx) 407 goto no_context; 408 409 switch (ch) { 410 case '%': 411 fputc('%', f); 412 break; 413 case 'b': 414#ifdef EXT2_NO_64_TYPE 415 fprintf(f, "%*u", width, (unsigned long) ctx->blk); 416#else 417 fprintf(f, "%*llu", width, (unsigned long long) ctx->blk); 418#endif 419 break; 420 case 'B': 421 if (ctx->blkcount == BLOCK_COUNT_IND) 422 m = _("indirect block"); 423 else if (ctx->blkcount == BLOCK_COUNT_DIND) 424 m = _("double indirect block"); 425 else if (ctx->blkcount == BLOCK_COUNT_TIND) 426 m = _("triple indirect block"); 427 else if (ctx->blkcount == BLOCK_COUNT_TRANSLATOR) 428 m = _("translator block"); 429 else 430 m = _("block #"); 431 if (*first && islower(m[0])) 432 fputc(toupper(*m++), f); 433 fputs(m, f); 434 if (ctx->blkcount >= 0) { 435#ifdef EXT2_NO_64_TYPE 436 fprintf(f, "%d", ctx->blkcount); 437#else 438 fprintf(f, "%lld", (long long) ctx->blkcount); 439#endif 440 } 441 break; 442 case 'c': 443#ifdef EXT2_NO_64_TYPE 444 fprintf(f, "%*u", width, (unsigned long) ctx->blk2); 445#else 446 fprintf(f, "%*llu", width, (unsigned long long) ctx->blk2); 447#endif 448 break; 449 case 'd': 450 fprintf(f, "%*u", width, ctx->dir); 451 break; 452 case 'g': 453 fprintf(f, "%*d", width, ctx->group); 454 break; 455 case 'i': 456 fprintf(f, "%*u", width, ctx->ino); 457 break; 458 case 'j': 459 fprintf(f, "%*u", width, ctx->ino2); 460 break; 461 case 'm': 462 fprintf(f, "%*s", width, error_message(ctx->errcode)); 463 break; 464 case 'N': 465#ifdef EXT2_NO_64_TYPE 466 fprintf(f, "%*u", width, ctx->num); 467#else 468 fprintf(f, "%*llu", width, (long long)ctx->num); 469#endif 470 break; 471 case 'p': 472 print_pathname(f, fs, ctx->ino, 0); 473 break; 474 case 'P': 475 print_pathname(f, fs, ctx->ino2, 476 ctx->dirent ? ctx->dirent->inode : 0); 477 break; 478 case 'q': 479 print_pathname(f, fs, ctx->dir, 0); 480 break; 481 case 'Q': 482 print_pathname(f, fs, ctx->dir, ctx->ino); 483 break; 484 case 'r': 485#ifdef EXT2_NO_64_TYPE 486 fprintf(f, "%*d", width, ctx->blkcount); 487#else 488 fprintf(f, "%*lld", width, (long long) ctx->blkcount); 489#endif 490 break; 491 case 'S': 492 fprintf(f, "%llu", get_backup_sb(NULL, fs, NULL, NULL)); 493 break; 494 case 's': 495 fprintf(f, "%*s", width, ctx->str ? ctx->str : "NULL"); 496 break; 497 case 't': 498 print_time(f, (time_t) ctx->num); 499 break; 500 case 'T': 501 print_time(f, e2fsck_ctx ? e2fsck_ctx->now : time(0)); 502 break; 503 case 'x': 504 fprintf(f, "0x%0*x", width, ctx->csum1); 505 break; 506 case 'X': 507#ifdef EXT2_NO_64_TYPE 508 fprintf(f, "0x%0*x", width, ctx->num); 509#else 510 fprintf(f, "0x%0*llx", width, (long long)ctx->num); 511#endif 512 break; 513 case 'y': 514 fprintf(f, "0x%0*x", width, ctx->csum2); 515 break; 516 default: 517 no_context: 518 fprintf(f, "%%%c", ch); 519 break; 520 } 521} 522 523void print_e2fsck_message(FILE *f, e2fsck_t ctx, const char *msg, 524 struct problem_context *pctx, int first, 525 int recurse) 526{ 527 ext2_filsys fs = ctx->fs; 528 const char * cp; 529 int i, width; 530 531 e2fsck_clear_progbar(ctx); 532 for (cp = msg; *cp; cp++) { 533 if (cp[0] == '@') { 534 cp++; 535 expand_at_expression(f, ctx, *cp, pctx, &first, 536 recurse); 537 } else if (cp[0] == '%') { 538 cp++; 539 width = 0; 540 while (isdigit(cp[0])) { 541 width = (width * 10) + cp[0] - '0'; 542 cp++; 543 } 544 if (cp[0] == 'I') { 545 cp++; 546 expand_inode_expression(f, fs, *cp, pctx); 547 } else if (cp[0] == 'D') { 548 cp++; 549 expand_dirent_expression(f, fs, *cp, pctx); 550 } else { 551 expand_percent_expression(f, fs, *cp, width, 552 &first, pctx); 553 } 554 } else { 555 for (i=0; cp[i]; i++) 556 if ((cp[i] == '@') || cp[i] == '%') 557 break; 558 fprintf(f, "%.*s", i, cp); 559 cp += i-1; 560 } 561 first = 0; 562 } 563} 564