| 1 | /******************************************************************************
 | 
|---|
| 2 | 
 | 
|---|
| 3 |   readsect.c - simple (dumb) HD sector read and test program
 | 
|---|
| 4 | 
 | 
|---|
| 5 |   Author: Markus Thielen
 | 
|---|
| 6 | 
 | 
|---|
| 7 |   Compilation (Watcom): wcl386 -bt=os2 readsect.c
 | 
|---|
| 8 |                         wcl386 -bt=os2 -d3 readsect.c (for debug)
 | 
|---|
| 9 | 
 | 
|---|
| 10 |   Copyright (c) 2010 by thi.guten Software development, www.thiguten.de
 | 
|---|
| 11 | 
 | 
|---|
| 12 |    This program is free software; you can redistribute it and/or modify
 | 
|---|
| 13 |    it under the terms of the GNU General Public License as published by
 | 
|---|
| 14 |    the Free Software Foundation; either version 2 of the License, or
 | 
|---|
| 15 |    (at your option) any later version.
 | 
|---|
| 16 | 
 | 
|---|
| 17 |    This program is distributed in the hope that it will be useful,
 | 
|---|
| 18 |    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
|---|
| 19 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
|---|
| 20 |    GNU General Public License for more details.
 | 
|---|
| 21 | 
 | 
|---|
| 22 |    You should have received a copy of the GNU General Public License
 | 
|---|
| 23 |    along with this program; if not, write to the Free Software
 | 
|---|
| 24 |    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
|---|
| 25 | 
 | 
|---|
| 26 | ******************************************************************************/
 | 
|---|
| 27 | 
 | 
|---|
| 28 | /*---- includes -------------------------------------------------------------*/
 | 
|---|
| 29 | 
 | 
|---|
| 30 | #define INCL_DOS
 | 
|---|
| 31 | #define INCL_ERRORS
 | 
|---|
| 32 | #define INCL_DOSDEVICES
 | 
|---|
| 33 | #define INCL_DOSDEVIOCTL
 | 
|---|
| 34 | #include <os2.h>
 | 
|---|
| 35 | #include <stdio.h>
 | 
|---|
| 36 | #include <io.h>
 | 
|---|
| 37 | #include <stddef.h>
 | 
|---|
| 38 | #include <stdlib.h>
 | 
|---|
| 39 | #include <stdarg.h>
 | 
|---|
| 40 | #include <ctype.h>
 | 
|---|
| 41 | #include <string.h>
 | 
|---|
| 42 | #include <errno.h>
 | 
|---|
| 43 | 
 | 
|---|
| 44 | #define SECTOR_SIZE        512
 | 
|---|
| 45 | #define SECTORS_PER_READ   4
 | 
|---|
| 46 | 
 | 
|---|
| 47 | 
 | 
|---|
| 48 | /*--- function prototypes ---------------------------------------------------*/
 | 
|---|
| 49 | 
 | 
|---|
| 50 | void          usage            (void);
 | 
|---|
| 51 | 
 | 
|---|
| 52 | int           read_sectors     (char *drv, unsigned long sectors_to_read, 
 | 
|---|
| 53 |                                 unsigned long sectors_per_read, FILE *fpo);
 | 
|---|
| 54 | 
 | 
|---|
| 55 | void          log              (char *fmt, ...);
 | 
|---|
| 56 | 
 | 
|---|
| 57 | 
 | 
|---|
| 58 | /*--- global data -----------------------------------------------------------*/
 | 
|---|
| 59 | 
 | 
|---|
| 60 | char *log_fn = NULL;
 | 
|---|
| 61 | unsigned long buffer_offset = 0;
 | 
|---|
| 62 | 
 | 
|---|
| 63 | 
 | 
|---|
| 64 | /*--- start of code ---------------------------------------------------------*/
 | 
|---|
| 65 | 
 | 
|---|
| 66 | /******************************************************************************
 | 
|---|
| 67 |  * main()
 | 
|---|
| 68 |  */
 | 
|---|
| 69 | int main(int argc, char **argv)
 | 
