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