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