home *** CD-ROM | disk | FTP | other *** search
/ Shareware Supreme Volume 6 #1 / swsii.zip / swsii / 165 / DTST20.ZIP / DTST.C < prev    next >
C/C++ Source or Header  |  1990-05-01  |  14KB  |  478 lines

  1.  
  2. /*
  3.  * DTST - Special disk test - finds slow sectors
  4.  *
  5.  * Copyright 1988-1990 Samuel H. Smith; All rights reserved.
  6.  *
  7.  * Written 13-feb-88 (rev. 01-may-90)
  8.  *
  9.  */
  10.  
  11. #define VERSION  "DiskTest v2.0 (5-01-90)   Copyright 1988-1990 S.H.Smith"
  12.  
  13. #include <bios.h>
  14. #include <ctype.h>
  15. #include <dos.h>
  16. #include <process.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20.  
  21. #include "abs4read.h"
  22.  
  23. #define SECSIZ   512
  24. #define NSECT    31
  25. char *secbuf;
  26. char *patbuf;
  27.  
  28. #define DIRPERSEC 16            /* directory entries per sector */
  29.  
  30.  
  31. struct bootrec {        /* found at logical sector 0 (absolute read) */
  32.     char jmp[3];
  33.     char oem[8];
  34.     int SectSiz;        /* bytes per sector */
  35.     char ClustSiz;        /* sectors per cluster */
  36.     int ResSecs;        /* reserved sectors before first FAT */
  37.     char FatCnt;        /* number of FATs */
  38.     int RootSiz;        /* max root directory entries (32 bytes each) */
  39.         unsigned sTotSecs;      /* total number or sectors in small partition */
  40.     char Media;        /* media descriptor */
  41.     int FatSize;        /* sectors per fat */
  42.     int TrkSecs;        /* sectors per track */
  43.     int HeadCnt;        /* number of heads */
  44.         long HidnSec;           /* hidden sectors */
  45.         long TotSecs;           /* hidden sectors */
  46.  
  47.         char filler[0x200 - 0x24];
  48. } bootrec;
  49.  
  50.  
  51. #define MAXBAD 2000
  52. unsigned badclusts[MAXBAD];
  53. unsigned badcount = 0;
  54.  
  55. unsigned reserved_sec;
  56. unsigned cyl_secs;
  57.  
  58. #define BADCLUST 0xFFF7
  59. #define FATS_PER_FATSEC (SECSIZ / sizeof(unsigned))
  60. unsigned fatbuf[FATS_PER_FATSEC];
  61.  
  62. int ptrack = -1;
  63. int maxtime = 30;   /* maximum number of ticks for a good read */
  64. unsigned cluster;
  65. int track;
  66.  
  67. int slows = 0;
  68. int new_bad = 0;
  69. int write_check = 0;
  70.  
  71.  
  72. /* --------------------------------------------------------------- */
  73. char *report_error(char result)
  74. {
  75.         switch (result+0x13) {
  76.                 case 0x13:      return "Write protect";
  77.                 case 0x14:      return "Unknown unit";
  78.                 case 0x15:      return "Not ready";
  79.                 case 0x16:      return "Unknown command";
  80.                 case 0x17:      return "Data error";
  81.                 case 0x18:      return "Bad request";
  82.                 case 0x19:      return "Seek error";
  83.                 case 0x1a:      return "Unknown media";
  84.                 case 0x1b:      return "Sector not found";
  85.                 case 0x1d:      return "Write fault";
  86.                 case 0x1e:      return "Read fault";
  87.                 case 0x1f:      return "General failure";
  88.                 case 0x20:      return "Share violation";
  89.                 case 0x21:      return "Lock violation";
  90.                 case 0x22:      return "Bad disk change";
  91.  
  92.                 default:
  93.                         {
  94.                                 static char message[80];
  95.                                 sprintf(message,"Error %02x",result);
  96.                                 return message;
  97.                         }
  98.         }
  99. }
  100.  
  101.  
  102.  
  103. /* --------------------------------------------------------------- */
  104. unsigned sec2clust(long secnum)
  105. {
  106.         if (secnum < reserved_sec)
  107.                 return 0;
  108.     else
  109.                 return 2 + (secnum - (long)reserved_sec) /
  110.                                   (long)bootrec.ClustSiz;
  111. }
  112.  
  113. /* --------------------------------------------------------------- */
  114. long clust2sec(unsigned clust)
  115. {
  116.          return (long)(clust - 2) * (long)bootrec.ClustSiz + (long)reserved_sec;
  117. }
  118.  
  119.  
  120. /* --------------------------------------------------------------- */
  121. void generate_pattern(void)
  122. {
  123.     int i;
  124.  
  125.     srand(biostime(0, 0L));
  126.  
  127.     for (i = 0; i < sizeof(patbuf); i++)
  128.         patbuf[i] = rand();
  129. }
  130.  
  131.  
  132. /* --------------------------------------------------------------- */
  133. int exclude_bad_blocks(unsigned cluster, int track)
  134. {
  135.     int i;
  136.  
  137.     for (i = 0; i < badcount; i++) {
  138.         if (badclusts[i] == cluster) {
  139.             printf("\r  Track %d, cluster %u - Already marked bad\n",
  140.                    track, cluster);
  141.             return 1;
  142.         }
  143.     }
  144.  
  145.     return 0;
  146. }
  147.  
  148.  
  149. /* --------------------------------------------------------------- */
  150. void report_cluster_number(FILE *fd, long secnum, int seccnt, unsigned speed)
  151. {
  152.         if (secnum < reserved_sec)
  153.                  fprintf(fd,"\r  Track %d, sector %u, speed %u mS ",
  154.                          track, secnum, speed);
  155.  
  156.         else if (seccnt > bootrec.ClustSiz)
  157.                 fprintf(fd,"\r  Track %d, clusters %u-%u, speed %u mS ",
  158.                        track,
  159.                        (unsigned)cluster,
  160.                        (unsigned)(cluster + (seccnt / bootrec.ClustSiz) - 1),
  161.                        speed);
  162.     else
  163.                 fprintf(fd,"\r  Track %d, cluster %u, speed %u mS ",
  164.                track, cluster, speed);
  165. }
  166.  
  167.  
  168. /* --------------------------------------------------------------- */
  169. void rewrite_block(char disk, long secnum, int seccnt)
  170.  /* perform a READ/WRITE/READ/ReWRITE test */
  171. {
  172.         char result;
  173.  
  174.         result = abs4write(disk - 'A', seccnt, secnum, patbuf);
  175.     if (result)
  176.                 printf("- PATTERN WRITE FAILED! [%s]\n",report_error(result));
  177.  
  178.         result = abs4read(disk - 'A', seccnt, secnum, patbuf);
  179.     if (result)
  180.                 printf("- PATTERN READ FAILED! [%s]\n",report_error(result));
  181.  
  182.     /* replace original data */
  183.         result = abs4write(disk - 'A', seccnt, secnum, secbuf);
  184.     if (result) {
  185.                 printf("- ORIGINAL RE-WRITE FAILED! [%s]\n\n",report_error(result));
  186.  
  187.                 result = abs4write(disk - 'A', seccnt, secnum, secbuf);
  188.         if (result) {
  189.                         printf("- SECOND RE-WRITE FAILED! [%s]\n\n",report_error(result));
  190.             printf("PROGRAM ABORTED - POSSIBLE DATA LOSS!\n");
  191.             exit(1);
  192.         }
  193.     }
  194. }
  195.  
  196.  
  197. /* --------------------------------------------------------------- */
  198. unsigned check_block(char disk, long secnum, int seccnt)
  199.  /* test a sector/block, return elapsed time for read.  time < 0 on errors */
  200. {
  201.         char result;
  202.     long start;
  203.     int elapsed;
  204.     unsigned speed;
  205.  
  206.         cluster = sec2clust(secnum);
  207.         track = secnum / cyl_secs;
  208.  
  209. /*
  210. printf("\rcheckblock:\tseccnt=%d(@%04x:%04x) secnum=%ld buf=%04x:%04x\n ",
  211.                 seccnt,FP_SEG(&seccnt),FP_OFF(&seccnt),
  212.                 secnum,
  213.                 FP_SEG(secbuf),FP_OFF(secbuf));
  214. */
  215.         if (seccnt > bootrec.ClustSiz)
  216.                 if (exclude_bad_blocks(cluster, track))
  217.                         return 0;
  218.  
  219.     start = biostime(0, 0L);
  220.         result = abs4read(disk - 'A', seccnt, secnum, secbuf);
  221.     elapsed = biostime(0, start) - start;
  222.     speed = elapsed * 18;
  223.  
  224.         if ((elapsed > maxtime) || (result)) {
  225.         ptrack = track;
  226.                 report_cluster_number(stdout, secnum, seccnt, speed);
  227.         } else
  228.  
  229.         /* send non-error status to stderr so it won't clutter logfiles */
  230.         if (track != ptrack) {
  231.         ptrack = track;
  232.                 report_cluster_number(stderr, secnum, seccnt, speed);
  233.     }
  234.  
  235.  
  236.     /* extablish max time for read based on SECOND read request */
  237. /********
  238.         if (maxtime == 999)
  239.         maxtime = 888;
  240.     else if (maxtime == 888)
  241.         maxtime = (elapsed + 1) * 3;
  242. *******/
  243.  
  244.     /* report errors or slow reads */
  245.     if (result) {
  246.                 printf("- HARD ERROR! [%s]\n",report_error(result));
  247.                 if (seccnt <= bootrec.ClustSiz)
  248.             new_bad++;
  249.     }
  250.     else if (elapsed > maxtime) {
  251.         printf("- SLOW! (soft errors)\n");
  252.         slows++;
  253.         result = 999;
  254.     }
  255.  
  256.     if ((result == 0) && write_check)
  257.                 rewrite_block(disk, secnum, seccnt);
  258.  
  259.     if (result)
  260.         return -elapsed;
  261.     else
  262.         return elapsed;
  263. }
  264.  
  265.  
  266. /* --------------------------------------------------------------- */
  267. void test_range(char disk, long low, long last)
  268. {
  269.         long current;
  270.         long single;
  271.     int elapsed;
  272.     int nsect;
  273.  
  274.     if (write_check)
  275.         printf("\nPerforming READ/WRITE test:\n");
  276.     else
  277.         printf("\nPerforming READ test:\n");
  278.  
  279.     nsect = bootrec.TrkSecs;
  280.     if (nsect > NSECT)
  281.         nsect = NSECT;    /* special case for large-track disks
  282.                  * (bernouli) */
  283.  
  284.     for (current = low; current <= last - nsect + 1; current += nsect) {
  285.  
  286. /*
  287. printf("\rrange(1):\t nsect=%d(@%04x:%04x) current=%ld \n",
  288.         nsect,FP_SEG(&nsect),FP_OFF(&nsect),current);
  289. */
  290.                 elapsed = check_block(disk, current, nsect);
  291. /*
  292. printf("\rrange(2):\t nsect=%d(@%04x:%04x) current=%ld elapsed=%u\n",
  293.         nsect,FP_SEG(&nsect),FP_OFF(&nsect),current,elapsed);
  294. */
  295.                 /*
  296.          * if there was an error then switch to single sectors and
  297.          * rescan the sectors in question 
  298.          */
  299.         if ((elapsed < 0) && (nsect > 1)) {
  300.  
  301.  
  302.                         for (single = current;
  303.                  single < (current + nsect);
  304.                  single += bootrec.ClustSiz) {
  305. /*
  306. printf("\rrange(3):\t nsect=%d(@%04x:%04x) current=%ld \n",
  307.         nsect,FP_SEG(&nsect),FP_OFF(&nsect),current);
  308. */
  309.                                 elapsed = check_block(disk, single, 1);
  310. /*
  311. printf("\rrange(4):\t nsect=%d(@%04x:%04x) current=%ld elapsed=%u\n",
  312.         nsect,FP_SEG(&nsect),FP_OFF(&nsect),current,elapsed);
  313. */
  314.                         }
  315.             printf("\r%78s\r", " ");
  316.         }
  317.     }
  318.  
  319.     printf("\n\nTest completed:\n  %d slow clusters\n  %d new bad clusters\n",
  320.            slows, new_bad);
  321. }
  322.  
  323.  
  324. /* --------------------------------------------------------------- */
  325. void get_disk_information(char disk)
  326. {
  327.     unsigned fatsec;
  328.     unsigned bufpos;
  329.     unsigned cluster;
  330.         char result;
  331.         FILE *fd;
  332.         char name[] = "x:\\$$$$$$$$.$$$";
  333.  
  334.         printf("\nDisk %c:\n", disk);
  335.  
  336.  
  337.         /* force dos to access this drive -- otherwise int 0x25 won't work */
  338.         name[0] = disk;
  339.         fd = fopen(name,"r");
  340.         if (fd != NULL)
  341.                 fclose(fd);
  342.  
  343.  
  344.         /* read boot record */
  345.         result = abs4read(disk - 'A', 1, 0, (char *)&bootrec);
  346.     if (result) {
  347.                 printf("CANNOT READ BOOT RECORD! [%s]\n",report_error(result));
  348.         exit(1);
  349.     }
  350.  
  351.  
  352.         /* for dos 3.3 or 4.0 small partitions, discard high word of hidden
  353.            sector count and convert 16 bit sector count to 32 bits.
  354.            otherwise we are under dos 4.0 and have a large partition */
  355.  
  356.         if (bootrec.sTotSecs != 0) {
  357.             bootrec.HidnSec &= 0x0000ffff;
  358.             bootrec.TotSecs = bootrec.sTotSecs;
  359.         }
  360.  
  361.         reserved_sec = bootrec.ResSecs +
  362.         (bootrec.FatCnt * bootrec.FatSize) +
  363.         (bootrec.RootSiz / DIRPERSEC);
  364.  
  365.     cyl_secs = bootrec.TrkSecs * bootrec.HeadCnt;
  366.  
  367.     printf("  Number of heads. . . . %d\n", bootrec.HeadCnt);
  368.     printf("  Number of tracks . . . %d\n", bootrec.TotSecs / cyl_secs);
  369.     printf("  Sectors per track. . . %d\n", bootrec.TrkSecs);
  370.     printf("  Sectors per cluster. . %d\n", bootrec.ClustSiz);
  371.     printf("  Total clusters . . . . %u\n", sec2clust(bootrec.TotSecs));
  372.  
  373.         printf("  Bad cluster numbers. . ");
  374.  
  375.         cluster = 0;
  376.         for (fatsec = 1; fatsec <= bootrec.FatSize; fatsec++) {
  377.                 result = abs4read(disk - 'A', 1, fatsec, (char *)fatbuf);
  378.         if (result) {
  379.                         printf("\n\nERROR READING FAT SECTOR %u! [%s]\n",
  380.                                 fatsec,report_error(result));
  381.             exit(1);
  382.         }
  383.  
  384.                 for (bufpos = 0; bufpos < FATS_PER_FATSEC; bufpos++) {
  385.             if (fatbuf[bufpos] == BADCLUST) {
  386.                 badclusts[badcount] = cluster;
  387.                 badcount++;
  388.  
  389.                 printf("%-5u ", cluster);
  390.                 if ((badcount % 9) == 0)
  391.                     printf("\n                         ");
  392.             }
  393.                         cluster++;
  394.                 }
  395.     }
  396.  
  397.     if (badcount == 0)
  398.         printf("None");
  399.     printf("\n");
  400. }
  401.  
  402.  
  403. /* --------------------------------------------------------------- */
  404. void usage(void)
  405. {
  406.         printf("\n%s\n\n", VERSION);
  407.         printf("Usage:\tdisktest D: [<lowclust> <hiclust>] [-W]\n");
  408.         printf("\t-W\tenables the nondestructive READ/WRITE test.\n\n");
  409.     printf("DTST is a small utility that is used to check out your hard disks.  It\n");
  410.         printf("has the special ability to report 'slow' sectors.  These are sectors\n");
  411.     printf("that read properly, but often require retries.  I feel that such sectors\n");
  412.     printf("are on the verge of failure and should quickly be locked out before they\n");
  413.     printf("cause any harm.  USE AT YOUR OWN RISK.\n");
  414.     exit(1);
  415. }
  416.  
  417.  
  418. /* --------------------------------------------------------------- */
  419. void decode_option(char *option)
  420. {
  421.         if (strcmp(option, "-W") == 0)
  422.                 write_check = 1;
  423.     else
  424.         usage();
  425. }
  426.  
  427.  
  428. /* --------------------------------------------------------------- */
  429. main(int argc, char **argv)
  430. {
  431.     char disk;
  432.         long lowsec, hisec;
  433.  
  434.     if (argc == 1)
  435.         usage();
  436.  
  437.         secbuf = (char *) malloc(NSECT * SECSIZ);
  438.         patbuf = (char *) malloc(NSECT * SECSIZ);
  439.         if ((secbuf == NULL) || (patbuf == NULL)) {
  440.                 printf("Can't allocate buffers\n");
  441.                 exit(1);
  442.         }
  443.  
  444.         disk = argv[1][0];
  445.     if (islower(disk))
  446.         disk = toupper(disk);
  447.  
  448.     get_disk_information(disk);
  449.     lowsec = 0;
  450.     hisec = bootrec.TotSecs;
  451.  
  452.     switch (argc) {
  453.     case 2:
  454.         break;        /* default to whole disk */
  455.  
  456.     case 3:
  457.         decode_option(argv[2]);
  458.         break;
  459.  
  460.     case 5:
  461.         decode_option(argv[4]);
  462.  
  463.     case 4:
  464.                 lowsec = clust2sec(atoi(argv[2]));
  465.                 hisec = clust2sec(atoi(argv[3]));
  466.         break;
  467.  
  468.     default:
  469.         usage();
  470.     }
  471.  
  472.     generate_pattern();
  473.     test_range(disk, lowsec, hisec);
  474.  
  475.         return new_bad;
  476. }
  477.  
  478.