message.c revision ecf1b7767e1772f4c6dba8f02026057ed05397bd
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 * 	%Ib	<inode> -> i_blocks
29 * 	%Il	<inode> -> i_links_count
30 * 	%Im	<inode> -> i_mode
31 * 	%IM	<inode> -> i_mtime
32 * 	%IF	<inode> -> i_faddr
33 * 	%If	<inode> -> i_file_acl
34 * 	%Id	<inode> -> i_dir_acl
35 * 	%Iu	<inode> -> i_uid
36 * 	%Ig	<inode> -> i_gid
37 * 	%j	<ino2>			inode number
38 * 	%m	<com_err error message>
39 * 	%N	<num>
40 *	%p	ext2fs_get_pathname of directory <ino>
41 * 	%P	ext2fs_get_pathname of <dirent>->ino with <ino2> as
42 * 			the containing directory.  (If dirent is NULL
43 * 			then return the pathname of directory <ino2>)
44 * 	%q	ext2fs_get_pathname of directory <dir>
45 * 	%Q	ext2fs_get_pathname of directory <ino> with <dir> as
46 * 			the containing directory.
47 * 	%s	<str>			miscellaneous string
48 * 	%S	backup superblock
49 * 	%X	<num> hexadecimal format
50 *
51 * The following '@' expansions are supported:
52 *
53 * 	@A	error allocating
54 * 	@b	block
55 * 	@B	bitmap
56 * 	@c	compress
57 * 	@C	conflicts with some other fs block
58 * 	@i	inode
59 * 	@I	illegal
60 * 	@D	deleted
61 * 	@d	directory
62 * 	@e	entry
63 * 	@E	Entry '%Dn' in %p (%i)
64 * 	@f	filesystem
65 * 	@F	for @i %i (%Q) is
66 * 	@g	group
67 * 	@l	lost+found
68 * 	@L	is a link
69 * 	@o	orphaned
70 * 	@r	root inode
71 * 	@s	should be
72 * 	@S	superblock
73 * 	@u	unattached
74 * 	@z	zero-length
75 */
76
77#include <stdlib.h>
78#include <unistd.h>
79#include <string.h>
80#include <ctype.h>
81#include <termios.h>
82
83#include "e2fsck.h"
84
85#include "problem.h"
86
87#ifdef __GNUC__
88#define _INLINE_ __inline__
89#else
90#define _INLINE_
91#endif
92
93/*
94 * This structure defines the abbreviations used by the text strings
95 * below.  The first character in the string is the index letter.  An
96 * abbreviation of the form '@<i>' is expanded by looking up the index
97 * letter <i> in the table below.
98 */
99static const char *abbrevs[] = {
100	N_("Aerror allocating"),
101	N_("bblock"),
102	N_("Bbitmap"),
103	N_("ccompress"),
104	N_("Cconflicts with some other fs @b"),
105	N_("iinode"),
106	N_("Iillegal"),
107	N_("Ddeleted"),
108	N_("ddirectory"),
109	N_("eentry"),
110	N_("E@e '%Dn' in %p (%i)"),
111	N_("ffilesystem"),
112	N_("Ffor @i %i (%Q) is"),
113	N_("ggroup"),
114	N_("llost+found"),
115	N_("Lis a link"),
116	N_("oorphaned"),
117	N_("rroot @i"),
118	N_("sshould be"),
119	N_("Ssuper@b"),
120	N_("uunattached"),
121	N_("zzero-length"),
122	"@@",
123	0
124	};
125
126/*
127 * Give more user friendly names to the "special" inodes.
128 */
129#define num_special_inodes	7
130static const char *special_inode_name[] =
131{
132	N_("<The NULL inode>"),			/* 0 */
133	N_("<The bad blocks inode>"),		/* 1 */
134	"/",					/* 2 */
135	N_("<The ACL index inode>"),		/* 3 */
136	N_("<The ACL data inode>"),		/* 4 */
137	N_("<The boot loader inode>"),		/* 5 */
138	N_("<The undelete directory inode>")	/* 6 */
139};
140
141/*
142 * This function does "safe" printing.  It will convert non-printable
143 * ASCII characters using '^' and M- notation.
144 */
145static void safe_print(const char *cp, int len)
146{
147	unsigned char	ch;
148
149	if (len < 0)
150		len = strlen(cp);
151
152	while (len--) {
153		ch = *cp++;
154		if (ch > 128) {
155			fputs("M-", stdout);
156			ch -= 128;
157		}
158		if ((ch < 32) || (ch == 0x7f)) {
159			fputc('^', stdout);
160			ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
161		}
162		fputc(ch, stdout);
163	}
164}
165
166
167/*
168 * This function prints a pathname, using the ext2fs_get_pathname
169 * function
170 */
171static void print_pathname(ext2_filsys fs, ino_t dir, ino_t ino)
172{
173	errcode_t	retval;
174	char		*path;
175
176	if (!dir && (ino < num_special_inodes)) {
177		fputs(_(special_inode_name[ino]), stdout);
178		return;
179	}
180
181	retval = ext2fs_get_pathname(fs, dir, ino, &path);
182	if (retval)
183		fputs("???", stdout);
184	else {
185		safe_print(path, -1);
186		ext2fs_free_mem((void **) &path);
187	}
188}
189
190/*
191 * This function handles the '@' expansion.  We allow recursive
192 * expansion; an @ expression can contain further '@' and '%'
193 * expressions.
194 */
195static _INLINE_ void expand_at_expression(e2fsck_t ctx, char ch,
196					  struct problem_context *pctx,
197					  int *first)
198{
199	const char **cpp, *str;
200
201	/* Search for the abbreviation */
202	for (cpp = abbrevs; *cpp; cpp++) {
203		if (ch == *cpp[0])
204			break;
205	}
206	if (*cpp) {
207		str = (*cpp) + 1;
208		if (*first && islower(*str)) {
209			*first = 0;
210			fputc(toupper(*str++), stdout);
211		}
212		print_e2fsck_message(ctx, _(str), pctx, *first);
213	} else
214		printf("@%c", ch);
215}
216
217/*
218 * This function expands '%IX' expressions
219 */
220static _INLINE_ void expand_inode_expression(char ch,
221					       struct problem_context *ctx)
222{
223	struct ext2_inode	*inode;
224	char *			time_str;
225	time_t			t;
226
227	if (!ctx || !ctx->inode)
228		goto no_inode;
229
230	inode = ctx->inode;
231
232	switch (ch) {
233	case 's':
234		if (LINUX_S_ISDIR(inode->i_mode))
235			printf("%u", inode->i_size);
236		else {
237#ifdef EXT2_NO_64_TYPE
238			if (inode->i_size_high)
239				printf("0x%x%08x", inode->i_size_high,
240				       inode->i_size);
241			else
242				printf("%u", inode->i_size);
243#else
244			printf("%llu", (inode->i_size |
245					((__u64) inode->i_size_high << 32)));
246#endif
247		}
248		break;
249	case 'b':
250		printf("%u", inode->i_blocks);
251		break;
252	case 'l':
253		printf("%d", inode->i_links_count);
254		break;
255	case 'm':
256		printf("0%o", inode->i_mode);
257		break;
258	case 'M':
259		t = inode->i_mtime;
260		time_str = ctime(&t);
261		printf("%.24s", time_str);
262		break;
263	case 'F':
264		printf("%u", inode->i_faddr);
265		break;
266	case 'f':
267		printf("%u", inode->i_file_acl);
268		break;
269	case 'd':
270		printf("%u", (LINUX_S_ISDIR(inode->i_mode) ?
271			      inode->i_dir_acl : 0));
272		break;
273	case 'u':
274		printf("%d", (inode->i_uid |
275			      (inode->osd2.linux2.l_i_uid_high << 16)));
276		break;
277	case 'g':
278		printf("%d", (inode->i_gid |
279			      (inode->osd2.linux2.l_i_gid_high << 16)));
280		break;
281	default:
282	no_inode:
283		printf("%%I%c", ch);
284		break;
285	}
286}
287
288/*
289 * This function expands '%dX' expressions
290 */
291static _INLINE_ void expand_dirent_expression(char ch,
292					      struct problem_context *ctx)
293{
294	struct ext2_dir_entry	*dirent;
295	int	len;
296
297	if (!ctx || !ctx->dirent)
298		goto no_dirent;
299
300	dirent = ctx->dirent;
301
302	switch (ch) {
303	case 'i':
304		printf("%u", dirent->inode);
305		break;
306	case 'n':
307		len = dirent->name_len & 0xFF;
308		if (len > EXT2_NAME_LEN)
309			len = EXT2_NAME_LEN;
310		if (len > dirent->rec_len)
311			len = dirent->rec_len;
312		safe_print(dirent->name, len);
313		break;
314	case 'r':
315		printf("%u", dirent->rec_len);
316		break;
317	case 'l':
318		printf("%u", dirent->name_len & 0xFF);
319		break;
320	case 't':
321		printf("%u", dirent->name_len >> 8);
322		break;
323	default:
324	no_dirent:
325		printf("%%D%c", ch);
326		break;
327	}
328}
329
330static _INLINE_ void expand_percent_expression(ext2_filsys fs, char ch,
331					       struct problem_context *ctx)
332{
333	if (!ctx)
334		goto no_context;
335
336	switch (ch) {
337	case '%':
338		fputc('%', stdout);
339		break;
340	case 'b':
341		printf("%u", ctx->blk);
342		break;
343	case 'B':
344#ifdef EXT2_NO_64_TYPE
345		printf("%d", ctx->blkcount);
346#else
347		printf("%lld", ctx->blkcount);
348#endif
349		break;
350	case 'c':
351		printf("%u", ctx->blk2);
352		break;
353	case 'd':
354		printf("%lu", ctx->dir);
355		break;
356	case 'g':
357		printf("%d", ctx->group);
358		break;
359	case 'i':
360		printf("%lu", ctx->ino);
361		break;
362	case 'j':
363		printf("%lu", ctx->ino2);
364		break;
365	case 'm':
366		printf("%s", error_message(ctx->errcode));
367		break;
368	case 'N':
369#ifdef EXT2_NO_64_TYPE
370		printf("%u", ctx->num);
371#else
372		printf("%llu", ctx->num);
373#endif
374		break;
375	case 'p':
376		print_pathname(fs, ctx->ino, 0);
377		break;
378	case 'P':
379		print_pathname(fs, ctx->ino2,
380			       ctx->dirent ? ctx->dirent->inode : 0);
381		break;
382	case 'q':
383		print_pathname(fs, ctx->dir, 0);
384		break;
385	case 'Q':
386		print_pathname(fs, ctx->dir, ctx->ino);
387		break;
388	case 'S':
389		printf("%d", get_backup_sb(fs));
390		break;
391	case 's':
392		printf("%s", ctx->str);
393		break;
394	case 'X':
395#ifdef EXT2_NO_64_TYPE
396		printf("0x%x", ctx->num);
397#else
398		printf("0x%llx", ctx->num);
399#endif
400		break;
401	default:
402	no_context:
403		printf("%%%c", ch);
404		break;
405	}
406}
407
408void print_e2fsck_message(e2fsck_t ctx, const char *msg,
409			  struct problem_context *pctx, int first)
410{
411	ext2_filsys fs = ctx->fs;
412	const char *	cp;
413	int		i;
414
415	e2fsck_clear_progbar(ctx);
416	for (cp = msg; *cp; cp++) {
417		if (cp[0] == '@') {
418			cp++;
419			expand_at_expression(ctx, *cp, pctx, &first);
420		} else if (cp[0] == '%' && cp[1] == 'I') {
421			cp += 2;
422			expand_inode_expression(*cp, pctx);
423		} else if (cp[0] == '%' && cp[1] == 'D') {
424			cp += 2;
425			expand_dirent_expression(*cp, pctx);
426		} else if ((cp[0] == '%')) {
427			cp++;
428			expand_percent_expression(fs, *cp, pctx);
429		} else {
430			for (i=0; cp[i]; i++)
431				if ((cp[i] == '@') || cp[i] == '%')
432					break;
433			printf("%.*s", i, cp);
434			cp += i-1;
435		}
436		first = 0;
437	}
438}
439