|---|
| 70 | {
 | 
|---|
| 71 |   char drv[50];
 | 
|---|
| 72 |   char *output;
 | 
|---|
| 73 |   FILE *fpo = NULL; /* output file */
 | 
|---|
| 74 |   int ret;
 | 
|---|
| 75 |   unsigned long sectors_to_read = 1024;
 | 
|---|
| 76 |   unsigned long i;
 | 
|---|
| 77 |   unsigned long sectors_per_read = SECTORS_PER_READ;
 | 
|---|
| 78 |   unsigned long loop_sectors_start = sectors_per_read;
 | 
|---|
| 79 |   int a;
 | 
|---|
| 80 | 
 | 
|---|
| 81 |   if (argc < 4) {
 | 
|---|
| 82 |     usage();
 | 
|---|
| 83 |     return -1;
 | 
|---|
| 84 |   }
 | 
|---|
| 85 | 
 | 
|---|
| 86 |   /* parse args */
 | 
|---|
| 87 |   for (a = 1; a < argc; a++) {
 | 
|---|
| 88 | 
 | 
|---|
| 89 |     if (argv[a][0] != '-' && argv[a][0] != '/') {
 | 
|---|
| 90 |       fprintf(stderr, "unknown argument %s - aborting\n", argv[a]);
 | 
|---|
| 91 |       return (-1);
 | 
|---|
| 92 |     }
 | 
|---|
| 93 | 
 | 
|---|
| 94 |     switch (tolower(argv[a][1])) {
 | 
|---|
| 95 | 
 | 
|---|
| 96 |     case 'd':
 | 
|---|
| 97 |       /* drive spec */
 | 
|---|
| 98 |       if (isalpha(argv[++a][0])) {
 | 
|---|
| 99 |         sprintf(drv, "\\\\.\\%c", toupper(argv[a][0]));
 | 
|---|
| 100 |       } else if (isdigit(argv[a][0])) {
 | 
|---|
| 101 |         sprintf(drv, "\\\\.\\Physical_Disk%c", argv[a][0]);
 | 
|---|
| 102 |       } else {
 | 
|---|
| 103 |         usage();
 | 
|---|
| 104 |         return -1;
 | 
|---|
| 105 |       }
 | 
|---|
| 106 |       break;
 | 
|---|
| 107 | 
 | 
|---|
| 108 |     case 's':
 | 
|---|
| 109 |       /* get sectors to read */
 | 
|---|
| 110 |       sectors_to_read = strtoul(argv[++a], NULL, 10);
 | 
|---|
| 111 |       break;
 | 
|---|
| 112 | 
 | 
|---|
| 113 |     case 'o':
 | 
|---|
| 114 |       /* get output file */
 | 
|---|
| 115 |       output = argv[++a];
 | 
|---|
| 116 |       if (*output == '-' && output[1] == 0) {
 | 
|---|
| 117 |         /* use stdout */
 | 
|---|
| 118 |         fpo = stdout;
 | 
|---|
| 119 |       } else {
 | 
|---|
| 120 |         fpo = fopen(output, "wb");
 | 
|---|
| 121 |         if (!fpo) {
 | 
|---|
| 122 |           perror("Failed to open output file");
 | 
|---|
| 123 |           return(-1);
 | 
|---|
| 124 |         }
 | 
|---|
| 125 |       }
 | 
|---|
| 126 |       break;
 | 
|---|
| 127 | 
 | 
|---|
| 128 |     case 'b':
 | 
|---|
| 129 |       /* optional buffer size */
 | 
|---|
| 130 |       if (*argv[++a] == '-') {
 | 
|---|
| 131 |         /* use a loop from 1 sectors to number specfied */
 | 
|---|
| 132 |         loop_sectors_start = 1;
 | 
|---|
| 133 |         sectors_per_read = (unsigned long) atoi(argv[a] + 1);
 | 
|---|
| 134 |         log("Looping sector count from 1 to %u; not using output file\n",
 | 
|---|
| 135 |             sectors_per_read);
 | 
|---|
| 136 |         if (fpo && fpo != stdin) {
 | 
|---|
| 137 |           fclose(fpo);
 | 
|---|
| 138 |           fpo = NULL;
 | 
|---|
| 139 |         }
 | 
|---|
| 140 |         
 | 
|---|
| 141 |       } else {
 | 
|---|
| 142 |         /* just one run */
 | 
|---|
| 143 |         sectors_per_read = (unsigned long) atoi(argv[a]);
 | 
|---|
| 144 |         loop_sectors_start = sectors_per_read;
 | 
|---|
| 145 |       }
 | 
|---|
| 146 |       if (sectors_per_read == 0) {
 | 
|---|
| 147 |         sectors_per_read = SECTORS_PER_READ;
 | 
|---|
| 148 |       }
 | 
|---|
| 149 |       break;
 | 
|---|
| 150 | 
 | 
|---|
| 151 |     case 'l':
 | 
|---|
| 152 |       /* optional log file name */
 | 
|---|
| 153 |       log_fn = argv[++a];
 | 
|---|
| 154 |       unlink(log_fn);
 | 
|---|
| 155 |       break;
 | 
|---|
| 156 | 
 | 
|---|
| 157 |     case 'u':
 | 
|---|
| 158 |       /* use unaligned read buffer */
 | 
|---|
| 159 |       buffer_offset = 1;
 | 
|---|
| 160 |       break;
 | 
|---|
| 161 |     }
 | 
|---|
| 162 |   }
 | 
|---|
| 163 | 
 | 
|---|
| 164 |   /* go */
 | 
|---|
| 165 |   for (i = loop_sectors_start; i <= sectors_per_read; i++) {
 | 
|---|
| 166 |     ret = read_sectors(drv, sectors_to_read, i, fpo);
 | 
|---|
| 167 |   }
 | 
|---|
| 168 | 
 | 
|---|
| 169 |   if (fpo && fpo != stdout) {
 | 
|---|
| 170 |     fclose(fpo);
 | 
|---|
| 171 |   }
 | 
|---|
| 172 | 
 | 
|---|
| 173 |   log("\nDone\n");
 | 
|---|
| 174 |   return ret;
 | 
|---|
| 175 | 
 | 
|---|
| 176 |   }
 | 
