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 */
108 static 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
146 static 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 */
safe_print(const char * cp,int len)165 static 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 */
print_pathname(ext2_filsys fs,ext2_ino_t dir,ext2_ino_t ino)191 static 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 */
expand_at_expression(e2fsck_t ctx,char ch,struct problem_context * pctx,int * first,int recurse)215 static _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 */
expand_inode_expression(char ch,struct problem_context * ctx)240 static _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 */
expand_dirent_expression(char ch,struct problem_context * ctx)341 static _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
expand_percent_expression(ext2_filsys fs,char ch,struct problem_context * ctx)380 static _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
print_e2fsck_message(e2fsck_t ctx,const char * msg,struct problem_context * pctx,int first,int recurse)458 void 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