Home | History | Annotate | Download | only in debugfs
      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 #define _XOPEN_SOURCE 600 /* needed for strptime */
     10 
     11 #include <stdio.h>
     12 #include <unistd.h>
     13 #include <stdlib.h>
     14 #include <ctype.h>
     15 #include <string.h>
     16 #include <time.h>
     17 #include <signal.h>
     18 #ifdef HAVE_GETOPT_H
     19 #include <getopt.h>
     20 #else
     21 extern int optind;
     22 extern char *optarg;
     23 #endif
     24 #ifdef HAVE_OPTRESET
     25 extern int optreset;		/* defined by BSD, but not others */
     26 #endif
     27 
     28 #include "ss/ss.h"
     29 #include "debugfs.h"
     30 
     31 /*
     32  * This function resets the libc getopt() function, which keeps
     33  * internal state.  Bad design!  Stupid libc API designers!  No
     34  * biscuit!
     35  *
     36  * BSD-derived getopt() functions require that optind be reset to 1 in
     37  * order to reset getopt() state.  This used to be generally accepted
     38  * way of resetting getopt().  However, glibc's getopt()
     39  * has additional getopt() state beyond optind, and requires that
     40  * optind be set zero to reset its state.  So the unfortunate state of
     41  * affairs is that BSD-derived versions of getopt() misbehave if
     42  * optind is set to 0 in order to reset getopt(), and glibc's getopt()
     43  * will core dump if optind is set 1 in order to reset getopt().
     44  *
     45  * More modern versions of BSD require that optreset be set to 1 in
     46  * order to reset getopt().   Sigh.  Standards, anyone?
     47  *
     48  * We hide the hair here.
     49  */
     50 void reset_getopt(void)
     51 {
     52 #if defined(__GLIBC__) || defined(__linux__)
     53 	optind = 0;
     54 #else
     55 	optind = 1;
     56 #endif
     57 #ifdef HAVE_OPTRESET
     58 	optreset = 1;		/* Makes BSD getopt happy */
     59 #endif
     60 }
     61 
     62 static const char *pager_search_list[] = { "pager", "more", "less", 0 };
     63 static const char *pager_dir_list[] = { "/usr/bin", "/bin", 0 };
     64 
     65 static const char *find_pager(char *buf)
     66 {
     67 	const char **i, **j;
     68 
     69 	for (i = pager_search_list; *i; i++) {
     70 		for (j = pager_dir_list; *j; j++) {
     71 			sprintf(buf, "%s/%s", *j, *i);
     72 			if (access(buf, X_OK) == 0)
     73 				return(buf);
     74 		}
     75 	}
     76 	return 0;
     77 }
     78 
     79 FILE *open_pager(void)
     80 {
     81 	FILE *outfile = 0;
     82 	const char *pager = ss_safe_getenv("DEBUGFS_PAGER");
     83 	char buf[80];
     84 
     85 	signal(SIGPIPE, SIG_IGN);
     86 	if (!isatty(1))
     87 		return stdout;
     88 	if (!pager)
     89 		pager = ss_safe_getenv("PAGER");
     90 	if (!pager)
     91 		pager = find_pager(buf);
     92 	if (!pager ||
     93 	    (strcmp(pager, "__none__") == 0) ||
     94 	    ((outfile = popen(pager, "w")) == 0))
     95 		return stdout;
     96 	return outfile;
     97 }
     98 
     99 void 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  */
    108 ext2_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, 0);
    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  */
    137 int 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  */
    150 int 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  */
    165 int 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  */
    179 int 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  */
    192 char *time_to_string(__u32 cl)
    193 {
    194 	static int	do_gmt = -1;
    195 	time_t		t = (time_t) cl;
    196 	const char	*tz;
    197 
    198 	if (do_gmt == -1) {
    199 		/* The diet libc doesn't respect the TZ environemnt variable */
    200 		tz = ss_safe_getenv("TZ");
    201 		if (!tz)
    202 			tz = "";
    203 		do_gmt = !strcmp(tz, "GMT");
    204 	}
    205 
    206 	return asctime((do_gmt) ? gmtime(&t) : localtime(&t));
    207 }
    208 
    209 /*
    210  * Parse a string as a time.  Return ((time_t)-1) if the string
    211  * doesn't appear to be a sane time.
    212  */
    213 time_t string_to_time(const char *arg)
    214 {
    215 	struct	tm	ts;
    216 	time_t		ret;
    217 	char *tmp;
    218 
    219 	if (strcmp(arg, "now") == 0) {
    220 		return time(0);
    221 	}
    222 	if (arg[0] == '@') {
    223 		/* interpret it as an integer */
    224 		ret = strtoul(arg+1, &tmp, 0);
    225 		if (*tmp)
    226 			return ((time_t) -1);
    227 		return ret;
    228 	}
    229 	memset(&ts, 0, sizeof(ts));
    230 #ifdef HAVE_STRPTIME
    231 	strptime(arg, "%Y%m%d%H%M%S", &ts);
    232 #else
    233 	sscanf(arg, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon,
    234 	       &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
    235 	ts.tm_year -= 1900;
    236 	ts.tm_mon -= 1;
    237 	if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 ||
    238 	    ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
    239 	    ts.tm_min > 59 || ts.tm_sec > 61)
    240 		ts.tm_mday = 0;
    241 #endif
    242 	ts.tm_isdst = -1;
    243 	ret = mktime(&ts);
    244 	if (ts.tm_mday == 0 || ret == ((time_t) -1)) {
    245 		/* Try it as an integer... */
    246 		ret = strtoul(arg, &tmp, 0);
    247 		if (*tmp)
    248 			return ((time_t) -1);
    249 	}
    250 	return ret;
    251 }
    252 
    253 /*
    254  * This function will convert a string to an unsigned long, printing
    255  * an error message if it fails, and returning success or failure in err.
    256  */
    257 unsigned long parse_ulong(const char *str, const char *cmd,
    258 			  const char *descr, int *err)
    259 {
    260 	char		*tmp;
    261 	unsigned long	ret;
    262 
    263 	ret = strtoul(str, &tmp, 0);
    264 	if (*tmp == 0) {
    265 		if (err)
    266 			*err = 0;
    267 		return ret;
    268 	}
    269 	com_err(cmd, 0, "Bad %s - %s", descr, str);
    270 	if (err)
    271 		*err = 1;
    272 	else
    273 		exit(1);
    274 	return 0;
    275 }
    276 
    277 /*
    278  * This function will convert a string to an unsigned long long, printing
    279  * an error message if it fails, and returning success or failure in err.
    280  */
    281 unsigned long long parse_ulonglong(const char *str, const char *cmd,
    282 				   const char *descr, int *err)
    283 {
    284 	char			*tmp;
    285 	unsigned long long	ret;
    286 
    287 	ret = strtoull(str, &tmp, 0);
    288 	if (*tmp == 0) {
    289 		if (err)
    290 			*err = 0;
    291 		return ret;
    292 	}
    293 	com_err(cmd, 0, "Bad %s - %s", descr, str);
    294 	if (err)
    295 		*err = 1;
    296 	else
    297 		exit(1);
    298 	return 0;
    299 }
    300 
    301 /*
    302  * This function will convert a string to a block number.  It returns
    303  * 0 on success, 1 on failure.
    304  */
    305 int strtoblk(const char *cmd, const char *str, blk64_t *ret)
    306 {
    307 	blk64_t	blk;
    308 	int	err;
    309 
    310 	blk = parse_ulonglong(str, cmd, "block number", &err);
    311 	*ret = blk;
    312 	if (err)
    313 		com_err(cmd, 0, "Invalid block number: %s", str);
    314 	return err;
    315 }
    316 
    317 /*
    318  * This is a common helper function used by the command processing
    319  * routines
    320  */
    321 int common_args_process(int argc, char *argv[], int min_argc, int max_argc,
    322 			const char *cmd, const char *usage, int flags)
    323 {
    324 	if (argc < min_argc || argc > max_argc) {
    325 		com_err(argv[0], 0, "Usage: %s %s", cmd, usage);
    326 		return 1;
    327 	}
    328 	if (flags & CHECK_FS_NOTOPEN) {
    329 		if (check_fs_not_open(argv[0]))
    330 			return 1;
    331 	} else {
    332 		if (check_fs_open(argv[0]))
    333 			return 1;
    334 	}
    335 	if ((flags & CHECK_FS_RW) && check_fs_read_write(argv[0]))
    336 		return 1;
    337 	if ((flags & CHECK_FS_BITMAPS) && check_fs_bitmaps(argv[0]))
    338 		return 1;
    339 	return 0;
    340 }
    341 
    342 /*
    343  * This is a helper function used by do_stat, do_freei, do_seti, and
    344  * do_testi, etc.  Basically, any command which takes a single
    345  * argument which is a file/inode number specifier.
    346  */
    347 int common_inode_args_process(int argc, char *argv[],
    348 			      ext2_ino_t *inode, int flags)
    349 {
    350 	if (common_args_process(argc, argv, 2, 2, argv[0], "<file>", flags))
    351 		return 1;
    352 
    353 	*inode = string_to_inode(argv[1]);
    354 	if (!*inode)
    355 		return 1;
    356 	return 0;
    357 }
    358 
    359 /*
    360  * This is a helper function used by do_freeb, do_setb, and do_testb
    361  */
    362 int common_block_args_process(int argc, char *argv[],
    363 			      blk64_t *block, blk64_t *count)
    364 {
    365 	int	err;
    366 
    367 	if (common_args_process(argc, argv, 2, 3, argv[0],
    368 				"<block> [count]", CHECK_FS_BITMAPS))
    369 		return 1;
    370 
    371 	if (strtoblk(argv[0], argv[1], block))
    372 		return 1;
    373 	if (*block == 0) {
    374 		com_err(argv[0], 0, "Invalid block number 0");
    375 		err = 1;
    376 	}
    377 
    378 	if (argc > 2) {
    379 		err = strtoblk(argv[0], argv[2], count);
    380 		if (err)
    381 			return 1;
    382 	}
    383 	return 0;
    384 }
    385 
    386 int debugfs_read_inode_full(ext2_ino_t ino, struct ext2_inode * inode,
    387 			const char *cmd, int bufsize)
    388 {
    389 	int retval;
    390 
    391 	retval = ext2fs_read_inode_full(current_fs, ino, inode, bufsize);
    392 	if (retval) {
    393 		com_err(cmd, retval, "while reading inode %u", ino);
    394 		return 1;
    395 	}
    396 	return 0;
    397 }
    398 
    399 int debugfs_read_inode(ext2_ino_t ino, struct ext2_inode * inode,
    400 			const char *cmd)
    401 {
    402 	int retval;
    403 
    404 	retval = ext2fs_read_inode(current_fs, ino, inode);
    405 	if (retval) {
    406 		com_err(cmd, retval, "while reading inode %u", ino);
    407 		return 1;
    408 	}
    409 	return 0;
    410 }
    411 
    412 int debugfs_write_inode_full(ext2_ino_t ino,
    413 			     struct ext2_inode *inode,
    414 			     const char *cmd,
    415 			     int bufsize)
    416 {
    417 	int retval;
    418 
    419 	retval = ext2fs_write_inode_full(current_fs, ino,
    420 					 inode, bufsize);
    421 	if (retval) {
    422 		com_err(cmd, retval, "while writing inode %u", ino);
    423 		return 1;
    424 	}
    425 	return 0;
    426 }
    427 
    428 int debugfs_write_inode(ext2_ino_t ino, struct ext2_inode * inode,
    429 			const char *cmd)
    430 {
    431 	int retval;
    432 
    433 	retval = ext2fs_write_inode(current_fs, ino, inode);
    434 	if (retval) {
    435 		com_err(cmd, retval, "while writing inode %u", ino);
    436 		return 1;
    437 	}
    438 	return 0;
    439 }
    440 
    441 int debugfs_write_new_inode(ext2_ino_t ino, struct ext2_inode * inode,
    442 			    const char *cmd)
    443 {
    444 	int retval;
    445 
    446 	retval = ext2fs_write_new_inode(current_fs, ino, inode);
    447 	if (retval) {
    448 		com_err(cmd, retval, "while creating inode %u", ino);
    449 		return 1;
    450 	}
    451 	return 0;
    452 }
    453 
    454 /*
    455  * Given a mode, return the ext2 file type
    456  */
    457 int ext2_file_type(unsigned int mode)
    458 {
    459 	if (LINUX_S_ISREG(mode))
    460 		return EXT2_FT_REG_FILE;
    461 
    462 	if (LINUX_S_ISDIR(mode))
    463 		return EXT2_FT_DIR;
    464 
    465 	if (LINUX_S_ISCHR(mode))
    466 		return EXT2_FT_CHRDEV;
    467 
    468 	if (LINUX_S_ISBLK(mode))
    469 		return EXT2_FT_BLKDEV;
    470 
    471 	if (LINUX_S_ISLNK(mode))
    472 		return EXT2_FT_SYMLINK;
    473 
    474 	if (LINUX_S_ISFIFO(mode))
    475 		return EXT2_FT_FIFO;
    476 
    477 	if (LINUX_S_ISSOCK(mode))
    478 		return EXT2_FT_SOCK;
    479 
    480 	return 0;
    481 }
    482