home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / hpfsdt2.zip / hpfsdt.c < prev    next >
C/C++ Source or Header  |  1999-10-16  |  12KB  |  357 lines

  1. /************************************************************************/
  2. /*                                                                      */
  3. /*  HPFSDT.C - Toggle the "dirty" flag on HPFS drives under OS/2        */
  4. /*                                                                      */
  5. /*  (C)1999 Mike Ruskai, all rights reserved, blah, blah, blah          */
  6. /*                                                                      */
  7. /*  Contact:  thanmeister@geocities.com                                 */
  8. /*                                                                      */
  9. /*  The following code reads the partition status byte contained in     */
  10. /*  the HPFS SpareBlock structure, located at logical sector number 17  */
  11. /*  on an HPFS volume, toggles the first bit, and writes it back to     */
  12. /*  the same location on the drive.                                     */
  13. /*                                                                      */
  14. /*  This code was written for compilation by IBM's VisualAge C++        */
  15. /*  version 3 compiler, though it should work with any 32-bit OS/2      */
  16. /*  compiler (you may have to remove the _Inline linkage).              */
  17. /*                                                                      */
  18. /*  You may use and modify this code, so long as proper credit is       */
  19. /*  given, and the appropriate warnings about usage are preserved.      */
  20. /*                                                                      */
  21. /*  And speaking of credit, I'd like to thank the UseNet presence of    */
  22. /*  Peter Fitzsimmons, whose articles (archived at DejaNews) provided   */
  23. /*  the necessary examples for doing a couple of the things below.      */
  24. /*  I'd also like to thank EDM/2, the online developer's magazine for   */
  25. /*  OS/2, for publishing the articles on HPFS which were my source of   */
  26. /*  information on how to do what this program does.                    */
  27. /*                                                                      */
  28. /************************************************************************/
  29.  
  30. #define INCL_DOSDEVICES
  31. #define INCL_DOSDEVIOCTL
  32. #define INCL_DOSFILEMGR
  33. #define INCL_DOSERRORS
  34. #include <os2.h>
  35. #include <string.h>
  36. #include <stdlib.h>
  37. #include <stdio.h>
  38. #include <ctype.h>
  39.  
  40. #define TOGGLE_DIRTY_CLEAN 1
  41. #define TOGGLE_CLEAN_DIRTY 2
  42. #define FAIL_USAGE 3
  43. #define FAIL_BAD_DRIVE_STRING 4
  44. #define FAIL_INVALID_DRIVE 5
  45. #define FAIL_UNKNOWN_FSTYPE 6
  46. #define FAIL_NOT_HPFS 7
  47. #define FAIL_OPEN_DRIVE 8
  48. #define FAIL_LOCK_DRIVE 9
  49. #define FAIL_UNKNOWN_SECTOR_SIZE 10
  50. #define FAIL_BAD_SECTOR_SIZE 11
  51. #define FAIL_FILE_POINTER 12
  52. #define FAIL_READ_DRIVE 13
  53. #define FAIL_WRITE_DRIVE 14
  54. #define FAIL_MEMORY_ALLOCATION 15
  55.  
  56. _Inline void Usage(void);
  57. _Inline void rcReportError(char *fName, ULONG rc);
  58. _Inline void ReportError(char *eString);
  59.  
  60. int main(int argc, char *argv[])
  61. {
  62.     HFILE driveHandle;
  63.     ULONG openAction, openMode, bufLen, bpParmLen, bpDataLen;
  64.     int exitval, i;
  65.     APIRET rc;
  66.     char driveString[3], driveLetter, *fsStuff, partStatus, pDirty=1,
  67.          pStat[8], cStat[8], bpParm[2], bpData[50], *sdata;
  68.     USHORT sectorSize;
  69.     FSQBUFFER2 *fsBuffer;
  70.     BIOSPARAMETERBLOCK bpBlock;
  71.     TRACKLAYOUT *tl;
  72.  
  73.     if (argc!=2)
  74.         {
  75.         Usage();
  76.         return FAIL_USAGE;
  77.         }
  78.  
  79.     memset(driveString, 0, sizeof(driveString));
  80.     strncpy(driveString, argv[1], sizeof(driveString)-1);
  81.  
  82.     /*
  83.         The following verifies that the drive letter provided
  84.         is a letter from A to Z, and that the second character
  85.         is a colon
  86.     */
  87.  
  88.     driveLetter=toupper(driveString[0]);
  89.     if (driveLetter<65 || driveLetter>90 || driveString[1]!=':')
  90.         {
  91.         ReportError("Invalid drive string provided.");
  92.         Usage();
  93.         return FAIL_BAD_DRIVE_STRING;
  94.         }
  95.  
  96.     /*
  97.         The following uses DosQueryFSAttach() to verify that the
  98.         file system of the specified drive is indeed HPFS, as
  99.         continuing otherwise would have somewhat unpredictable
  100.         results.  A lexically valid, but non-existent drive would
  101.         also be caught at this point.
  102.     */
  103.  
  104.     fsBuffer=(PFSQBUFFER2)malloc(2048);
  105.     if (fsBuffer==NULL)
  106.         {
  107.         ReportError("Memory allocation error.");
  108.         return FAIL_MEMORY_ALLOCATION;
  109.         }
  110.     memset(fsBuffer, 0, 2048);
  111.     bufLen=2048;
  112.     rc=DosQueryFSAttach(driveString, 0, FSAIL_QUERYNAME, fsBuffer, &bufLen);
  113.     if (rc!=NO_ERROR)
  114.         {
  115.         if (rc==ERROR_INVALID_DRIVE)
  116.             {
  117.             ReportError("Invalid drive specified.");
  118.             return FAIL_INVALID_DRIVE;
  119.             }
  120.         else
  121.             {
  122.             ReportError("Unable to determine file system.");
  123.             rcReportError("DosQueryFSAttach", rc);
  124.             return FAIL_UNKNOWN_FSTYPE;
  125.             }
  126.         }
  127.     fsStuff=(char*)(fsBuffer->szName+fsBuffer->cbName+1);
  128.     if (strcmp(fsStuff, "HPFS")!=0)
  129.         {
  130.         ReportError("Specified drive is not using the HPFS file system.");
  131.         Usage();
  132.         return FAIL_NOT_HPFS;
  133.         }
  134.     free(fsBuffer);
  135.  
  136.     /*
  137.         This next bit uses DosOpen() to open the entire drive for reading
  138.         and writing.
  139.     */
  140.  
  141.     openMode=0;
  142.     openMode=openMode | OPEN_FLAGS_DASD | OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE;
  143.     rc=DosOpen(driveString, &driveHandle, &openAction, 0L, 0L, FILE_OPEN, openMode, 0L);
  144.     if (rc!=NO_ERROR)
  145.         {
  146.         ReportError("Unable to open drive.");
  147.         rcReportError("DosOpen", rc);
  148.         return FAIL_OPEN_DRIVE;
  149.         }
  150.  
  151.     /*
  152.         Once successfully opened, the drive needs to be locked, so that
  153.         we know it's safe to modify the SpareBlock.  This is done with
  154.         a category 8, function 0x00 call to DosDevIOCtl().  This will fail 
  155.         on drives which are in use (open programs, open files, etc.).
  156.     */
  157.  
  158.     rc=DosDevIOCtl(driveHandle, IOCTL_DISK, DSK_LOCKDRIVE, 0, 0, 0, 0, 0, 0);
  159.     if (rc!=NO_ERROR)
  160.         {
  161.         DosClose(driveHandle);
  162.         ReportError("Unable to lock drive.");
  163.         rcReportError("DosDevIOCtl", rc);
  164.         return FAIL_LOCK_DRIVE;
  165.         }
  166.  
  167.     /*
  168.         Here we use a category 8, function 0x63 call to DosDevIOCtl()
  169.         to determine the physical sector size of the drive in question.
  170.         This information is necessary to correctly set the file
  171.         pointer to the location of the partition status byte.  The only
  172.         checking done is to verify that the returned value is greater
  173.         than zero.  Otherwise, the program relies entirely on the 
  174.         validity of the data in the BIOS Parameter Block, which is just
  175.         a label for certain information contained in the boot sector.
  176.         If the boot sector is corrupted, and an incorrect sector size
  177.         is returned, then the results of the program are unpredictable.
  178.     */
  179.  
  180.     memset(bpParm, 0, sizeof(bpParm));
  181.     memset(bpData, 0, sizeof(bpData));
  182.     bpParmLen=0;
  183.     bpDataLen=0;
  184.     rc=DosDevIOCtl(driveHandle, IOCTL_DISK, DSK_GETDEVICEPARAMS,
  185.                    bpParm, sizeof(bpParm), &bpParmLen,
  186.                    bpData, sizeof(bpData), &bpDataLen);
  187.  
  188.     if (rc!=NO_ERROR)
  189.         {
  190.         DosDevIOCtl(driveHandle, IOCTL_DISK, DSK_UNLOCKDRIVE, 0, 0, 0, 0, 0, 0);
  191.         DosClose(driveHandle);
  192.         ReportError("Unable to determine sector size.");
  193.         rcReportError("DosDevIOCtl", rc);
  194.         return FAIL_UNKNOWN_SECTOR_SIZE;
  195.         }
  196.     
  197.     memset(&bpBlock, 0, sizeof(bpBlock));
  198.     memcpy(&bpBlock, bpData, sizeof(bpBlock));
  199.     sectorSize=0;
  200.     sectorSize=bpBlock.usBytesPerSector;
  201.     if (sectorSize==0)
  202.         {
  203.         DosDevIOCtl(driveHandle, IOCTL_DISK, DSK_UNLOCKDRIVE, 0, 0, 0, 0, 0, 0);
  204.         DosClose(driveHandle);
  205.         ReportError("Bad sector size returned by DosDevIOCtl().");
  206.         return FAIL_BAD_SECTOR_SIZE;
  207.         }
  208.  
  209.     /*
  210.         Since the file access API's of OS/2 Warp 4 and earlier can't
  211.         handle files sizes greater than 2GB, the following gets around
  212.         that by using DosDevIOCtl() to read from the drive "directly".
  213.         The head and cylinder numbers are relative to the beginning of
  214.         the logical drive, since this is a category 0x08 function. The
  215.         data read will be from one sector before the boot sector onwards.
  216.     */
  217.  
  218.     #define SECC 1 /* sector count to read */
  219.  
  220.     bpParmLen=sizeof(TRACKLAYOUT)+sizeof(ULONG)*SECC;
  221.     tl=(PTRACKLAYOUT)malloc(bpParmLen);
  222.     if (tl==NULL)
  223.         {
  224.         DosDevIOCtl(driveHandle, IOCTL_DISK, DSK_UNLOCKDRIVE, 0, 0, 0, 0, 0, 0);
  225.         DosClose(driveHandle);
  226.         ReportError("Memory allocation error.");
  227.         return FAIL_MEMORY_ALLOCATION;
  228.         }
  229.  
  230.     bpDataLen=sectorSize*SECC;
  231.     sdata=(char*)malloc(bpDataLen);
  232.     if (sdata==NULL)
  233.         {
  234.         DosDevIOCtl(driveHandle, IOCTL_DISK, DSK_UNLOCKDRIVE, 0, 0, 0, 0, 0, 0);
  235.         DosClose(driveHandle);
  236.         ReportError("Memory allocation error.");
  237.         return FAIL_MEMORY_ALLOCATION;
  238.         }
  239.  
  240.     memset(sdata, 0, bpDataLen);
  241.  
  242.     tl->bCommand=0;
  243.     tl->usHead=1;
  244.     tl->usCylinder=0;
  245.     tl->usFirstSector=0;
  246.     tl->cSectors=SECC;
  247.  
  248.     for (i=0; i<SECC; i++)
  249.         {
  250.         tl->TrackTable[i].usSectorNumber=i+18;
  251.         tl->TrackTable[i].usSectorSize=sectorSize;
  252.         }
  253.  
  254.     rc=DosDevIOCtl(driveHandle, IOCTL_DISK, DSK_READTRACK, tl,
  255.                    bpParmLen, &bpParmLen, sdata, bpDataLen, &bpDataLen);
  256.  
  257.     if (rc!=NO_ERROR)
  258.         {
  259.         DosDevIOCtl(driveHandle, IOCTL_DISK, DSK_UNLOCKDRIVE, 0, 0, 0, 0, 0, 0);
  260.         DosClose(driveHandle);
  261.         ReportError("Unable to read SpareBlock sector.");
  262.         rcReportError("DosDevIOCtl", rc);
  263.         return FAIL_READ_DRIVE;
  264.         }
  265.  
  266.     partStatus=0;
  267.     memcpy(&partStatus, &sdata[7], 1);
  268.  
  269.     /*
  270.         If a bitwise AND of the partition status byte and the value
  271.         1 returns 1, the partition is dirty, and the value of the whole
  272.         byte is decremented.  Otherwise, the partition is clean, and the
  273.         byte is incremented.  The status strings for before and after are
  274.         set accordingly.
  275.     */
  276.  
  277.     if (partStatus & pDirty)
  278.         {
  279.         partStatus--;
  280.         strcpy(pStat, "\"dirty\"");
  281.         strcpy(cStat, "\"clean\"");
  282.         exitval=TOGGLE_DIRTY_CLEAN;
  283.         }
  284.     else
  285.         {
  286.         partStatus++;
  287.         strcpy(pStat, "\"clean\"");
  288.         strcpy(cStat, "\"dirty\"");
  289.         exitval=TOGGLE_CLEAN_DIRTY;
  290.         }
  291.  
  292.     memcpy(&sdata[7], &partStatus, 1);
  293.  
  294.     /*
  295.         Having modified the partition status byte, and sticking it back
  296.         in with the sector data, we write that sector back to the drive,
  297.         again using DosDevIOCtl() to circumvent the file access API
  298.         limitations.
  299.     */
  300.  
  301.     rc=DosDevIOCtl(driveHandle, IOCTL_DISK, DSK_WRITETRACK, tl,
  302.                    bpParmLen, &bpParmLen, sdata, bpDataLen, &bpDataLen);
  303.  
  304.     if (rc!=NO_ERROR)
  305.         {
  306.         DosDevIOCtl(driveHandle, IOCTL_DISK, DSK_UNLOCKDRIVE, 0, 0, 0, 0, 0, 0);
  307.         DosClose(driveHandle);
  308.         ReportError("Unable to write SpareBlock sector.");
  309.         rcReportError("DosDevIOCtl", rc);
  310.         return FAIL_READ_DRIVE;
  311.         }
  312.  
  313.     /*
  314.         Finally, we report which way the toggle went, unlock the drive,
  315.         and close the file handle.  All done.
  316.     */
  317.  
  318.     printf("\nDrive was %s - changed to %s.\n", pStat, cStat);
  319.  
  320.     DosDevIOCtl(driveHandle, IOCTL_DISK, DSK_UNLOCKDRIVE, 0, 0, 0, 0, 0, 0);
  321.     DosClose(driveHandle);
  322.  
  323.     free(tl);
  324.     free(sdata);
  325.  
  326.     return exitval;
  327. }
  328.  
  329. /*
  330.     This just shows the program usage, which is exceedingly simple.
  331. */
  332.  
  333. _Inline void Usage(void)
  334. {
  335.     ReportError("Usage: hpfsdt.exe <drive letter>");
  336.     ReportError("<drive letter> - letter of HPFS drive to toggle dirty bit on");
  337.     return;
  338. }
  339.  
  340. /*
  341.     These next two just clean up error reporting in the main function.
  342.     The first gives a generic API function failure message.  The second
  343.     just writes a string.  Both go to standard error.
  344. */
  345.  
  346. _Inline void rcReportError(char *fName, ULONG rc)
  347. {
  348.     printf("\n%s() failed with error %d.\n", fName, rc);
  349.     return;
  350. }
  351.  
  352. _Inline void ReportError(char *eString)
  353. {
  354.     printf("\n%s\n", eString);
  355.     return;
  356. }
  357.