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