util.c revision ec7fdb8f76ea88ba838690ae151735ae5ef800ba
1/*
2 * util.c --- utilities for the debugfs program
3 *
4 * Copyright (C) 1993, 1994 Theodore Ts'o.  This file may be
5 * redistributed under the terms of the GNU Public License.
6 *
7 */
8
9#include <stdio.h>
10#include <unistd.h>
11#include <stdlib.h>
12#include <ctype.h>
13#include <string.h>
14#include <time.h>
15#include <signal.h>
16#ifdef HAVE_GETOPT_H
17#include <getopt.h>
18#else
19extern int optind;
20extern char *optarg;
21#endif
22#ifdef HAVE_OPTRESET
23extern int optreset;		/* defined by BSD, but not others */
24#endif
25
26#include "debugfs.h"
27
28/*
29 * This function resets the libc getopt() function, which keeps
30 * internal state.  Bad design!  Stupid libc API designers!  No
31 * biscuit!
32 *
33 * BSD-derived getopt() functions require that optind be reset to 1 in
34 * order to reset getopt() state.  This used to be generally accepted
35 * way of resetting getopt().  However, glibc's getopt()
36 * has additional getopt() state beyond optind, and requires that
37 * optind be set zero to reset its state.  So the unfortunate state of
38 * affairs is that BSD-derived versions of getopt() misbehave if
39 * optind is set to 0 in order to reset getopt(), and glibc's getopt()
40 * will core ump if optind is set 1 in order to reset getopt().
41 *
42 * More modern versions of BSD require that optreset be set to 1 in
43 * order to reset getopt().   Sigh.  Standards, anyone?
44 *
45 * We hide the hair here.
46 */
47void reset_getopt(void)
48{
49#ifdef __GLIBC__
50	optind = 0;
51#else
52	optind = 1;
53#endif
54#ifdef HAVE_OPTRESET
55	optreset = 1;		/* Makes BSD getopt happy */
56#endif
57}
58
59static const char *pager_search_list[] = { "pager", "less", "more", 0 };
60static const char *pager_dir_list[] = { "/usr/bin", "/bin", 0 };
61
62static const char *find_pager(char *buf)
63{
64	const char **i, **j;
65
66	for (i = pager_search_list; *i; i++) {
67		for (j = pager_dir_list; *j; j++) {
68			sprintf(buf, "%s/%s", *j, *i);
69			if (access(buf, X_OK) == 0)
70				return(buf);
71		}
72	}
73	return 0;
74}
75
76FILE *open_pager(void)
77{
78	FILE *outfile = 0;
79	const char *pager = getenv("PAGER");
80	char buf[80];
81
82	signal(SIGPIPE, SIG_IGN);
83	if (pager) {
84		if (strcmp(pager, "__none__") == 0) {
85			return stdout;
86		}
87	} else
88		pager = find_pager(buf);
89
90	if (pager)
91		outfile = popen(pager, "w");
92
93	if (!outfile)
94		outfile = stdout;
95
96	return (outfile);
97}
98
99void close_pager(FILE *stream)
100{
101	if (stream && stream != stdout) pclose(stream);
102}
103
104/*
105 * This routine is used whenever a command needs to turn a string into
106 * an inode.
107 */
108ext2_ino_t string_to_inode(char *str)
109{
110	ext2_ino_t	ino;
111	int		len = strlen(str);
112	char		*end;
113	int		retval;
114
115	/*
116	 * If the string is of the form <ino>, then treat it as an
117	 * inode number.
118	 */
119	if ((len > 2) && (str[0] == '<') && (str[len-1] == '>')) {
120		ino = strtoul(str+1, &end, 0);
121		if (*end=='>')
122			return ino;
123	}
124
125	retval = ext2fs_namei(current_fs, root, cwd, str, &ino);
126	if (retval) {
127		com_err(str, retval, "");
128		return 0;
129	}
130	return ino;
131}
132
133/*
134 * This routine returns 1 if the filesystem is not open, and prints an
135 * error message to that effect.
136 */
137int check_fs_open(char *name)
138{
139	if (!current_fs) {
140		com_err(name, 0, "Filesystem not open");
141		return 1;
142	}
143	return 0;
144}
145
146/*
147 * This routine returns 1 if a filesystem is open, and prints an
148 * error message to that effect.
149 */
150int check_fs_not_open(char *name)
151{
152	if (current_fs) {
153		com_err(name, 0,
154			"Filesystem %s is still open.  Close it first.\n",
155			current_fs->device_name);
156		return 1;
157	}
158	return 0;
159}
160
161/*
162 * This routine returns 1 if a filesystem is not opened read/write,
163 * and prints an error message to that effect.
164 */
165int check_fs_read_write(char *name)
166{
167	if (!(current_fs->flags & EXT2_FLAG_RW)) {
168		com_err(name, 0, "Filesystem opened read/only");
169		return 1;
170	}
171	return 0;
172}
173
174/*
175 * This routine returns 1 if a filesystem is doesn't have its inode
176 * and block bitmaps loaded, and prints an error message to that
177 * effect.
178 */
179int check_fs_bitmaps(char *name)
180{
181	if (!current_fs->block_map || !current_fs->inode_map) {
182		com_err(name, 0, "Filesystem bitmaps not loaded");
183		return 1;
184	}
185	return 0;
186}
187
188/*
189 * This function takes a __u32 time value and converts it to a string,
190 * using ctime
191 */
192char *time_to_string(__u32 cl)
193{
194	time_t	t = (time_t) cl;
195
196	return ctime(&t);
197}
198
199/*
200 * This function will convert a string to an unsigned long, printing
201 * an error message if it fails, and returning success or failure in err.
202 */
203unsigned long parse_ulong(const char *str, const char *cmd,
204			  const char *descr, int *err)
205{
206	char		*tmp;
207	unsigned long	ret;
208
209	ret = strtoul(str, &tmp, 0);
210	if (*tmp == 0) {
211		if (err)
212			*err = 0;
213		return ret;
214	}
215	com_err(cmd, 0, "Bad %s - %s", descr, str);
216	if (err)
217		*err = 1;
218	else
219		exit(1);
220	return 0;
221}
222
223/*
224 * This function will convert a string to a block number.  It returns
225 * 0 on success, 1 on failure.
226 */
227int strtoblk(const char *cmd, const char *str, blk_t *ret)
228{
229	blk_t	blk;
230	int	err;
231
232	blk = parse_ulong(str, cmd, "block number", &err);
233	*ret = blk;
234	if (err == 0 && blk == 0) {
235		com_err(cmd, 0, "Invalid block number 0");
236		err = 1;
237	}
238	return err;
239}
240
241/*
242 * This is a common helper function used by the command processing
243 * routines
244 */
245int common_args_process(int argc, char *argv[], int min_argc, int max_argc,
246			const char *cmd, const char *usage, int flags)
247{
248	if (argc < min_argc || argc > max_argc) {
249		com_err(argv[0], 0, "Usage: %s %s", cmd, usage);
250		return 1;
251	}
252	if (flags & CHECK_FS_NOTOPEN) {
253		if (check_fs_not_open(argv[0]))
254			return 1;
255	} else {
256		if (check_fs_open(argv[0]))
257			return 1;
258	}
259	if ((flags & CHECK_FS_RW) && check_fs_read_write(argv[0]))
260		return 1;
261	if ((flags & CHECK_FS_BITMAPS) && check_fs_bitmaps(argv[0]))
262		return 1;
263	return 0;
264}
265
266/*
267 * This is a helper function used by do_stat, do_freei, do_seti, and
268 * do_testi, etc.  Basically, any command which takes a single
269 * argument which is a file/inode number specifier.
270 */
271int common_inode_args_process(int argc, char *argv[],
272			      ext2_ino_t *inode, int flags)
273{
274	if (common_args_process(argc, argv, 2, 2, argv[0], "<file>", flags))
275		return 1;
276
277	*inode = string_to_inode(argv[1]);
278	if (!*inode)
279		return 1;
280	return 0;
281}
282
283/*
284 * This is a helper function used by do_freeb, do_setb, and do_testb
285 */
286int common_block_args_process(int argc, char *argv[],
287			      blk_t *block, int *count)
288{
289	int	err;
290
291	if (common_args_process(argc, argv, 2, 3, argv[0],
292				"<block> [count]", CHECK_FS_BITMAPS))
293		return 1;
294
295	if (strtoblk(argv[0], argv[1], block))
296		return 1;
297	if (argc > 2) {
298		*count = parse_ulong(argv[2], argv[0], "count", &err);
299		if (err)
300			return 1;
301	}
302	return 0;
303}
304
305int debugfs_read_inode(ext2_ino_t ino, struct ext2_inode * inode,
306			const char *cmd)
307{
308	int retval;
309
310	retval = ext2fs_read_inode(current_fs, ino, inode);
311	if (retval) {
312		com_err(cmd, retval, "while reading inode %u", ino);
313		return 1;
314	}
315	return 0;
316}
317
318int debugfs_write_inode(ext2_ino_t ino, struct ext2_inode * inode,
319			const char *cmd)
320{
321	int retval;
322
323	retval = ext2fs_write_inode(current_fs, ino, inode);
324	if (retval) {
325		com_err(cmd, retval, "while writing inode %u", ino);
326		return 1;
327	}
328	return 0;
329}
330
331