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