util.c revision a6ce1349539f866334ef3d5758bc2ee44a454acd
15c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/*
25c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * util.c --- utilities for the debugfs program
35c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *
45c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Copyright (C) 1993, 1994 Theodore Ts'o.  This file may be
55c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * redistributed under the terms of the GNU Public License.
65c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *
75c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#define _XOPEN_SOURCE 600 /* needed for strptime */
10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include <stdio.h>
12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <unistd.h>
13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <stdlib.h>
14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <ctype.h>
15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <string.h>
16116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include <time.h>
17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <signal.h>
185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#ifdef HAVE_GETOPT_H
195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include <getopt.h>
205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#else
215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuextern int optind;
225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuextern char *optarg;
23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#endif
24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#ifdef HAVE_OPTRESET
25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)extern int optreset;		/* defined by BSD, but not others */
26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#endif
27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "debugfs.h"
29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/*
31116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * This function resets the libc getopt() function, which keeps
32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * internal state.  Bad design!  Stupid libc API designers!  No
33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * biscuit!
34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *
35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * BSD-derived getopt() functions require that optind be reset to 1 in
36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * order to reset getopt() state.  This used to be generally accepted
37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * way of resetting getopt().  However, glibc's getopt()
38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * has additional getopt() state beyond optind, and requires that
39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * optind be set zero to reset its state.  So the unfortunate state of
40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * affairs is that BSD-derived versions of getopt() misbehave if
41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * optind is set to 0 in order to reset getopt(), and glibc's getopt()
42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * will core dump if optind is set 1 in order to reset getopt().
43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *
44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * More modern versions of BSD require that optreset be set to 1 in
45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * order to reset getopt().   Sigh.  Standards, anyone?
46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *
47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * We hide the hair here.
48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */
49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void reset_getopt(void)
50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles){
51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#if defined(__GLIBC__) || defined(__linux__)
52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	optind = 0;
53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#else
54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	optind = 1;
55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#endif
56116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#ifdef HAVE_OPTRESET
57116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch	optreset = 1;		/* Makes BSD getopt happy */
58116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#endif
59116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
60116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)static const char *pager_search_list[] = { "pager", "more", "less", 0 };
62116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstatic const char *pager_dir_list[] = { "/usr/bin", "/bin", 0 };
635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)static const char *find_pager(char *buf)
655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles){
66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	const char **i, **j;
67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	for (i = pager_search_list; *i; i++) {
69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		for (j = pager_dir_list; *j; j++) {
70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			sprintf(buf, "%s/%s", *j, *i);
715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu			if (access(buf, X_OK) == 0)
725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu				return(buf);
735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu		}
745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu	}
755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu	return 0;
76}
77
78FILE *open_pager(void)
79{
80	FILE *outfile = 0;
81	const char *pager = getenv("DEBUGFS_PAGER");
82	char buf[80];
83
84	signal(SIGPIPE, SIG_IGN);
85	if (!isatty(1))
86		return stdout;
87	if (!pager)
88		pager = getenv("PAGER");
89	if (!pager)
90		pager = find_pager(buf);
91	if (!pager ||
92	    (strcmp(pager, "__none__") == 0) ||
93	    ((outfile = popen(pager, "w")) == 0))
94		return stdout;
95	return outfile;
96}
97
98void close_pager(FILE *stream)
99{
100	if (stream && stream != stdout) pclose(stream);
101}
102
103/*
104 * This routine is used whenever a command needs to turn a string into
105 * an inode.
106 */
107ext2_ino_t string_to_inode(char *str)
108{
109	ext2_ino_t	ino;
110	int		len = strlen(str);
111	char		*end;
112	int		retval;
113
114	/*
115	 * If the string is of the form <ino>, then treat it as an
116	 * inode number.
117	 */
118	if ((len > 2) && (str[0] == '<') && (str[len-1] == '>')) {
119		ino = strtoul(str+1, &end, 0);
120		if (*end=='>')
121			return ino;
122	}
123
124	retval = ext2fs_namei(current_fs, root, cwd, str, &ino);
125	if (retval) {
126		com_err(str, retval, 0);
127		return 0;
128	}
129	return ino;
130}
131
132/*
133 * This routine returns 1 if the filesystem is not open, and prints an
134 * error message to that effect.
135 */
136int check_fs_open(char *name)
137{
138	if (!current_fs) {
139		com_err(name, 0, "Filesystem not open");
140		return 1;
141	}
142	return 0;
143}
144
145/*
146 * This routine returns 1 if a filesystem is open, and prints an
147 * error message to that effect.
148 */
149int check_fs_not_open(char *name)
150{
151	if (current_fs) {
152		com_err(name, 0,
153			"Filesystem %s is still open.  Close it first.\n",
154			current_fs->device_name);
155		return 1;
156	}
157	return 0;
158}
159
160/*
161 * This routine returns 1 if a filesystem is not opened read/write,
162 * and prints an error message to that effect.
163 */
164int check_fs_read_write(char *name)
165{
166	if (!(current_fs->flags & EXT2_FLAG_RW)) {
167		com_err(name, 0, "Filesystem opened read/only");
168		return 1;
169	}
170	return 0;
171}
172
173/*
174 * This routine returns 1 if a filesystem is doesn't have its inode
175 * and block bitmaps loaded, and prints an error message to that
176 * effect.
177 */
178int check_fs_bitmaps(char *name)
179{
180	if (!current_fs->block_map || !current_fs->inode_map) {
181		com_err(name, 0, "Filesystem bitmaps not loaded");
182		return 1;
183	}
184	return 0;
185}
186
187/*
188 * This function takes a __u32 time value and converts it to a string,
189 * using ctime
190 */
191char *time_to_string(__u32 cl)
192{
193	static int	do_gmt = -1;
194	time_t		t = (time_t) cl;
195	const char	*tz;
196
197	if (do_gmt == -1) {
198		/* The diet libc doesn't respect the TZ environemnt variable */
199		tz = getenv("TZ");
200		if (!tz)
201			tz = "";
202		do_gmt = !strcmp(tz, "GMT");
203	}
204
205	return asctime((do_gmt) ? gmtime(&t) : localtime(&t));
206}
207
208/*
209 * Parse a string as a time.  Return ((time_t)-1) if the string
210 * doesn't appear to be a sane time.
211 */
212extern time_t string_to_time(const char *arg)
213{
214	struct	tm	ts;
215	time_t		ret;
216	char *tmp;
217
218	if (strcmp(arg, "now") == 0) {
219		return time(0);
220	}
221	memset(&ts, 0, sizeof(ts));
222#ifdef HAVE_STRPTIME
223	strptime(arg, "%Y%m%d%H%M%S", &ts);
224#else
225	sscanf(arg, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon,
226	       &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
227	ts.tm_year -= 1900;
228	ts.tm_mon -= 1;
229	if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 ||
230	    ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
231	    ts.tm_min > 59 || ts.tm_sec > 61)
232		ts.tm_mday = 0;
233#endif
234	ts.tm_isdst = -1;
235	ret = mktime(&ts);
236	if (ts.tm_mday == 0 || ret == ((time_t) -1)) {
237		/* Try it as an integer... */
238		ret = strtoul(arg, &tmp, 0);
239		if (*tmp)
240			return ((time_t) -1);
241	}
242	return ret;
243}
244
245/*
246 * This function will convert a string to an unsigned long, printing
247 * an error message if it fails, and returning success or failure in err.
248 */
249unsigned long parse_ulong(const char *str, const char *cmd,
250			  const char *descr, int *err)
251{
252	char		*tmp;
253	unsigned long	ret;
254
255	ret = strtoul(str, &tmp, 0);
256	if (*tmp == 0) {
257		if (err)
258			*err = 0;
259		return ret;
260	}
261	com_err(cmd, 0, "Bad %s - %s", descr, str);
262	if (err)
263		*err = 1;
264	else
265		exit(1);
266	return 0;
267}
268
269/*
270 * This function will convert a string to a block number.  It returns
271 * 0 on success, 1 on failure.
272 */
273int strtoblk(const char *cmd, const char *str, blk_t *ret)
274{
275	blk_t	blk;
276	int	err;
277
278	blk = parse_ulong(str, cmd, "block number", &err);
279	*ret = blk;
280	if (err)
281		com_err(cmd, 0, "Invalid block number: %s", str);
282	return err;
283}
284
285/*
286 * This is a common helper function used by the command processing
287 * routines
288 */
289int common_args_process(int argc, char *argv[], int min_argc, int max_argc,
290			const char *cmd, const char *usage, int flags)
291{
292	if (argc < min_argc || argc > max_argc) {
293		com_err(argv[0], 0, "Usage: %s %s", cmd, usage);
294		return 1;
295	}
296	if (flags & CHECK_FS_NOTOPEN) {
297		if (check_fs_not_open(argv[0]))
298			return 1;
299	} else {
300		if (check_fs_open(argv[0]))
301			return 1;
302	}
303	if ((flags & CHECK_FS_RW) && check_fs_read_write(argv[0]))
304		return 1;
305	if ((flags & CHECK_FS_BITMAPS) && check_fs_bitmaps(argv[0]))
306		return 1;
307	return 0;
308}
309
310/*
311 * This is a helper function used by do_stat, do_freei, do_seti, and
312 * do_testi, etc.  Basically, any command which takes a single
313 * argument which is a file/inode number specifier.
314 */
315int common_inode_args_process(int argc, char *argv[],
316			      ext2_ino_t *inode, int flags)
317{
318	if (common_args_process(argc, argv, 2, 2, argv[0], "<file>", flags))
319		return 1;
320
321	*inode = string_to_inode(argv[1]);
322	if (!*inode)
323		return 1;
324	return 0;
325}
326
327/*
328 * This is a helper function used by do_freeb, do_setb, and do_testb
329 */
330int common_block_args_process(int argc, char *argv[],
331			      blk_t *block, blk_t *count)
332{
333	int	err;
334
335	if (common_args_process(argc, argv, 2, 3, argv[0],
336				"<block> [count]", CHECK_FS_BITMAPS))
337		return 1;
338
339	if (strtoblk(argv[0], argv[1], block))
340		return 1;
341	if (*block == 0) {
342		com_err(argv[0], 0, "Invalid block number 0");
343		err = 1;
344	}
345
346	if (argc > 2) {
347		*count = parse_ulong(argv[2], argv[0], "count", &err);
348		if (err)
349			return 1;
350	}
351	return 0;
352}
353
354int debugfs_read_inode_full(ext2_ino_t ino, struct ext2_inode * inode,
355			const char *cmd, int bufsize)
356{
357	int retval;
358
359	retval = ext2fs_read_inode_full(current_fs, ino, inode, bufsize);
360	if (retval) {
361		com_err(cmd, retval, "while reading inode %u", ino);
362		return 1;
363	}
364	return 0;
365}
366
367int debugfs_read_inode(ext2_ino_t ino, struct ext2_inode * inode,
368			const char *cmd)
369{
370	int retval;
371
372	retval = ext2fs_read_inode(current_fs, ino, inode);
373	if (retval) {
374		com_err(cmd, retval, "while reading inode %u", ino);
375		return 1;
376	}
377	return 0;
378}
379
380int debugfs_write_inode(ext2_ino_t ino, struct ext2_inode * inode,
381			const char *cmd)
382{
383	int retval;
384
385	retval = ext2fs_write_inode(current_fs, ino, inode);
386	if (retval) {
387		com_err(cmd, retval, "while writing inode %u", ino);
388		return 1;
389	}
390	return 0;
391}
392
393int debugfs_write_new_inode(ext2_ino_t ino, struct ext2_inode * inode,
394			    const char *cmd)
395{
396	int retval;
397
398	retval = ext2fs_write_new_inode(current_fs, ino, inode);
399	if (retval) {
400		com_err(cmd, retval, "while creating inode %u", ino);
401		return 1;
402	}
403	return 0;
404}
405