util.c revision f404167dda29a59d2be2882328aeb074b9899669
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 */ 214time_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 if (arg[0] == '@') { 224 /* interpret it as an integer */ 225 ret = strtoul(arg+1, &tmp, 0); 226 if (*tmp) 227 return ((time_t) -1); 228 return ret; 229 } 230 memset(&ts, 0, sizeof(ts)); 231#ifdef HAVE_STRPTIME 232 strptime(arg, "%Y%m%d%H%M%S", &ts); 233#else 234 sscanf(arg, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon, 235 &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec); 236 ts.tm_year -= 1900; 237 ts.tm_mon -= 1; 238 if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 || 239 ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 || 240 ts.tm_min > 59 || ts.tm_sec > 61) 241 ts.tm_mday = 0; 242#endif 243 ts.tm_isdst = -1; 244 ret = mktime(&ts); 245 if (ts.tm_mday == 0 || ret == ((time_t) -1)) { 246 /* Try it as an integer... */ 247 ret = strtoul(arg, &tmp, 0); 248 if (*tmp) 249 return ((time_t) -1); 250 } 251 return ret; 252} 253 254/* 255 * This function will convert a string to an unsigned long, printing 256 * an error message if it fails, and returning success or failure in err. 257 */ 258unsigned long parse_ulong(const char *str, const char *cmd, 259 const char *descr, int *err) 260{ 261 char *tmp; 262 unsigned long ret; 263 264 ret = strtoul(str, &tmp, 0); 265 if (*tmp == 0) { 266 if (err) 267 *err = 0; 268 return ret; 269 } 270 com_err(cmd, 0, "Bad %s - %s", descr, str); 271 if (err) 272 *err = 1; 273 else 274 exit(1); 275 return 0; 276} 277 278/* 279 * This function will convert a string to an unsigned long long, printing 280 * an error message if it fails, and returning success or failure in err. 281 */ 282unsigned long long parse_ulonglong(const char *str, const char *cmd, 283 const char *descr, int *err) 284{ 285 char *tmp; 286 unsigned long long ret; 287 288 ret = strtoull(str, &tmp, 0); 289 if (*tmp == 0) { 290 if (err) 291 *err = 0; 292 return ret; 293 } 294 com_err(cmd, 0, "Bad %s - %s", descr, str); 295 if (err) 296 *err = 1; 297 else 298 exit(1); 299 return 0; 300} 301 302/* 303 * This function will convert a string to a block number. It returns 304 * 0 on success, 1 on failure. 305 */ 306int strtoblk(const char *cmd, const char *str, blk64_t *ret) 307{ 308 blk64_t blk; 309 int err; 310 311 blk = parse_ulonglong(str, cmd, "block number", &err); 312 *ret = blk; 313 if (err) 314 com_err(cmd, 0, "Invalid block number: %s", str); 315 return err; 316} 317 318/* 319 * This is a common helper function used by the command processing 320 * routines 321 */ 322int common_args_process(int argc, char *argv[], int min_argc, int max_argc, 323 const char *cmd, const char *usage, int flags) 324{ 325 if (argc < min_argc || argc > max_argc) { 326 com_err(argv[0], 0, "Usage: %s %s", cmd, usage); 327 return 1; 328 } 329 if (flags & CHECK_FS_NOTOPEN) { 330 if (check_fs_not_open(argv[0])) 331 return 1; 332 } else { 333 if (check_fs_open(argv[0])) 334 return 1; 335 } 336 if ((flags & CHECK_FS_RW) && check_fs_read_write(argv[0])) 337 return 1; 338 if ((flags & CHECK_FS_BITMAPS) && check_fs_bitmaps(argv[0])) 339 return 1; 340 return 0; 341} 342 343/* 344 * This is a helper function used by do_stat, do_freei, do_seti, and 345 * do_testi, etc. Basically, any command which takes a single 346 * argument which is a file/inode number specifier. 347 */ 348int common_inode_args_process(int argc, char *argv[], 349 ext2_ino_t *inode, int flags) 350{ 351 if (common_args_process(argc, argv, 2, 2, argv[0], "<file>", flags)) 352 return 1; 353 354 *inode = string_to_inode(argv[1]); 355 if (!*inode) 356 return 1; 357 return 0; 358} 359 360/* 361 * This is a helper function used by do_freeb, do_setb, and do_testb 362 */ 363int common_block_args_process(int argc, char *argv[], 364 blk64_t *block, blk64_t *count) 365{ 366 int err; 367 368 if (common_args_process(argc, argv, 2, 3, argv[0], 369 "<block> [count]", CHECK_FS_BITMAPS)) 370 return 1; 371 372 if (strtoblk(argv[0], argv[1], block)) 373 return 1; 374 if (*block == 0) { 375 com_err(argv[0], 0, "Invalid block number 0"); 376 err = 1; 377 } 378 379 if (argc > 2) { 380 err = strtoblk(argv[0], argv[2], count); 381 if (err) 382 return 1; 383 } 384 return 0; 385} 386 387int debugfs_read_inode_full(ext2_ino_t ino, struct ext2_inode * inode, 388 const char *cmd, int bufsize) 389{ 390 int retval; 391 392 retval = ext2fs_read_inode_full(current_fs, ino, inode, bufsize); 393 if (retval) { 394 com_err(cmd, retval, "while reading inode %u", ino); 395 return 1; 396 } 397 return 0; 398} 399 400int debugfs_read_inode(ext2_ino_t ino, struct ext2_inode * inode, 401 const char *cmd) 402{ 403 int retval; 404 405 retval = ext2fs_read_inode(current_fs, ino, inode); 406 if (retval) { 407 com_err(cmd, retval, "while reading inode %u", ino); 408 return 1; 409 } 410 return 0; 411} 412 413int debugfs_write_inode_full(ext2_ino_t ino, 414 struct ext2_inode *inode, 415 const char *cmd, 416 int bufsize) 417{ 418 int retval; 419 420 retval = ext2fs_write_inode_full(current_fs, ino, 421 inode, bufsize); 422 if (retval) { 423 com_err(cmd, retval, "while writing inode %u", ino); 424 return 1; 425 } 426 return 0; 427} 428 429int debugfs_write_inode(ext2_ino_t ino, struct ext2_inode * inode, 430 const char *cmd) 431{ 432 int retval; 433 434 retval = ext2fs_write_inode(current_fs, ino, inode); 435 if (retval) { 436 com_err(cmd, retval, "while writing inode %u", ino); 437 return 1; 438 } 439 return 0; 440} 441 442int debugfs_write_new_inode(ext2_ino_t ino, struct ext2_inode * inode, 443 const char *cmd) 444{ 445 int retval; 446 447 retval = ext2fs_write_new_inode(current_fs, ino, inode); 448 if (retval) { 449 com_err(cmd, retval, "while creating inode %u", ino); 450 return 1; 451 } 452 return 0; 453} 454 455/* 456 * Given a mode, return the ext2 file type 457 */ 458int ext2_file_type(unsigned int mode) 459{ 460 if (LINUX_S_ISREG(mode)) 461 return EXT2_FT_REG_FILE; 462 463 if (LINUX_S_ISDIR(mode)) 464 return EXT2_FT_DIR; 465 466 if (LINUX_S_ISCHR(mode)) 467 return EXT2_FT_CHRDEV; 468 469 if (LINUX_S_ISBLK(mode)) 470 return EXT2_FT_BLKDEV; 471 472 if (LINUX_S_ISLNK(mode)) 473 return EXT2_FT_SYMLINK; 474 475 if (LINUX_S_ISFIFO(mode)) 476 return EXT2_FT_FIFO; 477 478 if (LINUX_S_ISSOCK(mode)) 479 return EXT2_FT_SOCK; 480 481 return 0; 482} 483