home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 10 / mycd10.iso / share / os2 / utilidad / fst03e / diskio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-01-09  |  31.2 KB  |  1,081 lines

  1. /* diskio.c -- Disk/sector I/O for fst
  2.    Copyright (c) 1995-1996 by Eberhard Mattes
  3.  
  4. This file is part of fst.
  5.  
  6. fst is free software; you can redistribute it and/or modify it
  7. under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. fst is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with fst; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 59 Temple Place - Suite 330,
  19. Boston, MA 02111-1307, USA.  */
  20.  
  21.  
  22. #define INCL_DOSDEVIOCTL
  23. #define INCL_DOSDEVICES
  24. #include <os2.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <stdarg.h>
  28. #include <string.h>
  29. #include <ctype.h>
  30. #include <io.h>
  31. #include <fcntl.h>
  32. #include <limits.h>
  33. #include "fst.h"
  34. #include "crc.h"
  35. #include "diskio.h"
  36.  
  37. /* Size of the hash table used for speeding up reading snapshot files. */
  38.  
  39. #define HASH_SIZE       997
  40.  
  41. /* This value marks the end of a hash chain.  It should be an
  42.    `impossible' sector number. */
  43.  
  44. #define HASH_END        0xffffffff
  45.  
  46. /* Method for reading and writing sectors. */
  47.  
  48. enum disk_io_type
  49. {
  50.   DIOT_DISK_DASD,               /* DosRead, DosWrite */
  51.   DIOT_DISK_TRACK,              /* Logical: DSK_READTRACK, DSK_WRITETRACK */
  52.   DIOT_SNAPSHOT,                /* Snapshot file */
  53.   DIOT_CRC                      /* CRC file */
  54. };
  55.  
  56. /* Data for DIOT_DISK_DASD. */
  57.  
  58. struct diskio_dasd
  59. {
  60.   HFILE hf;                     /* File handle */
  61.   char sec_mode;                /* TRUE iff sector mode active */
  62. };
  63.  
  64. /* Data for DIOT_DISK_TRACK. */
  65.  
  66. struct diskio_track
  67. {
  68.   HFILE hf;                     /* File handle */
  69.   ULONG layout_size;            /* Size of structure pointed to by playout */
  70.   ULONG hidden;                 /* Hidden sectors */
  71.   ULONG spt;                    /* Sectors per track */
  72.   ULONG heads;                  /* Number of heads */
  73.   TRACKLAYOUT *playout;         /* Parameter for DSK_READTRACK */
  74.   BYTE *track_buf;              /* Buffer for one track */
  75. };
  76.  
  77. /* Data for DIOT_SNAPSHOT. */
  78.  
  79. struct diskio_snapshot
  80. {
  81.   HFILE hf;                     /* File handle */
  82.   ULONG sector_count;           /* Total number of sectors */
  83.   ULONG *sector_map;            /* Table containing relative sector numbers */
  84.   ULONG *hash_next;             /* Hash chains */
  85.   ULONG version;                /* Format version number */
  86.   ULONG hash_start[HASH_SIZE];  /* Hash chain heads */
  87. };
  88.  
  89. /* Data for DIOT_CRC. */
  90.  
  91. struct diskio_crc
  92. {
  93.   FILE *f;                      /* Stream */
  94.   ULONG version;                /* Format version number */
  95.   crc_t *vec;                   /* See diskio_crc_load() */
  96. };
  97.  
  98. /* DISKIO structure. */
  99.  
  100. struct diskio
  101. {
  102.   enum disk_io_type type;       /* Method */
  103.   ULONG spt;                    /* Sectors per track */
  104.   ULONG total_sectors;          /* Total number of sectors */
  105.   union
  106.     {
  107.       struct diskio_dasd dasd;
  108.       struct diskio_track track;
  109.       struct diskio_snapshot snapshot;
  110.       struct diskio_crc crc;
  111.     } x;                        /* Method-specific data */
  112. };
  113.  
  114. /* This variable selects the method of direct disk I/O to use.
  115.    ACCESS_DASD selects DosRead and DosWrite, ACCESS_LOG_TRACK selects
  116.    DSK_READTRACK and DSK_WRITETRACK for logical disks. */
  117.  
  118. enum access_type diskio_access;
  119.  
  120. /* Non-zero if write access is required. */
  121.  
  122. char write_enable;
  123.  
  124. /* Non-zero if removable disks are allowed. */
  125.  
  126. char removable_allowed;
  127.  
  128. /* Non-zero to ignore failure to lock the disk. */
  129.  
  130. char ignore_lock_error;
  131.  
  132. /* Non-zero to disable locking (FOR TESTING ONLY!). */
  133.  
  134. char dont_lock;
  135.  
  136. /* Type of the save file. */
  137.  
  138. enum save_type save_type;
  139.  
  140. /* This is the save file. */
  141.  
  142. FILE *save_file;
  143.  
  144. /* Name of the save file.  There is no save file if this variable is
  145.    NULL. */
  146.  
  147. const char *save_fname;
  148.  
  149. /* Number of sectors written to the save file. */
  150.  
  151. ULONG save_sector_count;
  152.  
  153. /* Number of elements allocated for save_sector_map. */
  154.  
  155. ULONG save_sector_alloc;
  156.  
  157. /* This table maps logical sector numbers to relative sector numbers
  158.    in the snap shot file, under construction. */
  159.  
  160. ULONG *save_sector_map;
  161.  
  162.  
  163. /* Return the drive letter of a file name, if any, as upper-case
  164.    letter.  Return 0 if there is no drive letter. */
  165.  
  166. static char fname_drive (const char *s)
  167. {
  168.   if (s[0] >= 'A' && s[0] <= 'Z' && s[1] == ':')
  169.     return s[0];
  170.   else if (s[0] >= 'a' && s[0] <= 'z' && s[1] == ':')
  171.     return (char)(s[0]-'a'+'A');
  172.   else
  173.     return 0;
  174. }
  175.  
  176.  
  177. /* Return the currently selected drive as upper-case letter. */
  178.  
  179. static char cur_drive (void)
  180. {
  181.   ULONG drive, map;
  182.  
  183.   if (DosQueryCurrentDisk (&drive, &map) != 0)
  184.     return 0;
  185.   return (char)(drive - 1 + 'A');
  186. }
  187.  
  188.  
  189. /* Obtain access to a disk, snapshot file, or CRC file.  FNAME is the
  190.    name of the disk or file to open.  FLAGS defines what types of
  191.    files are allowed; FLAGS is the inclusive OR of one or more of
  192.    DIO_DISK, DIO_SNAPSHOT, and DIO_CRC.  Open for writing if FOR_WRITE
  193.    is non-zero. */
  194.  
  195. DISKIO *diskio_open (PCSZ fname, unsigned flags, int for_write)
  196. {
  197.   ULONG rc, action, mode, parmlen, datalen, pos, nread, hash, i, ulParm, *map;
  198.   HFILE hf;
  199.   UCHAR data;
  200.   BIOSPARAMETERBLOCK bpb;
  201.   BYTE parm[2];
  202.   int h;
  203.   DISKIO *d;
  204.  
  205.   /* Writing required the -w option.  On the other hand, -w should not
  206.      be used unless writing is requested. */
  207.  
  208.   if (!for_write && write_enable)
  209.     error ("Do not use the -w option for actions that don't write sectors");
  210.   if (for_write && !write_enable)
  211.     error ("Use the -w option for actions that write sectors");
  212.  
  213.   /* Allocate a DISKIO structure. */
  214.  
  215.   d = xmalloc (sizeof (*d));
  216.  
  217.   /* Check for drive letter (direct disk access). */
  218.  
  219.   if (isalpha ((unsigned char)fname[0]) && fname[1] == ':' && fname[2] == 0)
  220.     {
  221.       /* Direct disk access requested.  Check if this is allowed. */
  222.  
  223.       if (!(flags & DIO_DISK))
  224.         error ("A drive name cannot be used for this action");
  225.  
  226.       /* Open a file handle for the logical disk drive. */
  227.  
  228.       if (for_write)
  229.         mode = (OPEN_FLAGS_DASD | OPEN_FLAGS_FAIL_ON_ERROR
  230.                 | OPEN_FLAGS_RANDOM | OPEN_FLAGS_NOINHERIT
  231.                 | OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE);
  232.       else
  233.         mode = (OPEN_FLAGS_DASD | OPEN_FLAGS_FAIL_ON_ERROR
  234.                 | OPEN_FLAGS_RANDOM | OPEN_FLAGS_NOINHERIT
  235.                 | OPEN_SHARE_DENYWRITE | OPEN_ACCESS_READONLY);
  236.       rc = DosOpen (fname, &hf, &action, 0, FILE_NORMAL,
  237.                     OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
  238.                     mode, 0);
  239.       if (rc != 0)
  240.         error ("Cannot open %s (rc=%lu)", fname, rc);
  241.  
  242.       if (!dont_lock)
  243.         {
  244.           /* Attempt to lock the drive. */
  245.  
  246.           parm[0] = 0; parmlen = 1;
  247.           datalen = 1;
  248.           rc = DosDevIOCtl (hf, IOCTL_DISK, DSK_LOCKDRIVE,
  249.                             parm, parmlen, &parmlen,
  250.                             &data, datalen, &datalen);
  251.           if (rc != 0 && (for_write || !ignore_lock_error))
  252.             error ("Cannot lock drive");
  253.           if (rc != 0)
  254.             {
  255.               warning (0, "Cannot lock drive -- proceeding without locking");
  256.               warning_cont (" NOTE: Results are not reliable without locking!");
  257.             }
  258.           else
  259.             {
  260.               /* Issue DSK_REDETERMINEMEDIA to flush the buffers of
  261.                  the file system driver. */
  262.  
  263.               parm[0] = 0; parmlen = 1;
  264.               datalen = 1;
  265.               rc = DosDevIOCtl (hf, IOCTL_DISK, DSK_REDETERMINEMEDIA,
  266.                                 parm, parmlen, &parmlen,
  267.                                 &data, datalen, &datalen);
  268.               if (rc != 0)
  269.                 warning (0, "Cannot flush the buffers of the file system "
  270.                          "driver");
  271.             }
  272.         }
  273.  
  274.       /* Obtain the disk geometry. */
  275.  
  276.       parm[0] = 0x01;       /* Current BPB */
  277.       parm[1] = 0;
  278.       parmlen = sizeof (parm);
  279.       datalen = sizeof (bpb);
  280.       rc = DosDevIOCtl (hf, IOCTL_DISK, DSK_GETDEVICEPARAMS,
  281.                         &parm, parmlen, &parmlen,
  282.                         &bpb, datalen, &datalen);
  283.       if (rc != 0)
  284.         error ("Cannot get device parameters (rc=%lu)", rc);
  285.  
  286.       /* CDROMs have a strange (faked) geometry which we cannot
  287.          handle.  Reject all removable disks. */
  288.  
  289.       if (!removable_allowed && !(bpb.fsDeviceAttr & 1))
  290.         error ("Disk is removable (use the -r option to override)");
  291.  
  292.       /* Compute the total number of sectors (TODO: check for
  293.          overflow). */
  294.  
  295.       d->total_sectors = bpb.usSectorsPerTrack * bpb.cHeads * bpb.cCylinders;
  296.  
  297.       /* Show the BIOS parameter block. */
  298.  
  299.       if (a_info)
  300.         {
  301.           info ("BIOS parameter block:\n");
  302.           info ("  Sectors per track:        %lu\n",
  303.                 (ULONG)bpb.usSectorsPerTrack);
  304.           info ("  Heads:                    %lu\n",
  305.                 (ULONG)bpb.cHeads);
  306.           info ("  Cylinders:                %lu\n",
  307.                 (ULONG)bpb.cCylinders);
  308.           info ("  Total number of sectors:  %lu\n", d->total_sectors);
  309.           info ("  Hidden sectors:           %lu\n", bpb.cHiddenSectors);
  310.         }
  311.  
  312.       /* Adjust the number of sectors and remember the number of
  313.          sectors per track. */
  314.  
  315.       d->total_sectors -= bpb.cHiddenSectors;
  316.       d->spt = bpb.usSectorsPerTrack;
  317.  
  318.       if (diskio_access == ACCESS_LOG_TRACK)
  319.         {
  320.           /* Set up data for accessing tracks of a logical drive. */
  321.  
  322.           d->x.track.hf = hf;
  323.           d->x.track.hidden = bpb.cHiddenSectors;
  324.           d->x.track.spt = bpb.usSectorsPerTrack;
  325.           d->x.track.heads = bpb.cHeads;
  326.  
  327.           /* Build the TRACKLAYOUT structure. */
  328.  
  329.           d->x.track.layout_size =
  330.             sizeof (TRACKLAYOUT)
  331.               + ((bpb.usSectorsPerTrack - 1)
  332.                  * sizeof (d->x.track.playout->TrackTable[0]));
  333.           d->x.track.playout = xmalloc (d->x.track.layout_size);
  334.           for (i = 0; i < bpb.usSectorsPerTrack; ++i)
  335.             {
  336.               d->x.track.playout->TrackTable[i].usSectorNumber = i + 1;
  337.               d->x.track.playout->TrackTable[i].usSectorSize = 512;
  338.             }
  339.  
  340.           /* Allocate the track buffer.  TODO: Avoid memory overflow
  341.              for large values of bpb.usSectorsPerTrack by
  342.              reading/writing tracks partially. */
  343.  
  344.           d->x.track.track_buf = xmalloc (bpb.usSectorsPerTrack * 512);
  345.           d->type = DIOT_DISK_TRACK;
  346.         }
  347.       else
  348.         {
  349.           /* Set up data for accessing the disk with DosRead and
  350.              DosWrite. */
  351.  
  352.           if (d->total_sectors + bpb.cHiddenSectors < LONG_MAX / 512)
  353.             d->x.dasd.sec_mode = FALSE;
  354.           else
  355.             {
  356.               datalen = 0;
  357.               parmlen = sizeof (ulParm);
  358.               ulParm = 0xdeadface;
  359.               rc = DosFSCtl (NULL, datalen, &datalen,
  360.                              (PVOID)&ulParm, parmlen, &parmlen,
  361.                              0x9014, NULL, hf, FSCTL_HANDLE);
  362.               if (rc != 0)
  363.                 error ("Cannot switch handle to sector I/O mode, rc=%lu\n",
  364.                        rc);
  365.               d->x.dasd.sec_mode = TRUE;
  366.             }
  367.           d->x.dasd.hf = hf;
  368.           d->type = DIOT_DISK_DASD;
  369.         }
  370.     }
  371.   else
  372.     {
  373.       header hdr;
  374.  
  375.       /* Reading a regular file requested.  Check if this is
  376.          allowed. */
  377.  
  378.       if (!(flags & (DIO_SNAPSHOT|DIO_CRC)))
  379.         error ("Drive name required");
  380.  
  381.       /* Open the file. */
  382.  
  383.       if (for_write)
  384.         mode = (OPEN_FLAGS_RANDOM | OPEN_FLAGS_NOINHERIT
  385.                 | OPEN_SHARE_DENYWRITE | OPEN_ACCESS_READWRITE);
  386.       else
  387.         mode = (OPEN_FLAGS_RANDOM | OPEN_FLAGS_NOINHERIT
  388.                 | OPEN_SHARE_DENYWRITE | OPEN_ACCESS_READONLY);
  389.       rc = DosOpen (fname, &hf, &action, 0, FILE_NORMAL,
  390.                     OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
  391.                     mode, 0);
  392.       if (rc != 0)
  393.         error ("Cannot open %s (rc=%lu)", fname, rc);
  394.  
  395.       /* Read the header and check the magic number. */
  396.  
  397.       rc = DosRead (hf, &hdr, sizeof (hdr), &nread);
  398.       if (rc != 0)
  399.         error ("Cannot read %s (rc=%lu)", fname, rc);
  400.       if (nread != 512
  401.           || !(((flags & DIO_SNAPSHOT)
  402.                 && ULONG_FROM_FS (hdr.magic) == SNAPSHOT_MAGIC)
  403.                || ((flags & DIO_CRC)
  404.                    && ULONG_FROM_FS (hdr.magic) == CRC_MAGIC)))
  405.         {
  406.           switch (flags & (DIO_SNAPSHOT | DIO_CRC))
  407.             {
  408.             case DIO_SNAPSHOT:
  409.               error ("%s is not a snapshot file", fname);
  410.             case DIO_CRC:
  411.               error ("%s is not a CRC file", fname);
  412.             case DIO_SNAPSHOT|DIO_CRC:
  413.               error ("%s is neither a snapshot file nor a CRC file", fname);
  414.             }
  415.         }
  416.  
  417.       /* Further processing depends on the magic number. */
  418.  
  419.       switch (ULONG_FROM_FS (hdr.magic))
  420.         {
  421.         case SNAPSHOT_MAGIC:
  422.  
  423.           /* Check the header of a snapshot file and remember the
  424.              values of the header. */
  425.  
  426.           if (ULONG_FROM_FS (hdr.s.version) > 1)
  427.             error ("Format of %s too new -- please upgrade this program",
  428.                    fname);
  429.           d->x.snapshot.hf = hf;
  430.           d->x.snapshot.sector_count = ULONG_FROM_FS (hdr.s.sector_count);
  431.           d->x.snapshot.version = ULONG_FROM_FS (hdr.s.version);
  432.           rc = DosSetFilePtr (hf, ULONG_FROM_FS (hdr.s.map_pos),
  433.                               FILE_BEGIN, &pos);
  434.           if (rc != 0)
  435.             error ("Cannot read %s (rc=%lu)", fname, rc);
  436.  
  437.           /* Load the sector map. */
  438.  
  439.           map = xmalloc (d->x.snapshot.sector_count * sizeof (ULONG));
  440.           d->x.snapshot.sector_map = map;
  441.           d->x.snapshot.hash_next = xmalloc (d->x.snapshot.sector_count * sizeof (ULONG));
  442.           rc = DosRead (hf, map, d->x.snapshot.sector_count * sizeof (ULONG),
  443.                         &nread);
  444.           if (rc != 0)
  445.             error ("Cannot read %s (rc=%lu)", fname, rc);
  446.           if (nread != d->x.snapshot.sector_count * sizeof (ULONG))
  447.             error ("Cannot read %s", fname);
  448.  
  449.           for (i = 0; i < d->x.snapshot.sector_count; ++i)
  450.             map[i] = ULONG_FROM_FS (map[i]);
  451.  
  452.           /* Initialize hashing. */
  453.  
  454.           for (i = 0; i < HASH_SIZE; ++i)
  455.             d->x.snapshot.hash_start[i] = HASH_END;
  456.           for (i = 0; i < d->x.snapshot.sector_count; ++i)
  457.             d->x.snapshot.hash_next[i] = HASH_END;
  458.           for (i = 0; i < d->x.snapshot.sector_count; ++i)
  459.             {
  460.               hash = map[i] % HASH_SIZE;
  461.               d->x.snapshot.hash_next[i] = d->x.snapshot.hash_start[hash];
  462.               d->x.snapshot.hash_start[hash] = i;
  463.             }
  464.           d->total_sectors = 0;
  465.           d->type = DIOT_SNAPSHOT;
  466.           break;
  467.  
  468.         case CRC_MAGIC:
  469.  
  470.           /* Check the header of a CRC file and remember the values of
  471.              the header. */
  472.  
  473.           if (ULONG_FROM_FS (hdr.c.version) > 1)
  474.             error ("Format of %s too new -- please upgrade this program",
  475.                    fname);
  476.  
  477.           /* Build a C stream from the file handle. */
  478.  
  479. #ifdef __EMX__
  480.           h = _imphandle ((int)hf);
  481.           if (h == -1)
  482.             error ("%s: %s", (const char *)fname, strerror (errno));
  483. #else
  484.           h = (int)hf;
  485.           if (setmode (h, O_BINARY) == -1)
  486.             error ("%s: %s", (const char *)fname, strerror (errno));
  487. #endif
  488.           d->x.crc.f = fdopen (h, (for_write ? "r+b" : "rb"));
  489.  
  490.           /* Remember values of the header. */
  491.  
  492.           d->total_sectors = ULONG_FROM_FS (hdr.c.sector_count);
  493.           d->x.crc.version = ULONG_FROM_FS (hdr.c.version);
  494.           d->x.crc.vec = NULL;  /* CRCs not read into memory */
  495.  
  496.           /* Seek to the first CRC. */
  497.  
  498.           fseek (d->x.crc.f, 512, SEEK_SET);
  499.           d->type = DIOT_CRC;
  500.           break;
  501.  
  502.         default:
  503.           abort ();
  504.         }
  505.     }
  506.   return d;
  507. }
  508.  
  509.  
  510. /* Close a DISKIO returned by diskio_open(). */
  511.  
  512. void diskio_close (DISKIO *d)
  513. {
  514.   ULONG rc, parmlen, datalen;
  515.   UCHAR parm, data;
  516.  
  517.   switch (d->type)
  518.     {
  519.     case DIOT_DISK_DASD:
  520.       parm = 0; parmlen = 1;
  521.       datalen = 1;
  522.       rc = DosDevIOCtl (d->x.dasd.hf, IOCTL_DISK, DSK_LOCKDRIVE,
  523.                         &parm, sizeof (parm), &parmlen,
  524.                         &data, sizeof (data), &datalen);
  525.       rc = DosClose (d->x.dasd.hf);
  526.       break;
  527.     case DIOT_DISK_TRACK:
  528.       parm = 0; parmlen = 1;
  529.       datalen = 1;
  530.       rc = DosDevIOCtl (d->x.track.hf, IOCTL_DISK, DSK_LOCKDRIVE,
  531.                         &parm, sizeof (parm), &parmlen,
  532.                         &data, sizeof (data), &datalen);
  533.       rc = DosClose (d->x.track.hf);
  534.       free (d->x.track.track_buf);
  535.       free (d->x.track.playout);
  536.       break;
  537.     case DIOT_SNAPSHOT:
  538.       rc = DosClose (d->x.snapshot.hf);
  539.       free (d->x.snapshot.sector_map);
  540.       free (d->x.snapshot.hash_next);
  541.       break;
  542.     case DIOT_CRC:
  543.       if (fclose (d->x.crc.f) != 0)
  544.         error ("fclose(): %s", strerror (errno));
  545.       rc = 0;
  546.       break;
  547.     default:
  548.       abort ();
  549.     }
  550.   if (rc != 0)
  551.     error ("disk_close failed, rc=%lu", rc);
  552.   free (d);
  553. }
  554.  
  555.  
  556. /* Return the type of a DISKIO. */
  557.  
  558. unsigned diskio_type (DISKIO *d)
  559. {
  560.   switch (d->type)
  561.     {
  562.     case DIOT_DISK_DASD:
  563.     case DIOT_DISK_TRACK:
  564.       return DIO_DISK;
  565.     case DIOT_SNAPSHOT:
  566.       return DIO_SNAPSHOT;
  567.     case DIOT_CRC:
  568.       return DIO_CRC;
  569.     default:
  570.       abort ();
  571.     }
  572. }
  573.  
  574.  
  575. /* Return the number of sectors covered by a DISKIO. */
  576.  
  577. ULONG diskio_total_sectors (DISKIO *d)
  578. {
  579.   return d->total_sectors;
  580. }
  581.  
  582.  
  583. /* Return the number of snapshot sectors. */
  584.  
  585. ULONG diskio_snapshot_sectors (DISKIO *d)
  586. {
  587.   if (d->type != DIOT_SNAPSHOT)
  588.     abort ();
  589.   return d->x.snapshot.sector_count;
  590. }
  591.  
  592.  
  593. /* Compare two sector numbers, for qsort(). */
  594.  
  595. static int snapshot_sort_comp (const void *x1, const void *x2)
  596. {
  597.   const ULONG *p1 = (const ULONG *)x1;
  598.   const ULONG *p2 = (const ULONG *)x2;
  599.   if (*p1 < *p2)
  600.     return -1;
  601.   else if (*p1 > *p2)
  602.     return 1;
  603.   else
  604.     return 0;
  605. }
  606.  
  607.  
  608. /* Return a sorted array of all sector numbers of a snapshot file.  If
  609.    DISKIO is not associated with a snapshot file, return NULL. */
  610.  
  611. ULONG *diskio_snapshot_sort (DISKIO *d)
  612. {
  613.   ULONG *p;
  614.   size_t n;
  615.  
  616.   if (diskio_type (d) != DIO_SNAPSHOT)
  617.     return NULL;
  618.   n = (size_t)d->x.snapshot.sector_count;
  619.   p = xmalloc (n * sizeof (ULONG));
  620.   memcpy (p, d->x.snapshot.sector_map, n * sizeof (ULONG));
  621.   qsort (p, n, sizeof (ULONG), snapshot_sort_comp);
  622.   return p;
  623. }
  624.  
  625.  
  626. /* Read all CRCs of the CRC file associated with D into memory, unless
  627.    there are too many CRCs.  This is used for speeding up
  628.    processing. */
  629.  
  630. void diskio_crc_load (DISKIO *d)
  631. {
  632.   ULONG i;
  633.  
  634.   if (d->type != DIOT_CRC || d->x.crc.vec != NULL)
  635.     abort ();
  636.   if (d->total_sectors * sizeof (crc_t) >= 8*1024*1024)
  637.     return;
  638.   d->x.crc.vec = xmalloc (d->total_sectors * sizeof (crc_t));
  639.   fseek (d->x.crc.f, 512, SEEK_SET);
  640.   if (fread (d->x.crc.vec, sizeof (crc_t), d->total_sectors,
  641.              d->x.crc.f) != d->total_sectors)
  642.     error ("Cannot read CRC file");
  643.   for (i = 0; i < d->total_sectors; ++i)
  644.     d->x.crc.vec[i] = ULONG_FROM_FS (d->x.crc.vec[i]);
  645. }
  646.  
  647.  
  648. /* Write the sector with number SEC and data SRC to the save file. */
  649.  
  650. static void save_one_sec (const void *src, ULONG sec)
  651. {
  652.   ULONG i;
  653.   BYTE raw[512];
  654.  
  655.   for (i = 0; i < save_sector_count; ++i)
  656.     if (save_sector_map[i] == sec)
  657.       return;
  658.   if (save_sector_count >= save_sector_alloc)
  659.     {
  660.       save_sector_alloc += 1024;
  661.       save_sector_map = realloc (save_sector_map,
  662.                                  save_sector_alloc * sizeof (ULONG));
  663.       if (save_sector_map == NULL)
  664.         error ("Out of memory");
  665.     }
  666.   save_sector_map[save_sector_count++] = sec;
  667.   memcpy (raw, src, 512);
  668.  
  669.   /* Scramble the signature so that there are no sectors with the
  670.      original HPFS sector signatures.  This simplifies recovering HPFS
  671.      file systems and undeleting files. */
  672.  
  673.   *(ULONG *)raw ^= ULONG_TO_FS (SNAPSHOT_SCRAMBLE);
  674.   if (fwrite (raw, 512, 1, save_file) != 1)
  675.     save_error ();
  676. }
  677.  
  678.  
  679. /* Write COUNT sectors starting at number SEC to the save file. */
  680.  
  681. void save_sec (const void *src, ULONG sec, ULONG count)
  682. {
  683.   const char *p;
  684.  
  685.   p = (const char *)src;
  686.   while (count != 0)
  687.     {
  688.       save_one_sec (p, sec);
  689.       p += 512; ++sec; --count;
  690.     }
  691. }
  692.  
  693.  
  694. /* Create a save file of type TYPE.  The file name is passed in the
  695.    global variable `save_fname'.  Complain if the file would be on the
  696.    drive AVOID_FNAME. */
  697.  
  698. void save_create (const char *avoid_fname, enum save_type type)
  699. {
  700.   char drive;
  701.   header hdr;
  702.  
  703.   if (isalpha ((unsigned char)avoid_fname[0]) && avoid_fname[1] == ':'
  704.       && avoid_fname[2] == 0)
  705.     {
  706.       drive = fname_drive (save_fname);
  707.       if (drive == 0)
  708.         drive = cur_drive ();
  709.       if (toupper (drive) == toupper (avoid_fname[0]))
  710.         error ("The target file must not be on the source or target drive");
  711.     }
  712.   save_file = fopen (save_fname, "wb");
  713.   if (save_file == NULL)
  714.     save_error ();
  715.   save_type = type;
  716.   switch (save_type)
  717.     {
  718.     case SAVE_SNAPSHOT:
  719.       save_sector_count = 0;
  720.       save_sector_alloc = 0;
  721.       save_sector_map = NULL;
  722.       memset (&hdr, 0, sizeof (hdr));
  723.       fwrite (&hdr, sizeof (hdr), 1, save_file);
  724.       break;
  725.  
  726.     case SAVE_CRC:
  727.       save_sector_count = 0;
  728.       memset (&hdr, 0, sizeof (hdr));
  729.       fwrite (&hdr, sizeof (hdr), 1, save_file);
  730.       break;
  731.  
  732.     default:
  733.       break;
  734.     }
  735. }
  736.  
  737.  
  738. /* Error while writing to the save file. */
  739.  
  740. void save_error (void)
  741. {
  742.   error ("%s: %s", save_fname, strerror (errno));
  743. }
  744.  
  745.  
  746. /* Close the save file and update the header. */
  747.  
  748. void save_close (void)
  749. {
  750.   header hdr;
  751.   ULONG i;
  752.  
  753.   switch (save_type)
  754.     {
  755.     case SAVE_SNAPSHOT:
  756.       memset (&hdr, 0, sizeof (hdr));
  757.       hdr.s.magic = ULONG_TO_FS (SNAPSHOT_MAGIC);
  758.       hdr.s.sector_count = ULONG_TO_FS (save_sector_count);
  759.       hdr.s.map_pos = ULONG_TO_FS (ftell (save_file));
  760.       hdr.s.version = ULONG_TO_FS (1); /* Scrambled */
  761.       for (i = 0; i < save_sector_count; ++i)
  762.         save_sector_map[i] = ULONG_TO_FS (save_sector_map[i]);
  763.       if (fwrite (save_sector_map, sizeof (ULONG), save_sector_count,
  764.                   save_file)
  765.           != save_sector_count || fseek (save_file, 0L, SEEK_SET) != 0)
  766.         save_error ();
  767.       fwrite (&hdr, sizeof (hdr), 1, save_file);
  768.       for (i = 0; i < save_sector_count; ++i)
  769.         save_sector_map[i] = ULONG_FROM_FS (save_sector_map[i]);
  770.       break;
  771.  
  772.     case SAVE_CRC:
  773.       memset (&hdr, 0, sizeof (hdr));
  774.       hdr.c.magic = ULONG_TO_FS (CRC_MAGIC);
  775.       hdr.c.sector_count = ULONG_TO_FS (save_sector_count);
  776.       hdr.c.version = ULONG_TO_FS (1);
  777.       if (fseek (save_file, 0L, SEEK_SET) != 0)
  778.         save_error ();
  779.       fwrite (&hdr, sizeof (hdr), 1, save_file);
  780.       break;
  781.  
  782.     default:
  783.       break;
  784.     }
  785.  
  786.   if (fclose (save_file) != 0)
  787.     save_error ();
  788.   save_file = NULL;
  789. }
  790.  
  791.  
  792. /* Convert the logical sector number SECNO to cylinder numbe
  793.    (0-based), head number (0-based), and sector number (1-based).
  794.    Return TRUE iff successful. */
  795.  
  796. int diskio_cyl_head_sec (DISKIO *d, cyl_head_sec *dst, ULONG secno)
  797. {
  798.   if (d->type != DIOT_DISK_TRACK)
  799.     return FALSE;
  800.  
  801.   secno += d->x.track.hidden;
  802.   dst->sec = secno % d->x.track.spt + 1; secno /= d->x.track.spt;
  803.   dst->head = secno % d->x.track.heads; secno /= d->x.track.heads;
  804.   dst->cyl = secno;
  805.   return TRUE;
  806. }
  807.  
  808.  
  809. /* Return the relative sector number of sector N in the snapshot file
  810.    associated with D.  Return 0 if there is no such sector (relative
  811.    sector number 0 is the header of the snapshot file). */
  812.  
  813. ULONG find_sec_in_snapshot (DISKIO *d, ULONG n)
  814. {
  815.   ULONG j;
  816.  
  817.   for (j = d->x.snapshot.hash_start[n % HASH_SIZE]; j != HASH_END;
  818.        j = d->x.snapshot.hash_next[j])
  819.     if (d->x.snapshot.sector_map[j] == n)
  820.       return j + 1;
  821.   return 0;
  822. }
  823.  
  824.  
  825. /* Seek to sector SEC in file HF. */
  826.  
  827. static void seek_sec_hfile (HFILE hf, int sec_io, ULONG sec)
  828. {
  829.   ULONG rc, act_pos, n, rest;
  830.   LONG new_pos;
  831.  
  832.   new_pos = (sec_io ? sec : sec * 512);
  833.   if (new_pos >= 0)
  834.     rc = DosSetFilePtr (hf, new_pos, FILE_BEGIN, &act_pos);
  835.   else
  836.     {
  837.       rc = DosSetFilePtr (hf, 0, FILE_BEGIN, &act_pos);
  838.       rest = sec;
  839.       while (rc == 0 && rest != 0)
  840.         {
  841.           n = MIN (rest, LONG_MAX / 512);
  842.           new_pos = n * 512;
  843.           rc = DosSetFilePtr (hf, new_pos, FILE_CURRENT, &act_pos);
  844.           rest -= n;
  845.         }
  846.     }
  847.   if (rc != 0)
  848.     error ("Cannot seek to sector #%lu (rc=%lu)", sec, rc);
  849. }
  850.  
  851.  
  852. /* Read COUNT sectors from HF. */
  853.  
  854. static void read_sec_hfile (HFILE hf, int sec_io, void *dst,
  855.                             ULONG sec, ULONG count)
  856. {
  857.   ULONG rc, n, i;
  858.  
  859.   i = (sec_io ? count : 512 * count);
  860.   seek_sec_hfile (hf, sec_io, sec);
  861.   rc = DosRead (hf, dst, i, &n);
  862.   if (rc != 0)
  863.     error ("Cannot read sector #%lu (rc=%lu)", sec, rc);
  864.   if (n != i)
  865.     error ("EOF reached while reading sector #%lu", sec);
  866. }
  867.  
  868.  
  869. /* Read COUNT sectors using DSK_READTRACK. */
  870.  
  871. static void read_sec_track (HFILE hf, struct diskio_track *dt, void *dst,
  872.                             ULONG sec, ULONG count)
  873. {
  874.   ULONG rc, parmlen, datalen, temp;
  875.   TRACKLAYOUT *ptl;
  876.   char *p;
  877.  
  878.   /* Note: Reading an entire track (dt->spt sectors) slows down
  879.      things even when caching one track, probably due to mapping done
  880.      by the hard disk controller. */
  881.  
  882.   p = dst;
  883.   while (count != 0)
  884.     {
  885.       temp = sec + dt->hidden;
  886.       ptl = dt->playout;
  887.       ptl->bCommand = 0x01;     /* Consecutive sectors */
  888.       ptl->usFirstSector = temp % dt->spt; temp /= dt->spt;
  889.       ptl->usHead = temp % dt->heads; temp /= dt->heads;
  890.       ptl->usCylinder = temp;
  891.       temp = dt->spt - ptl->usFirstSector;
  892.       if (temp > count)
  893.         temp = count;
  894.       ptl->cSectors = temp;
  895.       parmlen = dt->layout_size;
  896.       datalen = ptl->cSectors * 512;
  897.       rc = DosDevIOCtl (hf, IOCTL_DISK, DSK_READTRACK,
  898.                         ptl, parmlen, &parmlen,
  899.                         dt->track_buf, datalen, &datalen);
  900.       if (rc != 0)
  901.         error ("Cannot read sector #%lu (rc=%lu)", sec, rc);
  902.       memcpy (p, dt->track_buf, temp * 512);
  903.       sec += temp; p += temp * 512; count -= temp;
  904.     }
  905. }
  906.  
  907.  
  908. /* Read COUNT sectors from D to DST.  SEC is the starting sector
  909.    number.  Copy the sector to the save file if SAVE is non-zero. */
  910.  
  911. void read_sec (DISKIO *d, void *dst, ULONG sec, ULONG count, int save)
  912. {
  913.   ULONG i, j, n;
  914.   char *p;
  915.  
  916.   switch (d->type)
  917.     {
  918.     case DIOT_DISK_DASD:
  919.       read_sec_hfile (d->x.dasd.hf, d->x.dasd.sec_mode, dst, sec, count);
  920.       break;
  921.     case DIOT_DISK_TRACK:
  922.       read_sec_track (d->x.track.hf, &d->x.track, dst, sec, count);
  923.       break;
  924.     case DIOT_SNAPSHOT:
  925.       p = (char *)dst; n = sec;
  926.       for (i = 0; i < count; ++i)
  927.         {
  928.           j = find_sec_in_snapshot (d, n);
  929.           if (j == 0)
  930.             error ("Sector #%lu not found in snapshot file", n);
  931.           read_sec_hfile (d->x.snapshot.hf, FALSE, p, j, 1);
  932.           if (d->x.snapshot.version >= 1)
  933.             *(ULONG *)p ^= ULONG_TO_FS (SNAPSHOT_SCRAMBLE);
  934.           p += 512; ++n;
  935.         }
  936.       break;
  937.     default:
  938.       abort ();
  939.     }
  940.   if (a_save && save)
  941.     save_sec (dst, sec, count);
  942. }
  943.  
  944.  
  945. /* Store the CRC of sector SECNO to the object pointed to by PCRC. */
  946.  
  947. int crc_sec (DISKIO *d, crc_t *pcrc, ULONG secno)
  948. {
  949.   if (d->type == DIOT_CRC)
  950.     {
  951.       if (secno >= d->total_sectors)
  952.         return FALSE;
  953.       if (d->x.crc.vec != NULL)
  954.         {
  955.           *pcrc = d->x.crc.vec[secno];
  956.           return TRUE;
  957.         }
  958.       fseek (d->x.crc.f, 512 + secno * sizeof (crc_t), SEEK_SET);
  959.       if (fread (pcrc, sizeof (crc_t), 1, d->x.crc.f) != 1)
  960.         error ("CRC file: %s", strerror (errno));
  961.       *pcrc = ULONG_FROM_FS (*pcrc);
  962.       return TRUE;
  963.     }
  964.   else
  965.     {
  966.       BYTE data[512];
  967.  
  968.       read_sec (d, data, secno, 1, FALSE);
  969.       *pcrc = crc_compute (data, 512);
  970.       return TRUE;
  971.     }
  972. }
  973.  
  974.  
  975. /* Write sector SEC to HF. */
  976.  
  977. static int write_sec_hfile (HFILE hf, int sec_io, const void *src, ULONG sec)
  978. {
  979.   ULONG rc, n, i;
  980.  
  981.   i = (sec_io ? 1 : 512);
  982.   seek_sec_hfile (hf, sec_io, sec);
  983.   rc = DosWrite (hf, src, i, &n);
  984.   if (rc != 0)
  985.     {
  986.       warning (1, "Cannot write sector #%lu (rc=%lu)", sec, rc);
  987.       return FALSE;
  988.     }
  989.   if (n != i)
  990.     {
  991.       warning (1, "Incomplete write for sector #%lu", sec);
  992.       return FALSE;
  993.     }
  994.   return TRUE;
  995. }
  996.  
  997.  
  998. /* Write COUNT sectors to HF, using DSK_WRITETRACK.  DT points to a
  999.    structure containing the disk geometry. */
  1000.  
  1001. static int write_sec_track (HFILE hf, struct diskio_track *dt,
  1002.                             const void *src, ULONG sec, ULONG count)
  1003. {
  1004.   ULONG rc, parmlen, datalen, temp;
  1005.   TRACKLAYOUT *ptl;
  1006.   const char *p;
  1007.  
  1008.   p = src;
  1009.   while (count != 0)
  1010.     {
  1011.       temp = sec + dt->hidden;
  1012.       ptl = dt->playout;
  1013.       ptl->bCommand = 0x01;     /* Consecutive sectors */
  1014.       ptl->usFirstSector = temp % dt->spt; temp /= dt->spt;
  1015.       ptl->usHead = temp % dt->heads; temp /= dt->heads;
  1016.       ptl->usCylinder = temp;
  1017.       temp = dt->spt - ptl->usFirstSector;
  1018.       if (temp > count)
  1019.         temp = count;
  1020.       ptl->cSectors = temp;
  1021.       parmlen = dt->layout_size;
  1022.       datalen = ptl->cSectors * 512;
  1023.       memcpy (dt->track_buf, p, temp * 512);
  1024.       rc = DosDevIOCtl (hf, IOCTL_DISK, DSK_WRITETRACK,
  1025.                         ptl, parmlen, &parmlen,
  1026.                         dt->track_buf, datalen, &datalen);
  1027.       if (rc != 0)
  1028.         {
  1029.           warning (1, "Cannot write sector #%lu (rc=%lu)", sec, rc);
  1030.           return FALSE;
  1031.         }
  1032.       sec += temp; p += temp * 512; count -= temp;
  1033.     }
  1034.   return TRUE;
  1035. }
  1036.  
  1037.  
  1038. /* Replace the sector SEC in the snapshot file associated with D.
  1039.    Return FALSE on failure. */
  1040.  
  1041. static int write_sec_snapshot (DISKIO *d, const void *src, ULONG sec)
  1042. {
  1043.   BYTE raw[512];
  1044.   ULONG j;
  1045.  
  1046.   j = find_sec_in_snapshot (d, sec);
  1047.   if (j == 0)
  1048.     {
  1049.       warning (1, "Sector #%lu not found in snapshot file", sec);
  1050.       return FALSE;
  1051.     }
  1052.  
  1053.   memcpy (raw, src, 512);
  1054.   /* Scramble the signature so that there are no sectors with the
  1055.      original HPFS sector signatures.  This simplifies recovering HPFS
  1056.      file systems and undeleting files. */
  1057.   if (d->x.snapshot.version >= 1)
  1058.     *(ULONG *)raw ^= ULONG_TO_FS (SNAPSHOT_SCRAMBLE);
  1059.  
  1060.   return write_sec_hfile (d->x.snapshot.hf, FALSE, raw, j);
  1061. }
  1062.  
  1063.  
  1064. /* Write sector SRC to D. */
  1065.  
  1066. int write_sec (DISKIO *d, const void *src, ULONG sec)
  1067. {
  1068.   switch (d->type)
  1069.     {
  1070.     case DIOT_DISK_DASD:
  1071.       return write_sec_hfile (d->x.dasd.hf, d->x.dasd.sec_mode, src, sec);
  1072.     case DIOT_DISK_TRACK:
  1073.       return write_sec_track (d->x.track.hf, &d->x.track, src, sec, 1);
  1074.     case DIOT_SNAPSHOT:
  1075.       return write_sec_snapshot (d, src, sec);
  1076.     default:
  1077.       abort ();
  1078.     }
  1079.   return FALSE;
  1080. }
  1081.