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