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