|---|
| 177 | 
 | 
|---|
| 178 | /******************************************************************************
 | 
|---|
| 179 |  * read_sectors() - read sectors and dump to output file
 | 
|---|
| 180 |  */
 | 
|---|
| 181 | int read_sectors(char *drv, unsigned long sectors_to_read, 
 | 
|---|
| 182 |                  unsigned long sectors_per_read, FILE *fpo)
 | 
|---|
| 183 | {
 | 
|---|
| 184 |   unsigned long ret;
 | 
|---|
| 185 |   HFILE hf_disk;
 | 
|---|
| 186 |   unsigned long action;
 | 
|---|
| 187 |   unsigned long cb_read;
 | 
|---|
| 188 |   char *buf;
 | 
|---|
| 189 |   int rc = 0;
 | 
|---|
| 190 |   unsigned long cb_take;
 | 
|---|
| 191 |   unsigned long sectors_take;
 | 
|---|
| 192 |   unsigned long sectors_read = 0;
 | 
|---|
| 193 | 
 | 
|---|
| 194 |   buf = malloc(SECTOR_SIZE * sectors_per_read + buffer_offset);
 | 
|---|
| 195 |   log("\nusing %u sectors per read\n", sectors_per_read);
 | 
|---|
| 196 |   log("read buffer address: 0x%u %s\n", buf + buffer_offset, 
 | 
|---|
| 197 |       buffer_offset ? "(unaligned)" : "");
 | 
|---|
| 198 | 
 | 
|---|
| 199 |   /* open drive */
 | 
|---|
| 200 |   log("Getting handle for drive %s\n", drv);
 | 
|---|
| 201 |   ret = DosOpen(drv, &hf_disk, &action, 0, 0, 
 | 
|---|
| 202 |                 OPEN_ACTION_OPEN_IF_EXISTS,
 | 
|---|
| 203 |                 OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY,
 | 
|---|
| 204 |                 NULL);
 | 
|---|
| 205 |   if (ret != NO_ERROR) {
 | 
|---|
| 206 |     fprintf(stderr, "Failed to open disk %s for reading: %d\n", drv, ret);
 | 
|---|
| 207 |     return -1;
 | 
|---|
| 208 |   }
 | 
|---|
| 209 | 
 | 
|---|
| 210 |   /* go... */
 | 
|---|
| 211 |   while (sectors_read < sectors_to_read) {
 | 
|---|
| 212 | 
 | 
|---|
| 213 |     sectors_take = min(sectors_to_read - sectors_read, sectors_per_read);
 | 
|---|
| 214 |     cb_take = SECTOR_SIZE * sectors_take;
 | 
|---|
| 215 | 
 | 
|---|
| 216 |     log("\rReading %u sector(s) this run; (%u sectors read so far)...",
 | 
|---|
| 217 |            sectors_take, sectors_read);
 | 
|---|
| 218 |     ret = DosRead(hf_disk, buf + buffer_offset, cb_take, &cb_read);
 | 
|---|
| 219 |     if (ret != NO_ERROR) {
 | 
|---|
| 220 |       fprintf(stderr, "\nFailed to read from disk %s, code %d\n", drv, ret);
 | 
|---|
| 221 |       rc = -1;
 | 
|---|
| 222 |       break;
 | 
|---|
| 223 |     }
 | 
|---|
| 224 | 
 | 
|---|
| 225 |     if (cb_read == 0) {
 | 
|---|
| 226 |       break;
 | 
|---|
| 227 |     }
 | 
|---|
| 228 | 
 | 
|---|
| 229 |     if (cb_read != cb_take) {
 | 
|---|
| 230 |       log("\n\nRead only %u instead of %u bytes...\n\n", cb_read, cb_take);
 | 
|---|
| 231 |       break;
 | 
|---|
| 232 |     }
 | 
|---|
| 233 | 
 | 
|---|
| 234 |     sectors_read += cb_read / SECTOR_SIZE;
 | 
|---|
| 235 |     if (fpo && fwrite(buf + buffer_offset, 1, cb_read, fpo) != cb_read) {
 | 
|---|
| 236 |       perror("Failed to write to output file");
 | 
|---|
| 237 |       rc = -1;
 | 
|---|
| 238 |       break;
 | 
|---|
| 239 |     }
 | 
|---|
| 240 | 
 | 
|---|
| 241 |   }
 | 
|---|
| 242 | 
 | 
|---|
| 243 |   log("\n%u sectors read.\n", sectors_read);
 | 
|---|
| 244 | 
 | 
|---|
| 245 |   DosClose(hf_disk);
 | 
|---|
| 246 |   free(buf);
 | 
|---|
| 247 | 
 | 
|---|
| 248 |   return rc;
 | 
|---|
| 249 | }
 | 
