util.c revision 25f291c9b32d8017e6969c72a75e37d354c0570b
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") || !strcmp(tz, "GMT0"); 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 arg++; 226 fallback: 227 ret = strtoul(arg, &tmp, 0); 228 if (*tmp) 229 return ((time_t) -1); 230 return ret; 231 } 232 memset(&ts, 0, sizeof(ts)); 233#ifdef HAVE_STRPTIME 234 tmp = strptime(arg, "%Y%m%d%H%M%S", &ts); 235 if (tmp == NULL) 236 goto fallback; 237#else 238 sscanf(arg, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon, 239 &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec); 240 ts.tm_year -= 1900; 241 ts.tm_mon -= 1; 242 if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 || 243 ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 || 244 ts.tm_min > 59 || ts.tm_sec > 61) 245 ts.tm_mday = 0; 246#endif 247 ts.tm_isdst = -1; 248 /* strptime() may only update the specified fields, which does not 249 * necessarily include ts.tm_yday (%j). Calculate this if unset: 250 * 251 * Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec 252 * 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 253 * 254 * Start with 31 days per month. Even months have only 30 days, but 255 * reverse in August, subtract one day for those months. February has 256 * only 28 days, not 30, subtract two days. Add day of month, minus 257 * one, since day is not finished yet. Leap years handled afterward. */ 258 if (ts.tm_yday == 0) 259 ts.tm_yday = (ts.tm_mon * 31) - 260 ((ts.tm_mon - (ts.tm_mon > 7)) / 2) - 261 2 * (ts.tm_mon > 1) + ts.tm_mday - 1; 262 ret = ts.tm_sec + ts.tm_min*60 + ts.tm_hour*3600 + ts.tm_yday*86400 + 263 (ts.tm_year-70)*31536000 + ((ts.tm_year-69)/4)*86400 - 264 ((ts.tm_year-1)/100)*86400 + ((ts.tm_year+299)/400)*86400; 265 return ret; 266} 267 268/* 269 * This function will convert a string to an unsigned long, printing 270 * an error message if it fails, and returning success or failure in err. 271 */ 272unsigned long parse_ulong(const char *str, const char *cmd, 273 const char *descr, int *err) 274{ 275 char *tmp; 276 unsigned long ret; 277 278 ret = strtoul(str, &tmp, 0); 279 if (*tmp == 0) { 280 if (err) 281 *err = 0; 282 return ret; 283 } 284 com_err(cmd, 0, "Bad %s - %s", descr, str); 285 if (err) 286 *err = 1; 287 else 288 exit(1); 289 return 0; 290} 291 292/* 293 * This function will convert a string to an unsigned long long, printing 294 * an error message if it fails, and returning success or failure in err. 295 */ 296unsigned long long parse_ulonglong(const char *str, const char *cmd, 297 const char *descr, int *err) 298{ 299 char *tmp; 300 unsigned long long ret; 301 302 ret = strtoull(str, &tmp, 0); 303 if (*tmp == 0) { 304 if (err) 305 *err = 0; 306 return ret; 307 } 308 com_err(cmd, 0, "Bad %s - %s", descr, str); 309 if (err) 310 *err = 1; 311 else 312 exit(1); 313 return 0; 314} 315 316/* 317 * This function will convert a string to a block number. It returns 318 * 0 on success, 1 on failure. On failure, it outputs either an optionally 319 * specified error message or a default. 320 */ 321int strtoblk(const char *cmd, const char *str, const char *errmsg, 322 blk64_t *ret) 323{ 324 blk64_t blk; 325 int err; 326 327 if (errmsg == NULL) 328 blk = parse_ulonglong(str, cmd, "block number", &err); 329 else 330 blk = parse_ulonglong(str, cmd, errmsg, &err); 331 *ret = blk; 332 return err; 333} 334 335/* 336 * This is a common helper function used by the command processing 337 * routines 338 */ 339int common_args_process(int argc, char *argv[], int min_argc, int max_argc, 340 const char *cmd, const char *usage, int flags) 341{ 342 if (argc < min_argc || argc > max_argc) { 343 com_err(argv[0], 0, "Usage: %s %s", cmd, usage); 344 return 1; 345 } 346 if (flags & CHECK_FS_NOTOPEN) { 347 if (check_fs_not_open(argv[0])) 348 return 1; 349 } else { 350 if (check_fs_open(argv[0])) 351 return 1; 352 } 353 if ((flags & CHECK_FS_RW) && check_fs_read_write(argv[0])) 354 return 1; 355 if ((flags & CHECK_FS_BITMAPS) && check_fs_bitmaps(argv[0])) 356 return 1; 357 return 0; 358} 359 360/* 361 * This is a helper function used by do_stat, do_freei, do_seti, and 362 * do_testi, etc. Basically, any command which takes a single 363 * argument which is a file/inode number specifier. 364 */ 365int common_inode_args_process(int argc, char *argv[], 366 ext2_ino_t *inode, int flags) 367{ 368 if (common_args_process(argc, argv, 2, 2, argv[0], "<file>", flags)) 369 return 1; 370 371 *inode = string_to_inode(argv[1]); 372 if (!*inode) 373 return 1; 374 return 0; 375} 376 377/* 378 * This is a helper function used by do_freeb, do_setb, and do_testb 379 */ 380int common_block_args_process(int argc, char *argv[], 381 blk64_t *block, blk64_t *count) 382{ 383 int err; 384 385 if (common_args_process(argc, argv, 2, 3, argv[0], 386 "<block> [count]", CHECK_FS_BITMAPS)) 387 return 1; 388 389 if (strtoblk(argv[0], argv[1], NULL, block)) 390 return 1; 391 if (*block == 0) { 392 com_err(argv[0], 0, "Invalid block number 0"); 393 return 1; 394 } 395 396 if (argc > 2) { 397 err = strtoblk(argv[0], argv[2], "count", count); 398 if (err) 399 return 1; 400 } 401 return 0; 402} 403 404int debugfs_read_inode_full(ext2_ino_t ino, struct ext2_inode * inode, 405 const char *cmd, int bufsize) 406{ 407 int retval; 408 409 retval = ext2fs_read_inode_full(current_fs, ino, inode, bufsize); 410 if (retval) { 411 com_err(cmd, retval, "while reading inode %u", ino); 412 return 1; 413 } 414 return 0; 415} 416 417int debugfs_read_inode(ext2_ino_t ino, struct ext2_inode * inode, 418 const char *cmd) 419{ 420 int retval; 421 422 retval = ext2fs_read_inode(current_fs, ino, inode); 423 if (retval) { 424 com_err(cmd, retval, "while reading inode %u", ino); 425 return 1; 426 } 427 return 0; 428} 429 430int debugfs_write_inode_full(ext2_ino_t ino, 431 struct ext2_inode *inode, 432 const char *cmd, 433 int bufsize) 434{ 435 int retval; 436 437 retval = ext2fs_write_inode_full(current_fs, ino, 438 inode, bufsize); 439 if (retval) { 440 com_err(cmd, retval, "while writing inode %u", ino); 441 return 1; 442 } 443 return 0; 444} 445 446int debugfs_write_inode(ext2_ino_t ino, struct ext2_inode * inode, 447 const char *cmd) 448{ 449 int retval; 450 451 retval = ext2fs_write_inode(current_fs, ino, inode); 452 if (retval) { 453 com_err(cmd, retval, "while writing inode %u", ino); 454 return 1; 455 } 456 return 0; 457} 458 459int debugfs_write_new_inode(ext2_ino_t ino, struct ext2_inode * inode, 460 const char *cmd) 461{ 462 int retval; 463 464 retval = ext2fs_write_new_inode(current_fs, ino, inode); 465 if (retval) { 466 com_err(cmd, retval, "while creating inode %u", ino); 467 return 1; 468 } 469 return 0; 470} 471 472/* 473 * Given a mode, return the ext2 file type 474 */ 475int ext2_file_type(unsigned int mode) 476{ 477 if (LINUX_S_ISREG(mode)) 478 return EXT2_FT_REG_FILE; 479 480 if (LINUX_S_ISDIR(mode)) 481 return EXT2_FT_DIR; 482 483 if (LINUX_S_ISCHR(mode)) 484 return EXT2_FT_CHRDEV; 485 486 if (LINUX_S_ISBLK(mode)) 487 return EXT2_FT_BLKDEV; 488 489 if (LINUX_S_ISLNK(mode)) 490 return EXT2_FT_SYMLINK; 491 492 if (LINUX_S_ISFIFO(mode)) 493 return EXT2_FT_FIFO; 494 495 if (LINUX_S_ISSOCK(mode)) 496 return EXT2_FT_SOCK; 497 498 return 0; 499} 500 501errcode_t read_list(char *str, blk64_t **list, size_t *len) 502{ 503 blk64_t *lst = *list; 504 size_t ln = *len; 505 char *tok, *p = str; 506 errcode_t retval; 507 508 while ((tok = strtok(p, ","))) { 509 blk64_t *l; 510 blk64_t x, y; 511 char *e; 512 513 errno = 0; 514 y = x = strtoull(tok, &e, 0); 515 if (errno) 516 return errno; 517 if (*e == '-') { 518 y = strtoull(e + 1, NULL, 0); 519 if (errno) 520 return errno; 521 } else if (*e != 0) { 522 retval = EINVAL; 523 goto err; 524 } 525 if (y < x) { 526 retval = EINVAL; 527 goto err; 528 } 529 l = realloc(lst, sizeof(blk64_t) * (ln + y - x + 1)); 530 if (l == NULL) { 531 retval = ENOMEM; 532 goto err; 533 } 534 lst = l; 535 for (; x <= y; x++) 536 lst[ln++] = x; 537 p = NULL; 538 } 539 540 *list = lst; 541 *len = ln; 542 return 0; 543err: 544 free(lst); 545 return retval; 546} 547