util.c revision efc6f628e15de95bcd13e4f0ee223cb42115d520
1/*
2 * util.c --- miscellaneous utilities
3 *
4 * Copyright (C) 1993, 1994, 1995, 1996, 1997 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
12#include <stdlib.h>
13#include <unistd.h>
14#include <string.h>
15#include <ctype.h>
16
17#ifdef HAVE_CONIO_H
18#undef HAVE_TERMIOS_H
19#include <conio.h>
20#define read_a_char()	getch()
21#else
22#ifdef HAVE_TERMIOS_H
23#include <termios.h>
24#endif
25#include <stdio.h>
26#endif
27
28#ifdef HAVE_MALLOC_H
29#include <malloc.h>
30#endif
31
32#ifdef HAVE_ERRNO_H
33#include <errno.h>
34#endif
35
36#include "e2fsck.h"
37
38extern e2fsck_t e2fsck_global_ctx;   /* Try your very best not to use this! */
39
40#include <sys/time.h>
41#include <sys/resource.h>
42
43void fatal_error(e2fsck_t ctx, const char *msg)
44{
45	if (msg)
46		fprintf (stderr, "e2fsck: %s\n", msg);
47	if (ctx->fs && ctx->fs->io) {
48		if (ctx->fs->io->magic == EXT2_ET_MAGIC_IO_CHANNEL)
49			io_channel_flush(ctx->fs->io);
50		else
51			fprintf(stderr, "e2fsck: io manager magic bad!\n");
52	}
53	ctx->flags |= E2F_FLAG_ABORT;
54	if (ctx->flags & E2F_FLAG_SETJMP_OK)
55		longjmp(ctx->abort_loc, 1);
56	exit(FSCK_ERROR);
57}
58
59void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
60			     const char *description)
61{
62	void *ret;
63	char buf[256];
64
65#ifdef DEBUG_ALLOCATE_MEMORY
66	printf("Allocating %d bytes for %s...\n", size, description);
67#endif
68	ret = malloc(size);
69	if (!ret) {
70		sprintf(buf, "Can't allocate %s\n", description);
71		fatal_error(ctx, buf);
72	}
73	memset(ret, 0, size);
74	return ret;
75}
76
77char *string_copy(e2fsck_t ctx EXT2FS_ATTR((unused)),
78		  const char *str, int len)
79{
80	char	*ret;
81
82	if (!str)
83		return NULL;
84	if (!len)
85		len = strlen(str);
86	ret = malloc(len+1);
87	if (ret) {
88		strncpy(ret, str, len);
89		ret[len] = 0;
90	}
91	return ret;
92}
93
94#ifndef HAVE_STRNLEN
95/*
96 * Incredibly, libc5 doesn't appear to have strnlen.  So we have to
97 * provide our own.
98 */
99int e2fsck_strnlen(const char * s, int count)
100{
101	const char *cp = s;
102
103	while (count-- && *cp)
104		cp++;
105	return cp - s;
106}
107#endif
108
109#ifndef HAVE_CONIO_H
110static int read_a_char(void)
111{
112	char	c;
113	int	r;
114	int	fail = 0;
115
116	while(1) {
117		if (e2fsck_global_ctx &&
118		    (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) {
119			return 3;
120		}
121		r = read(0, &c, 1);
122		if (r == 1)
123			return c;
124		if (fail++ > 100)
125			break;
126	}
127	return EOF;
128}
129#endif
130
131int ask_yn(const char * string, int def)
132{
133	int		c;
134	const char	*defstr;
135	const char	*short_yes = _("yY");
136	const char	*short_no = _("nN");
137
138#ifdef HAVE_TERMIOS_H
139	struct termios	termios, tmp;
140
141	tcgetattr (0, &termios);
142	tmp = termios;
143	tmp.c_lflag &= ~(ICANON | ECHO);
144	tmp.c_cc[VMIN] = 1;
145	tmp.c_cc[VTIME] = 0;
146	tcsetattr (0, TCSANOW, &tmp);
147#endif
148
149	if (def == 1)
150		defstr = _(_("<y>"));
151	else if (def == 0)
152		defstr = _(_("<n>"));
153	else
154		defstr = _(" (y/n)");
155	printf("%s%s? ", string, defstr);
156	while (1) {
157		fflush (stdout);
158		if ((c = read_a_char()) == EOF)
159			break;
160		if (c == 3) {
161#ifdef HAVE_TERMIOS_H
162			tcsetattr (0, TCSANOW, &termios);
163#endif
164			if (e2fsck_global_ctx &&
165			    e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) {
166				puts("\n");
167				longjmp(e2fsck_global_ctx->abort_loc, 1);
168			}
169			puts(_("cancelled!\n"));
170			return 0;
171		}
172		if (strchr(short_yes, (char) c)) {
173			def = 1;
174			break;
175		}
176		else if (strchr(short_no, (char) c)) {
177			def = 0;
178			break;
179		}
180		else if ((c == ' ' || c == '\n') && (def != -1))
181			break;
182	}
183	if (def)
184		puts(_("yes\n"));
185	else
186		puts (_("no\n"));
187#ifdef HAVE_TERMIOS_H
188	tcsetattr (0, TCSANOW, &termios);
189#endif
190	return def;
191}
192
193int ask (e2fsck_t ctx, const char * string, int def)
194{
195	if (ctx->options & E2F_OPT_NO) {
196		printf (_("%s? no\n\n"), string);
197		return 0;
198	}
199	if (ctx->options & E2F_OPT_YES) {
200		printf (_("%s? yes\n\n"), string);
201		return 1;
202	}
203	if (ctx->options & E2F_OPT_PREEN) {
204		printf ("%s? %s\n\n", string, def ? _("yes") : _("no"));
205		return def;
206	}
207	return ask_yn(string, def);
208}
209
210void e2fsck_read_bitmaps(e2fsck_t ctx)
211{
212	ext2_filsys fs = ctx->fs;
213	errcode_t	retval;
214	const char	*old_op;
215
216	if (ctx->invalid_bitmaps) {
217		com_err(ctx->program_name, 0,
218		    _("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"),
219			ctx->device_name);
220		fatal_error(ctx, 0);
221	}
222
223	old_op = ehandler_operation(_("reading inode and block bitmaps"));
224	retval = ext2fs_read_bitmaps(fs);
225	ehandler_operation(old_op);
226	if (retval) {
227		com_err(ctx->program_name, retval,
228			_("while retrying to read bitmaps for %s"),
229			ctx->device_name);
230		fatal_error(ctx, 0);
231	}
232}
233
234void e2fsck_write_bitmaps(e2fsck_t ctx)
235{
236	ext2_filsys fs = ctx->fs;
237	errcode_t	retval;
238	const char	*old_op;
239
240	old_op = ehandler_operation(_("writing block and inode bitmaps"));
241	retval = ext2fs_write_bitmaps(fs);
242	ehandler_operation(old_op);
243	if (retval) {
244		com_err(ctx->program_name, retval,
245			_("while rewriting block and inode bitmaps for %s"),
246			ctx->device_name);
247		fatal_error(ctx, 0);
248	}
249}
250
251void preenhalt(e2fsck_t ctx)
252{
253	ext2_filsys fs = ctx->fs;
254
255	if (!(ctx->options & E2F_OPT_PREEN))
256		return;
257	fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; "
258		"RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"),
259	       ctx->device_name);
260	if (fs != NULL) {
261		fs->super->s_state |= EXT2_ERROR_FS;
262		ext2fs_mark_super_dirty(fs);
263		ext2fs_close(fs);
264	}
265	exit(FSCK_UNCORRECTED);
266}
267
268#ifdef RESOURCE_TRACK
269void init_resource_track(struct resource_track *track, io_channel channel)
270{
271#ifdef HAVE_GETRUSAGE
272	struct rusage r;
273#endif
274	io_stats io_start = 0;
275
276	track->brk_start = sbrk(0);
277	gettimeofday(&track->time_start, 0);
278#ifdef HAVE_GETRUSAGE
279#ifdef sun
280	memset(&r, 0, sizeof(struct rusage));
281#endif
282	getrusage(RUSAGE_SELF, &r);
283	track->user_start = r.ru_utime;
284	track->system_start = r.ru_stime;
285#else
286	track->user_start.tv_sec = track->user_start.tv_usec = 0;
287	track->system_start.tv_sec = track->system_start.tv_usec = 0;
288#endif
289	track->bytes_read = 0;
290	track->bytes_written = 0;
291	if (channel && channel->manager && channel->manager->get_stats)
292		channel->manager->get_stats(channel, &io_start);
293	if (io_start) {
294		track->bytes_read = io_start->bytes_read;
295		track->bytes_written = io_start->bytes_written;
296	}
297}
298
299#ifdef __GNUC__
300#define _INLINE_ __inline__
301#else
302#define _INLINE_
303#endif
304
305static _INLINE_ float timeval_subtract(struct timeval *tv1,
306				       struct timeval *tv2)
307{
308	return ((tv1->tv_sec - tv2->tv_sec) +
309		((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000);
310}
311
312void print_resource_track(const char *desc, struct resource_track *track,
313			  io_channel channel)
314{
315#ifdef HAVE_GETRUSAGE
316	struct rusage r;
317#endif
318#ifdef HAVE_MALLINFO
319	struct mallinfo	malloc_info;
320#endif
321	struct timeval time_end;
322
323	gettimeofday(&time_end, 0);
324
325	if (desc)
326		printf("%s: ", desc);
327
328#ifdef HAVE_MALLINFO
329#define kbytes(x)	(((x) + 1023) / 1024)
330
331	malloc_info = mallinfo();
332	printf(_("Memory used: %dk/%dk (%dk/%dk), "),
333	       kbytes(malloc_info.arena), kbytes(malloc_info.hblkhd),
334	       kbytes(malloc_info.uordblks), kbytes(malloc_info.fordblks));
335#else
336	printf(_("Memory used: %d, "),
337	       (int) (((char *) sbrk(0)) - ((char *) track->brk_start)));
338#endif
339#ifdef HAVE_GETRUSAGE
340	getrusage(RUSAGE_SELF, &r);
341
342	printf(_("time: %5.2f/%5.2f/%5.2f\n"),
343	       timeval_subtract(&time_end, &track->time_start),
344	       timeval_subtract(&r.ru_utime, &track->user_start),
345	       timeval_subtract(&r.ru_stime, &track->system_start));
346#else
347	printf(_("elapsed time: %6.3f\n"),
348	       timeval_subtract(&time_end, &track->time_start));
349#endif
350#define mbytes(x)	(((x) + 1048575) / 1048576)
351	if (channel && channel->manager && channel->manager->get_stats) {
352		io_stats delta = 0;
353		unsigned long long bytes_read = 0;
354		unsigned long long bytes_written = 0;
355
356		if (desc)
357			printf("%s: ", desc);
358
359		channel->manager->get_stats(channel, &delta);
360		if (delta) {
361			bytes_read = delta->bytes_read - track->bytes_read;
362			bytes_written = delta->bytes_written -
363				track->bytes_written;
364		}
365		printf("I/O read: %lluMB, write: %lluMB, rate: %.2fMB/s\n",
366		       mbytes(bytes_read), mbytes(bytes_written),
367		       (double)mbytes(bytes_read + bytes_written) /
368		       timeval_subtract(&time_end, &track->time_start));
369	}
370}
371#endif /* RESOURCE_TRACK */
372
373void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
374			      struct ext2_inode * inode, const char *proc)
375{
376	int retval;
377
378	retval = ext2fs_read_inode(ctx->fs, ino, inode);
379	if (retval) {
380		com_err("ext2fs_read_inode", retval,
381			_("while reading inode %ld in %s"), ino, proc);
382		fatal_error(ctx, 0);
383	}
384}
385
386void e2fsck_read_inode_full(e2fsck_t ctx, unsigned long ino,
387			    struct ext2_inode *inode, int bufsize,
388			    const char *proc)
389{
390	int retval;
391
392	retval = ext2fs_read_inode_full(ctx->fs, ino, inode, bufsize);
393	if (retval) {
394		com_err("ext2fs_read_inode_full", retval,
395			_("while reading inode %ld in %s"), ino, proc);
396		fatal_error(ctx, 0);
397	}
398}
399
400extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
401			       struct ext2_inode * inode, int bufsize,
402			       const char *proc)
403{
404	int retval;
405
406	retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize);
407	if (retval) {
408		com_err("ext2fs_write_inode", retval,
409			_("while writing inode %ld in %s"), ino, proc);
410		fatal_error(ctx, 0);
411	}
412}
413
414extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
415			       struct ext2_inode * inode, const char *proc)
416{
417	int retval;
418
419	retval = ext2fs_write_inode(ctx->fs, ino, inode);
420	if (retval) {
421		com_err("ext2fs_write_inode", retval,
422			_("while writing inode %ld in %s"), ino, proc);
423		fatal_error(ctx, 0);
424	}
425}
426
427#ifdef MTRACE
428void mtrace_print(char *mesg)
429{
430	FILE	*malloc_get_mallstream();
431	FILE	*f = malloc_get_mallstream();
432
433	if (f)
434		fprintf(f, "============= %s\n", mesg);
435}
436#endif
437
438blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name,
439		   io_manager manager)
440{
441	struct ext2_super_block *sb;
442	io_channel		io = NULL;
443	void			*buf = NULL;
444	int			blocksize;
445	blk_t			superblock, ret_sb = 8193;
446
447	if (fs && fs->super) {
448		ret_sb = (fs->super->s_blocks_per_group +
449			  fs->super->s_first_data_block);
450		if (ctx) {
451			ctx->superblock = ret_sb;
452			ctx->blocksize = fs->blocksize;
453		}
454		return ret_sb;
455	}
456
457	if (ctx) {
458		if (ctx->blocksize) {
459			ret_sb = ctx->blocksize * 8;
460			if (ctx->blocksize == 1024)
461				ret_sb++;
462			ctx->superblock = ret_sb;
463			return ret_sb;
464		}
465		ctx->superblock = ret_sb;
466		ctx->blocksize = 1024;
467	}
468
469	if (!name || !manager)
470		goto cleanup;
471
472	if (manager->open(name, 0, &io) != 0)
473		goto cleanup;
474
475	if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
476		goto cleanup;
477	sb = (struct ext2_super_block *) buf;
478
479	for (blocksize = EXT2_MIN_BLOCK_SIZE;
480	     blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) {
481		superblock = blocksize*8;
482		if (blocksize == 1024)
483			superblock++;
484		io_channel_set_blksize(io, blocksize);
485		if (io_channel_read_blk(io, superblock,
486					-SUPERBLOCK_SIZE, buf))
487			continue;
488#ifdef WORDS_BIGENDIAN
489		if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
490			ext2fs_swap_super(sb);
491#endif
492		if ((sb->s_magic == EXT2_SUPER_MAGIC) &&
493		    (EXT2_BLOCK_SIZE(sb) == blocksize)) {
494			ret_sb = superblock;
495			if (ctx) {
496				ctx->superblock = superblock;
497				ctx->blocksize = blocksize;
498			}
499			break;
500		}
501	}
502
503cleanup:
504	if (io)
505		io_channel_close(io);
506	if (buf)
507		ext2fs_free_mem(&buf);
508	return (ret_sb);
509}
510
511/*
512 * Given a mode, return the ext2 file type
513 */
514int ext2_file_type(unsigned int mode)
515{
516	if (LINUX_S_ISREG(mode))
517		return EXT2_FT_REG_FILE;
518
519	if (LINUX_S_ISDIR(mode))
520		return EXT2_FT_DIR;
521
522	if (LINUX_S_ISCHR(mode))
523		return EXT2_FT_CHRDEV;
524
525	if (LINUX_S_ISBLK(mode))
526		return EXT2_FT_BLKDEV;
527
528	if (LINUX_S_ISLNK(mode))
529		return EXT2_FT_SYMLINK;
530
531	if (LINUX_S_ISFIFO(mode))
532		return EXT2_FT_FIFO;
533
534	if (LINUX_S_ISSOCK(mode))
535		return EXT2_FT_SOCK;
536
537	return 0;
538}
539
540#define STRIDE_LENGTH 8
541/*
542 * Helper function which zeros out _num_ blocks starting at _blk_.  In
543 * case of an error, the details of the error is returned via _ret_blk_
544 * and _ret_count_ if they are non-NULL pointers.  Returns 0 on
545 * success, and an error code on an error.
546 *
547 * As a special case, if the first argument is NULL, then it will
548 * attempt to free the static zeroizing buffer.  (This is to keep
549 * programs that check for memory leaks happy.)
550 */
551errcode_t e2fsck_zero_blocks(ext2_filsys fs, blk_t blk, int num,
552			     blk_t *ret_blk, int *ret_count)
553{
554	int		j, count, next_update, next_update_incr;
555	static char	*buf;
556	errcode_t	retval;
557
558	/* If fs is null, clean up the static buffer and return */
559	if (!fs) {
560		if (buf) {
561			free(buf);
562			buf = 0;
563		}
564		return 0;
565	}
566	/* Allocate the zeroizing buffer if necessary */
567	if (!buf) {
568		buf = malloc(fs->blocksize * STRIDE_LENGTH);
569		if (!buf) {
570			com_err("malloc", ENOMEM,
571				_("while allocating zeroizing buffer"));
572			exit(1);
573		}
574		memset(buf, 0, fs->blocksize * STRIDE_LENGTH);
575	}
576	/* OK, do the write loop */
577	next_update = 0;
578	next_update_incr = num / 100;
579	if (next_update_incr < 1)
580		next_update_incr = 1;
581	for (j = 0; j < num; j += STRIDE_LENGTH, blk += STRIDE_LENGTH) {
582		count = num - j;
583		if (count > STRIDE_LENGTH)
584			count = STRIDE_LENGTH;
585		retval = io_channel_write_blk(fs->io, blk, count, buf);
586		if (retval) {
587			if (ret_count)
588				*ret_count = count;
589			if (ret_blk)
590				*ret_blk = blk;
591			return retval;
592		}
593	}
594	return 0;
595}
596