|---|
| 250 | 
 | 
|---|
| 251 | /******************************************************************************
 | 
|---|
| 252 |  * usage() - print usage summary to STDOUT
 | 
|---|
| 253 |  */
 | 
|---|
| 254 | void usage(void)
 | 
|---|
| 255 | {
 | 
|---|
| 256 |   printf("readsect.exe - read HD sectors and store them to an output file.\n"
 | 
|---|
| 257 |          "Call with a drive letter (for logical drive) or 1-based disk number (for\n"
 | 
|---|
| 258 |          "physical drive)\n\n"
 | 
|---|
| 259 |          "Usage:\n\n"
 | 
|---|
| 260 |          "readsect <drive letter|number> <number of sectors> <outfile>\n"
 | 
|---|
| 261 |          "         [<read buffer size>] [-lf <log file>] [-unaligned]\n"
 | 
|---|
| 262 |          "where:\n"
 | 
|---|
| 263 |          "-d drive letter       drive letter of logical drive to read from\n"
 | 
|---|
| 264 |          "   drive number       1-based physical disk number to read from\n"
 | 
|---|
| 265 |          "-s number of sectors  number of sectors to read (e.g. 1024)\n"
 | 
|---|
| 266 |          "-o outfile            path and filename of output file\n"
 | 
|---|
| 267 |          "-b read buffer size   optional buffer size in number of sectors (512 byte)\n"
 | 
|---|
| 268 |          "                      (default is %d sectors)\n"
 | 
|---|
| 269 |          "-l log file           optional log file name\n"
 | 
|---|
| 270 |          "-u                    use unaligned buffer\n",
 | 
|---|
| 271 |          SECTORS_PER_READ);
 | 
|---|
| 272 | }
 | 
|---|
| 273 | 
 | 
|---|
| 274 | 
 | 
|---|
| 275 | /*******************************************************************************
 | 
|---|
| 276 |  * log() - log a string to the log file (if any)
 | 
|---|
| 277 |  */
 | 
|---|
| 278 | void log(char *fmt, ...)
 | 
|---|
| 279 | {
 | 
|---|
| 280 |   va_list arglist;
 | 
|---|
| 281 |   FILE *fp;
 | 
|---|
| 282 |   char buf[500];
 | 
|---|
| 283 | 
 | 
|---|
| 284 |   /* assemble arguments */
 | 
|---|
| 285 |   va_start(arglist, fmt);
 | 
|---|
| 286 |   vsprintf(buf, fmt, arglist);
 | 
|---|
| 287 | 
 | 
|---|
| 288 |   if (log_fn && (fp = fopen(log_fn, "a")) != NULL) {
 | 
|---|
| 289 |     /* write string to log file */
 | 
|---|
| 290 |     fprintf(fp, buf);
 | 
|---|
| 291 |     fflush(fp);
 | 
|---|
| 292 |     fclose(fp);
 | 
|---|
| 293 |   }
 | 
|---|
| 294 | 
 | 
|---|
| 295 |   /* write to stdout */
 | 
|---|
| 296 |   printf(buf);
 | 
|---|
| 297 | 
 | 
|---|
| 298 | }
 | 
|---|