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