/* $OpenBSD: devtostream.c,v 1.2 2001/07/04 08:44:58 niklas Exp $ */ /* -------------------------------------------------- | NAME | devtostream | PURPOSE | dump all data on a device to a stream. | | NOTES | only works for RDB partitions. | | COPYRIGHT | Copyright (C) 1993 Christian E. Hopps | | This program is free software; you can redistribute it and/or modify | it under the terms of the GNU General Public License as published by | the Free Software Foundation; either version 2 of the License, or | (at your option) any later version. | | This program is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General Public License for more details. | | You should have received a copy of the GNU General Public License | along with this program; if not, write to the Free Software | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | | HISTORY | chopps - Oct 9, 1993: Created. +--------------------------------------------------- */ #include #include #include #include #include #include #include #include #define __GNU_LIBRARY__ 1 #include #undef __GNU_LIBRARY__ #include "getdevices.h" #if defined (SASC) /* we will handle this ourselves. */ int __regargs chkabort (void) { return 0; } int __regargs Chk_Abort (void) { return 0; } #endif struct partition *find_partition (struct List *dl, char *dev_name, char *part_name, ulong unit, ulong start_block, ulong end_block); void dev_to_file (char *name, ulong unit, ulong bpb, FILE *file, ulong cb, ulong end); int check_values (struct partition *p, ulong st, ulong end, int exp); struct option long_options[] = { { "output", required_argument, NULL, 'o' }, { "rdb-name", required_argument, NULL, 'n'}, #if defined (EXPERT_VERSION) { "start-block", required_argument, NULL, 's'}, { "end-block", required_argument, NULL, 'e'}, { "expert-mode", no_argument, NULL, 'x'}, #endif { "device", required_argument, NULL, 'd'}, { "unit", required_argument, NULL, 'u'}, { "buffer-blocks", required_argument, NULL, 'b'}, { "verbose", no_argument, NULL, 'V'}, { "help", no_argument, NULL, 'h'}, { "version", no_argument, NULL, 'v' }, { "quiet", no_argument, NULL, 'q' }, { NULL, 0, NULL, 0 } }; char *short_options = "?qvVhn:d:u:b:o:" #if defined (EXPERT_VERSION) "xs:e:" #endif ; char *cmd_vers_string = "\0$VERS devtostream 1.0 (93.10.10)"; char *version_string = "devtostream V1.0 -- Copyright 1993 Christian E. Hopps\n"; char *help_string = "Usage: %s [options]\n" "Options:\n" " -[vVxghnsedubo] [--output=file] [--rdb-name=partition_name]\n" " [--device=device_name] [--unit=unit_num] [--version]\n" #if defined (EXPERT_VERSION) " [--start-block=block] [--end-block=block] [--expert-mode]\n" #endif " [--buffer-blocks=blocks] [--verbose] [--quiet] [--help]\n" "\n" "Number Formats: (where `n\' are alpha-num. digits)\n" " 0[xX]nnn | [xX]nnn | nnn[hH] | $nnn - for Hex\n" " nnn[oO] - for octal\n" " nnn[bB] - for binary\n" " nnn - for decimal (also default for non-recognized)\n" "\n" " given the above you can also postpend a [MKk] for Megabyte\n" " Kilobyte and kilobyte respectively. [range checking inuse]"; char *opt_outfile_name; char *opt_rdb_name; char *opt_device_name; ulong opt_unit = -1; /* -1 for invalid */ ulong opt_start_block = -1; /* -1 for invalid. */ ulong opt_end_block = -1; /* -1 for invalid */ ulong opt_verbose; ulong opt_expert; ulong opt_debug; ulong opt_quiet = 0; ulong number_of_buffer_blocks = 100; FILE *mout; FILE *min; int main (int argc, char **argv) { int ret = 0; int opt; int opt_quit = 0; int opt_version = 0; int opt_help = 0; int longind = 0; struct List *dl; FILE *file = NULL; signal (SIGINT, SIG_IGN); mout = stdout; min = stdin; if (argc) { while (EOF != (opt = getopt_long (argc, argv, short_options, long_options, &longind))) { switch (opt) { case 'q': opt_quiet = 1; break; case 'v': opt_version = 1; opt_quit = 1; break; case 'V': opt_verbose = 1; break; case '?': case 'h': opt_help = 1; opt_quit = 1; break; case 'n': opt_rdb_name = optarg; break; case 'd': opt_device_name = optarg; break; case 'o': opt_outfile_name = optarg; break; case 'b': if (!(string_to_number (optarg, &number_of_buffer_blocks))) { opt_quit = 1; opt_help = 1; ret = 20; } break; #if defined (EXPERT_VERSION) case 'x': opt_expert = 1; break; case 's': if (!(string_to_number (optarg, &opt_start_block))) { opt_quit = 1; opt_help = 1; ret = 20; } break; case 'e': if (!(string_to_number (optarg, &opt_end_block))) { opt_quit = 1; opt_help = 1; ret = 20; } break; #endif /* EXPERT_VERSION */ case 'u': if (!(string_to_number (optarg, &opt_unit))) { opt_quit = 1; opt_help = 1; ret = 20; } break; case 'g': opt_debug = 1; } } if (!opt_outfile_name) { mout = fopen ("*", "w+"); if (!mout) { return (20); } file = stdout; } if (opt_quiet && opt_expert) { message ("--quiet-mode (-q) and --expert-mode (-x) not allowed at same time.\n"); opt_quit = 1; ret = 20; } if (opt_version) { message (version_string, argv[0]); } if (opt_help) { message (help_string, argv[0]); } if (opt_quit) { return (ret); } /* there should be NO messages before this point!! */ dl = get_drive_list (); if (dl) { struct partition *p = find_partition (dl, opt_device_name, opt_rdb_name, opt_unit, opt_start_block, opt_end_block); if (p) { if (opt_outfile_name) { file = fopen (opt_outfile_name, "w"); } if (file) { if (!isatty (fileno(file))) { int def = 'N'; ulong st, end; if (!opt_quiet) { message ("found partition: \"%s\" capacity: %ld.%ld Megs", p->name, megs (p->total_blocks*p->block_size), tenths_of_a_meg (p->total_blocks*p->block_size)); message ("start block: %ld end block: %ld total blocks: %ld", p->start_block, p->end_block, p->total_blocks); message ("block Size: %ld", p->block_size); } st = opt_start_block; end = opt_end_block; if (st == (ulong)-1) { st = p->start_block; } if (end == (ulong)-1) { end = p->end_block; } if (check_values (p, st, end, opt_expert)) { int do_it = 0; /* default do not. */ if (!opt_quiet) { message ("dumping: start block: %ld to end block: %ld [size: %ldK]\n", st, end, ((end-st)*p->unit->bytes_per_block)/1024); def = ask_bool (def, 'y', "write from partition \"%s\" to file \"%s\"", p->name, opt_outfile_name ? opt_outfile_name : "stdout"); if (tolower (def) == 'y') { do_it = 1; } } else { do_it = 1; } if (do_it) { dev_to_file (p->unit->name, p->unit->unit, p->unit->bytes_per_block, file, st, end); } else { message ("ok, quiting..."); } } } else { warn_message ("Pipes and re-direction will work but interactive\n" "input/output is prohibited."); } if (opt_outfile_name) { fclose (file); } } } else { warn_message ("could not locate a partition with your specs."); } free_drive_list (dl); } if (!opt_outfile_name) { fclose (mout); } } return (0); } int check_values (struct partition *p, ulong st, ulong end, int exp) { if (st > end) { message ("error: Your end block [%ld] is less than your start block [%ld]!\n", st,end); return (0); } if (st < p->start_block || st > p->end_block || end > p->end_block || end < p->start_block) { warn_message ("ERROR: start and end blocks cannot cross partition boundries."); return (0); } if (st != p->start_block || end != p->end_block) { if (exp) { message ("Please note the values you gave for start and end\n" "do NOT match the partition in question."); } else { message ("error: you must set specify the `expert-mode\' argument to gain\n" " access inside the partition (ie. not the exact\n" " same block numbers as the partition's)."); return (0); } } return (1); } int check_break (void) { if (SIGBREAKF_CTRL_C & SetSignal (0, SIGBREAKF_CTRL_C)) { return (1); } return (0); } void dev_to_file (char *name, ulong unit, ulong bpb, FILE *file, ulong cb, ulong end) { struct device_data *dd = alloc_device (name, unit, 0, sizeof (struct IOStdReq)); if (dd) { ulong num_buffers = number_of_buffer_blocks; ulong total_blocks = end - cb + 1; ulong bw = 0; void *buffer = zmalloc (num_buffers*bpb); if (buffer) { while (cb <= end) { if (num_buffers > (end - cb + 1)) { num_buffers = (end - cb + 1); } if (check_break ()) { break; } /* Read from device. */ if (!opt_quiet) { fprintf (mout, "reading: %08ld -> %08ld [%3ld%%] \r", cb, cb + num_buffers - 1, ((bw+(num_buffers/2))*100/total_blocks)); fflush (mout); } if (bpb*num_buffers != device_read (dd, cb*bpb, num_buffers*bpb, buffer)) { fprintf (mout, "\n"); warn_message ("couldn't complete operation, read failed."); break; } /* Write to file. */ bw += num_buffers; if (!opt_quiet) { fprintf (mout, "writing: %08ld -> %08ld [%3ld%%] \r", cb, cb + num_buffers - 1, (bw*100/total_blocks)); fflush (mout); } if (num_buffers != fwrite (buffer, bpb, num_buffers, file)) { fprintf (mout, "\n"); warn_message ("couldn't complete operation, write failed."); break; } cb += num_buffers; } zfree (buffer); fprintf (mout, "\n"); } else { warn_message ("couldn't allocate io for operation."); } free_device (dd); } else { warn_message ("couldn't open device \"%s\" unit: %ld for operation.", name, unit); } } /* all the arguments, except the drive list itself, are search limiters. */ /* they are generalized with: NULL for strings and (ulong)-1 for ulongs. */ /* also the function returns as soon as all non-generalized criterion are met.*/ struct partition * find_partition (struct List *dl, char *dev_name, char *part_name, ulong unit, ulong start_block, ulong end_block) { struct Node *dn, *un, *pn; /* walk list of devices. */ for (dn = dl->lh_Head; dn->ln_Succ; dn = dn->ln_Succ) { struct device *d = ptrfrom (struct device, node, dn); if (dev_name == NULL || (!stricmp (dev_name, d->name))) { /* walk list of units. */ for (un = d->units.lh_Head; un->ln_Succ; un = un->ln_Succ) { struct unit *u = ptrfrom (struct unit, node, un); if (unit == (ulong)-1 || (u->unit == unit)) { /* walk list of partitions. */ for (pn = u->parts.lh_Head; pn->ln_Succ; pn = pn->ln_Succ) { struct partition *p = ptrfrom (struct partition, node, pn); int do_it = 1; if (part_name && stricmp (p->name, part_name)) { do_it = 0; } if (start_block != (ulong)-1 && (start_block < p->start_block || start_block > p->end_block)) { do_it = 0; } if (end_block != (ulong)-1 && (end_block > p->end_block || end_block < p->start_block)) { do_it = 0; } if (do_it) { return (p); } } } } } } return (NULL); }