home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 10 / mycd10.iso / share / os2 / utilidad / fst03e / do_fat.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-16  |  42.2 KB  |  1,404 lines

  1. /* do_fat.c -- FAT-specific code 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. /* TODO: Don't assume a sector size of 512 bytes. */
  23.  
  24. #include <os2.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <stdarg.h>
  28. #include <stddef.h>
  29. #include <string.h>
  30. #include "fst.h"
  31. #include "crc.h"
  32. #include "diskio.h"
  33. #include "fat.h"
  34.  
  35.  
  36. struct vfat
  37. {
  38.   char flag;
  39.   char unprintable;
  40.   BYTE total;
  41.   BYTE index;
  42.   BYTE checksum;
  43.   int start;
  44.   BYTE name[256+1];
  45. };
  46.  
  47. static ULONG first_sector;
  48. static ULONG total_sectors;
  49. static ULONG total_clusters;
  50. static ULONG sectors_per_cluster;
  51. static ULONG bytes_per_cluster;
  52. static ULONG sectors_per_fat;
  53. static ULONG number_of_fats;
  54. static ULONG root_entries;
  55. static ULONG root_sectors;
  56. static ULONG data_sector;
  57. static ULONG what_cluster;
  58. static USHORT **fats;
  59. static USHORT *fat;
  60.  
  61. static BYTE *usage_vector;      /* One byte per cluster, indicating usage */
  62. static const path_chain **path_vector; /* One path name chain per sector */
  63.  
  64. static BYTE find_comp[256];     /* Current component of `find_path' */
  65.  
  66. static char ea_ok;
  67. static ULONG ea_data_start;
  68. static ULONG ea_data_size;
  69. static ULONG ea_data_clusters;
  70. static USHORT ea_table1[240];   /* First table from `EA DATA. SF' */
  71. static USHORT *ea_table2;       /* Second table from `EA DATA. SF' */
  72. static ULONG ea_table2_entries;
  73. static BYTE *ea_usage;          /* One byte per cluster of `EA DATA. SF' */
  74.  
  75. #define CLUSTER_TO_SECTOR(c) (((c) - 2) * sectors_per_cluster + data_sector)
  76. #define SECTOR_TO_CLUSTER(s) (((s) - data_sector) / sectors_per_cluster + 2)
  77.  
  78.  
  79. /* Rotate right a byte by one bit. */
  80.  
  81. static INLINE BYTE rorb1 (BYTE b)
  82. {
  83.   return (b & 1) ? (b >> 1) | 0x80 : b >> 1;
  84. }
  85.  
  86.  
  87. /* Compare two file names pointed to by P1 and P2. */
  88.  
  89. static int compare_fname (const BYTE *p1, const BYTE *p2)
  90. {
  91.   for (;;)
  92.     {
  93.       if (*p1 == 0 && *p2 == 0)
  94.         return 0;
  95.       if (*p2 == 0)
  96.         return 1;
  97.       if (*p1 == 0)
  98.         return -1;
  99.       if (cur_case_map[*p1] > cur_case_map[*p2])
  100.         return 1;
  101.       if (cur_case_map[*p1] < cur_case_map[*p2])
  102.         return -1;
  103.       ++p1; ++p2;
  104.     }
  105. }
  106.  
  107.  
  108. /* Return a pointer to a string containing a formatted range of
  109.    cluster numbers.  Note that the pointer points to static memory; do
  110.    not use format_cluster_range() more than once in one expression! */
  111.  
  112. static const char *format_cluster_range (ULONG start, ULONG count)
  113. {
  114.   static char buf[60];
  115.  
  116.   if (count == 1)
  117.     sprintf (buf, "cluster %lu", start);
  118.   else
  119.     sprintf (buf, "%lu clusters %lu-%lu", count, start, start + count - 1);
  120.   return buf;
  121. }
  122.  
  123.  
  124. /* Return a pointer to a string containing a file time as string.
  125.    Note that the pointer points to static memory; do not use
  126.    format_time() more than once in one expression! */
  127.  
  128. static const char *format_time (unsigned t)
  129. {
  130.   static char buf[20];
  131.  
  132.   sprintf (buf, "%.2d:%.2d:%.2d",
  133.            (t >> 11) & 31, (t >> 5) & 63, (t & 31) << 1);
  134.   return buf;
  135. }
  136.  
  137.  
  138. /* Return a pointer to a string containing a file date as string.
  139.    Note that the pointer points to static memory; do not use
  140.    format_date() more than once in one expression! */
  141.  
  142. static const char *format_date (unsigned d)
  143. {
  144.   static char buf[20];
  145.  
  146.   sprintf (buf, "%d-%.2d-%.2d",
  147.            ((d >> 9) & 127) + 1980, (d >> 5) & 15, d & 31);
  148.   return buf;
  149. }
  150.  
  151.  
  152. /* Return the number of days in month M of year Y. */
  153.  
  154. static int days (int y, int m)
  155. {
  156.   static int month_len[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  157.  
  158.   if (m < 1 || m > 12)
  159.     return 0;
  160.   else if (m != 2)
  161.     return month_len[m-1];
  162.   else
  163.     return y % 4 != 0 ? 28 : y % 100 != 0 ? 29 : y % 400 != 0 ? 28 : 29;
  164. }
  165.  
  166.  
  167. /* Types of clusters. */
  168.  
  169. #define USE_EMPTY       0
  170. #define USE_FILE        1
  171. #define USE_DIR         2
  172.  
  173.  
  174. /* Return a pointer to a string containing a description of a cluster
  175.    type. */
  176.  
  177. static const char *cluster_usage (BYTE what)
  178. {
  179.   switch (what)
  180.     {
  181.     case USE_EMPTY:
  182.       return "empty";
  183.     case USE_DIR:
  184.       return "directory";
  185.     case USE_FILE:
  186.       return "file";
  187.     default:
  188.       return "INTERNAL_ERROR";
  189.     }
  190. }
  191.  
  192.  
  193. /* Use cluster CLUSTER for WHAT.  PATH points to a string naming the
  194.    object for which the cluster is used.  PATH points to the path name
  195.    chain for the file or directory.  Return FALSE if a cycle has been
  196.    detected. */
  197.  
  198. static int use_cluster (ULONG cluster, BYTE what, const path_chain *path)
  199. {
  200.   BYTE old;
  201.  
  202.   if (cluster >= total_clusters)
  203.     abort ();
  204.   old = usage_vector[cluster];
  205.   if (old != USE_EMPTY)
  206.     {
  207.       warning (1, "Cluster %lu usage conflict: %s vs. %s",
  208.                cluster, cluster_usage (usage_vector[cluster]),
  209.                cluster_usage (what));
  210.       if (path_vector != NULL && path_vector[cluster] != NULL)
  211.         warning_cont ("File 1: \"%s\"",
  212.                       format_path_chain (path_vector[cluster], NULL));
  213.       if (path != NULL)
  214.         warning_cont ("File 2: \"%s\"",
  215.                       format_path_chain (path, NULL));
  216.       return !(path != NULL && path == path_vector[cluster]);
  217.     }
  218.   else
  219.     {
  220.       usage_vector[cluster] = what;
  221.       if (path_vector != NULL)
  222.         path_vector[cluster] = path;
  223.       return TRUE;
  224.     }
  225. }
  226.  
  227.  
  228. static void dirent_warning (int level, const char *fmt, ULONG secno,
  229.                             const path_chain *path,
  230.                             const BYTE *name, ...) ATTR_PRINTF (2, 6);
  231.  
  232.  
  233. static USHORT *read_fat16 (DISKIO *d, ULONG secno)
  234. {
  235.   USHORT *fat;
  236.   ULONG sectors, clusters, i;
  237.  
  238.   clusters = total_clusters;
  239.   sectors = DIVIDE_UP (clusters * 2, 512);
  240.   if (sectors != sectors_per_fat)
  241.     warning (1, "Incorrect FAT size: %lu vs. %lu", sectors, sectors_per_fat);
  242.   fat = xmalloc (sectors * 512);
  243.   read_sec (d, fat, secno, sectors, TRUE);
  244.   for (i = 0; i < clusters; ++i)
  245.     fat[i] = USHORT_FROM_FS (fat[i]);
  246.   return fat;
  247. }
  248.  
  249.  
  250. static USHORT *read_fat12 (DISKIO *d, ULONG secno)
  251. {
  252.   USHORT *fat;
  253.   ULONG clusters, sectors, i, s, t;
  254.   USHORT c1, c2;
  255.   BYTE *raw;
  256.  
  257.   clusters = total_clusters;
  258.   sectors = DIVIDE_UP (clusters * 3, 512*2);
  259.   if (sectors != sectors_per_fat)
  260.     warning (1, "Incorrect FAT size: %lu vs. %lu", sectors, sectors_per_fat);
  261.   raw = xmalloc (sectors * 512 + 2);
  262.   read_sec (d, raw, secno, sectors, TRUE);
  263.   fat = xmalloc (clusters * 2 + 1);
  264.   s = 0;
  265.   for (i = 0; i < clusters; i += 2)
  266.     {
  267.       t = raw[s+0] | (raw[s+1] << 8) | (raw[s+2] << 16);
  268.       c1 = t & 0xfff;
  269.       if (c1 >= 0xff7)
  270.         c1 |= 0xf000;
  271.       c2 = (t >> 12) & 0xfff;
  272.       if (c2 >= 0xff7)
  273.         c2 |= 0xf000;
  274.       fat[i+0] = c1;
  275.       fat[i+1] = c2;
  276.       s += 3;
  277.     }
  278.   free (raw);
  279.   return fat;
  280. }
  281.  
  282.  
  283. static USHORT *read_fat (DISKIO *d, ULONG secno, ULONG fatno)
  284. {
  285.   if (a_what)
  286.     {
  287.       if (!what_cluster_flag && IN_RANGE (what_sector, secno, sectors_per_fat))
  288.         info ("Sector #%lu: Fat %lu (+%lu)\n", what_sector, fatno + 1,
  289.               what_sector - secno);
  290.     }
  291.   if (total_clusters - 2 > 4085)
  292.     return read_fat16 (d, secno);
  293.   else
  294.     return read_fat12 (d, secno);
  295. }
  296.  
  297.  
  298. static void do_fats (DISKIO *d)
  299. {
  300.   ULONG secno, i, j, k, free, bad;
  301.  
  302.   fats = xmalloc (number_of_fats * sizeof (*fats));
  303.   secno = first_sector;
  304.   for (i = 0; i < number_of_fats; ++i)
  305.     {
  306.       if (a_info)
  307.         info ("FAT %lu:                      %s\n",
  308.               i + 1, format_sector_range (secno, sectors_per_fat));
  309.       fats[i] = read_fat (d, secno, i);
  310.       secno += sectors_per_fat;
  311.     }
  312.   for (i = 0; i < number_of_fats; ++i)
  313.     for (j = i + 1; j < number_of_fats; ++j)
  314.       if (memcmp (fats[i], fats[j], total_clusters * sizeof (USHORT)) != 0)
  315.         {
  316.           warning (1, "FATs %lu and %lu differ", i, j);
  317.           list_start ("Differing clusters:");
  318.           for (k = 0; k < total_clusters; ++k)
  319.             if (fats[i][k] != fats[j][k])
  320.               list ("%lu", k);
  321.           list_end ();
  322.         }
  323.   fat = fats[0];
  324.   free = bad = 0;
  325.   for (i = 2; i < total_clusters; ++i)
  326.     if (fat[i] == 0)
  327.       ++free;
  328.     else if (fat[i] == 0xfff7)
  329.       ++bad;
  330.   if (a_info)
  331.     {
  332.       info ("Number of free clusters:    %lu\n", free);
  333.       info ("Number of bad clusters:     %lu\n", bad);
  334.     }
  335. }
  336.  
  337.  
  338. static void read_ea_data (DISKIO *d)
  339. {
  340.   FAT_SECTOR ea1;
  341.   ULONG i, min_cluster, cluster, pos, size2;
  342.   char *tab2, *cluster_buffer;
  343.  
  344.   if (ea_data_start == 0xffff)
  345.     return;
  346.   if (ea_data_start < 2 || ea_data_start >= total_clusters)
  347.     {
  348.       warning (1, "\"EA DATA. SF\": Start cluster (%lu) is invalid",
  349.                ea_data_start);
  350.       return;
  351.     }
  352.  
  353.   /* Read the first table and copy it to `ea_table1'. */
  354.  
  355.   read_sec (d, &ea1, CLUSTER_TO_SECTOR (ea_data_start), 1, FALSE);
  356.   if (memcmp (ea1.ea1.magic, "ED", 2) != 0)
  357.     {
  358.       warning (1, "\"EA DATA. SF\": Incorrect signature");
  359.       return;
  360.     }
  361.   memcpy (ea_table1, ea1.ea1.table, sizeof (ea_table1));
  362.  
  363.   /* Find the smallest cluster number referenced by the first table.
  364.      This gives us the maximum size of the second table. */
  365.  
  366.   min_cluster = ea_table1[0];
  367.   for (i = 1; i < 240; ++i)
  368.     if (ea_table1[i] < min_cluster)
  369.       min_cluster = ea_table1[i];
  370.  
  371.   if (min_cluster < 1)
  372.     {
  373.       warning (1, "\"EA DATA. SF\": First table contains a zero entry");
  374.       return;
  375.     }
  376.   if (min_cluster >= total_clusters)
  377.     {
  378.       warning (1, "\"EA DATA. SF\": Second table is too big (%lu clusters)",
  379.                min_cluster);
  380.       return;
  381.     }
  382.  
  383.   /* We make the table too big by one sector to simplify reading the
  384.      table. */
  385.  
  386.   size2 = min_cluster * bytes_per_cluster;
  387.   if (size2 > ea_data_size)
  388.     {
  389.       warning (1, "\"EA DATA. SF\": Beyond end of file");
  390.       return;
  391.     }
  392.   tab2 = xmalloc (size2);
  393.   cluster_buffer = xmalloc (bytes_per_cluster);
  394.  
  395.   cluster = ea_data_start;
  396.   for (pos = 0; pos < ea_data_size; pos += bytes_per_cluster)
  397.     {
  398.       if (cluster < 2 || cluster >= total_clusters)
  399.         {
  400.           warning (1, "\"EA DATA. SF\": Invalid FAT chain");
  401.           free (cluster_buffer);
  402.           return;
  403.         }
  404.       read_sec (d, cluster_buffer, CLUSTER_TO_SECTOR (cluster),
  405.                 sectors_per_cluster, TRUE);
  406.       if (pos < size2)
  407.         memcpy (tab2 + pos, cluster_buffer, 
  408.                 MIN (bytes_per_cluster, size2 - pos));
  409.       cluster = fat[cluster];
  410.     }
  411.   free (cluster_buffer);
  412.  
  413.   /* Skip the first 512 bytes (table 1). */
  414.  
  415.   ea_table2 = (USHORT *)(tab2 + 512);
  416.   ea_table2_entries = (size2 - 512) / 2;
  417.  
  418.   ea_usage = xmalloc (ea_data_clusters);
  419.   memset (ea_usage, FALSE, ea_data_clusters);
  420.   ea_ok = TRUE;
  421. }
  422.  
  423.  
  424. static void do_ea (DISKIO *d, const path_chain *path, ULONG ea_index,
  425.                    int show)
  426. {
  427.   ULONG rel_cluster, cluster, size, size2, i, n;
  428.   ULONG pos, need_eas, value_size, secno;
  429.   FAT_SECTOR ea3;
  430.   char *buf;
  431.   const char *name, *value;
  432.   const FEA *pfea;
  433.  
  434.   if ((ea_index >> 7) >= 240 || ea_index >= ea_table2_entries)
  435.     {
  436.       warning (1, "\"%s\": Invalid EA index (%lu)",
  437.                format_path_chain (path, NULL), ea_index);
  438.       return;
  439.     }
  440.   if (ea_table2[ea_index] == 0xffff)
  441.     {
  442.       warning (1, "\"%s\": EA index (%lu) points to unused slot",
  443.                format_path_chain (path, NULL), ea_index);
  444.       return;
  445.     }
  446.   rel_cluster = ea_table1[ea_index >> 7] + ea_table2[ea_index];
  447.   if (show)
  448.     info ("Rel. EA cluster:    %lu\n", rel_cluster);
  449.   if ((rel_cluster + 1) * bytes_per_cluster > ea_data_size)
  450.     {
  451.       warning (1, "\"%s\": Invalid relative EA cluster (%lu)",
  452.                format_path_chain (path, NULL), rel_cluster);
  453.       return;
  454.     }
  455.  
  456.   cluster = ea_data_start;
  457.   for (i = 0; i < rel_cluster; ++i)
  458.     {
  459.       if (cluster < 2 || cluster >= total_clusters)
  460.         abort ();               /* Already checked */
  461.       cluster = fat[cluster];
  462.     }
  463.  
  464.   secno = CLUSTER_TO_SECTOR (cluster);
  465.   read_sec (d, &ea3, secno, 1, FALSE);
  466.   if (memcmp (ea3.ea3.magic, "EA", 2) != 0)
  467.     {
  468.       warning (1, "\"%s\": Incorrect signature for EA (sector #%lu)",
  469.                format_path_chain (path, NULL), secno);
  470.       return;
  471.     }
  472.  
  473.   if (USHORT_FROM_FS (ea3.ea3.rel_cluster) != ea_index)
  474.     warning (1, "\"%s\": Incorrect EA index in \"EA DATA. SF\" "
  475.              "(sector #%lu)", format_path_chain (path, NULL), secno);
  476.  
  477.   if (memchr (ea3.ea3.name, 0, 13) == NULL)
  478.     warning (1, "\"%s\": Name in \"EA DATA. SF\" not null-terminated "
  479.              "(sector #%lu)", format_path_chain (path, NULL), secno);
  480.   /* OS/2 does not update "EA DATA. SF" when renaming a file!
  481.      Therefore we check this only if -p is given. */
  482.   else if (check_pedantic
  483.            && strcmp ((const char *)ea3.ea3.name, path->name) != 0)
  484.     warning (0, "\"%s\": Name in \"EA DATA. SF\" does not match "
  485.              "(sector #%lu)", format_path_chain (path, NULL), secno);
  486.  
  487.   size = ULONG_FROM_FS (ea3.ea3.fealist.cbList);
  488.   if (size >= 0x40000000
  489.       || (rel_cluster * bytes_per_cluster
  490.           + offsetof (FAT_SECTOR, ea3.fealist)) > ea_data_size)
  491.     {
  492.       warning (1, "\"%s\": EAs too big (sector #%lu, %lu bytes)",
  493.                format_path_chain (path, NULL), secno, size);
  494.       return;
  495.     }
  496.  
  497.   if (show)
  498.     info ("Size of EAs:        %lu\n", size);
  499.  
  500.   size2 = size + offsetof (FAT_SECTOR, ea3.fealist);
  501.  
  502.   if (a_check)
  503.     {
  504.       n = DIVIDE_UP (size2, bytes_per_cluster);
  505.       for (i = 0; i < n; ++i)
  506.         {
  507.           if (ea_usage[rel_cluster+i] != FALSE)
  508.             {
  509.               warning (1, "Relative cluster %lu of \"EA DATA. SF\" "
  510.                        "multiply used", rel_cluster + i);
  511.               warning_cont ("File 2: \"%s\"", format_path_chain (path, NULL));
  512.             }
  513.           else
  514.             ea_usage[rel_cluster+i] = TRUE;
  515.         }
  516.     }
  517.  
  518.   if (a_where)
  519.     {
  520.       ULONG c;
  521.  
  522.       n = DIVIDE_UP (size2, bytes_per_cluster);
  523.       c = cluster;
  524.       for (i = 0; i < n; ++i)
  525.         {
  526.           if (c < 2 || c >= total_clusters)
  527.             abort ();           /* Already checked */
  528.           info ("Extended attributes in cluster %lu\n", c);
  529.           c = fat[c];
  530.         }
  531.     }
  532.  
  533.   if (a_what)
  534.     {
  535.       for (pos = 0; pos < size2; pos += bytes_per_cluster)
  536.         {
  537.           if (cluster < 2 || cluster >= total_clusters)
  538.             abort ();           /* Already checked */
  539.           if (what_cluster_flag && cluster == what_cluster)
  540.             info ("Cluster %lu: Extended attributes for \"%s\"\n",
  541.                   cluster, format_path_chain (path, NULL));
  542.           else if (!what_cluster_flag)
  543.             {
  544.               secno = CLUSTER_TO_SECTOR (cluster);
  545.               if (IN_RANGE (what_sector, secno,
  546.                             MIN (sectors_per_cluster,
  547.                                  DIVIDE_UP (size2 - pos, 512))))
  548.                 info ("Sector #%lu: Extended attributes for \"%s\"\n",
  549.                       what_sector, format_path_chain (path, NULL));
  550.             }
  551.           cluster = fat[cluster];
  552.         }
  553.     }
  554.   else if (size2 <= 0x100000 && (a_check || a_where))
  555.     {
  556.       buf = xmalloc (ROUND_UP (size2, 512));
  557.       /* TODO: This reads the first sector twice. */
  558.       for (pos = 0; pos < size2; pos += bytes_per_cluster)
  559.         {
  560.           if (cluster < 2 || cluster >= total_clusters)
  561.             abort ();           /* Already checked */
  562.           read_sec (d, buf + pos, CLUSTER_TO_SECTOR (cluster),
  563.                     MIN (sectors_per_cluster, DIVIDE_UP (size2 - pos, 512)),
  564.                     FALSE);
  565.           cluster = fat[cluster];
  566.         }
  567.  
  568.       pos = offsetof (FEALIST, list); need_eas = 0;
  569.       while (pos < size)
  570.         {
  571.           if (pos + sizeof (FEA) > size)
  572.             {
  573.               warning (1, "\"%s\": Truncated FEA structure",
  574.                        format_path_chain (path, NULL));
  575.               break;
  576.             }
  577.           pfea = (const FEA *)(buf + pos + offsetof (FAT_SECTOR, ea3.fealist));
  578.           if (pfea->fEA & FEA_NEEDEA)
  579.             ++need_eas;
  580.           value_size = USHORT_FROM_FS (pfea->cbValue);
  581.           if (pos + sizeof (FEA) + pfea->cbName + 1 + value_size > size)
  582.             {
  583.               warning (1, "\"%s\": Incorrect EA size",
  584.                        format_path_chain (path, NULL));
  585.               break;
  586.             }
  587.           name = (const char *)pfea + sizeof (FEA);
  588.           value = name + 1 + pfea->cbName;
  589.           if (name[pfea->cbName] != 0)
  590.             warning (1, "\"%s\": EA name not null-terminated",
  591.                      format_path_chain (path, NULL));
  592.           if (show_eas >= 1)
  593.             info ("Extended attribute %s (%lu bytes)\n",
  594.                   format_ea_name (pfea), value_size);
  595.           pos += sizeof (FEA) + pfea->cbName + 1 + value_size;
  596.         }
  597.       if (need_eas != ULONG_FROM_FS (ea3.ea3.need_eas))
  598.         warning (1, "\"%s\": Incorrect number of `need' EAs",
  599.                  format_path_chain (path, NULL));
  600.       free (buf);
  601.     }
  602. }
  603.  
  604.  
  605. static void do_dir (DISKIO *d, ULONG secno, ULONG entries,
  606.                     const path_chain *path, struct vfat *pv,
  607.                     ULONG parent_cluster, ULONG start_cluster,
  608.                     ULONG this_cluster, ULONG dirent_index, int list);
  609.  
  610. static void dirent_warning (int level, const char *fmt, ULONG secno,
  611.                             const path_chain *path, const BYTE *name, ...)
  612. {
  613.   va_list arg_ptr;
  614.  
  615.   warning_prolog (level);
  616.   my_fprintf (diag_file, "Directory sector #%lu (\"%s\"): \"%s\": ",
  617.               secno, format_path_chain (path, NULL), name);
  618.   va_start (arg_ptr, name);
  619.   my_vfprintf (diag_file, fmt, arg_ptr);
  620.   va_end (arg_ptr);
  621.   fputc ('\n', diag_file);
  622.   warning_epilog ();
  623. }
  624.  
  625.  
  626. static void do_enddir (DISKIO *d, const path_chain *path, struct vfat *pv,
  627.                        int found)
  628. {
  629.   if (pv->flag)
  630.     warning (1, "\"%s\": No real directory entry after VFAT name",
  631.              format_path_chain (path, NULL));
  632.   if (a_find)
  633.     {
  634.       if (found)
  635.         quit (0, FALSE);
  636.       else
  637.         error ("\"%s\" not found in \"%s\"",
  638.                find_comp, format_path_chain (path, NULL));
  639.     }
  640. }
  641.  
  642.  
  643. static void do_file (DISKIO *d, ULONG start_cluster, int dir_flag,
  644.                      const path_chain *path, ULONG parent_cluster,
  645.                      ULONG file_size, ULONG ea_index, int list)
  646. {
  647.   ULONG count, cluster, dirent_index, extents, ext_start, ext_length;
  648.   int show, found;
  649.   char *copy_buf = NULL;
  650.   struct vfat v;
  651.  
  652.   found = (a_find && *find_path == 0);
  653.   show = (a_where && found);
  654.   if (found && a_copy)
  655.     {
  656.       if (dir_flag)
  657.         error ("Directories cannot be copied");
  658.       copy_buf = xmalloc (bytes_per_cluster);
  659.     }
  660.   count = 0; cluster = start_cluster; dirent_index = 0; v.flag = FALSE;
  661.   extents = 0; ext_start = 0; ext_length = 0;
  662.   if (cluster != 0)
  663.     while (cluster < 0xfff8)
  664.       {
  665.         if (ext_length == 0)
  666.           {
  667.             ++extents; ext_start = cluster; ext_length = 1;
  668.           }
  669.         else if (cluster == ext_start + ext_length)
  670.           ++ext_length;
  671.         else
  672.           {
  673.             if (show)
  674.               info ("File data in %s\n",
  675.                     format_cluster_range (ext_start, ext_length));
  676.             ++extents; ext_start = cluster; ext_length = 1;
  677.           }
  678.         if (cluster == 0)
  679.           {
  680.             warning (1, "\"%s\": References unused cluster",
  681.                      format_path_chain (path, NULL));
  682.             break;
  683.           }
  684.         else if (cluster == 0xfff7)
  685.           {
  686.             warning (1, "\"%s\": References bad cluster",
  687.                      format_path_chain (path, NULL));
  688.             break;
  689.           }
  690.         else if (cluster < 0xfff8
  691.                  && (cluster < 2 || cluster >= total_clusters))
  692.           {
  693.             warning (1, "\"%s\": %lu: Invalid cluster number",
  694.                      format_path_chain (path, NULL), cluster);
  695.             break;
  696.           }
  697.         else
  698.           {
  699.             if (!use_cluster (cluster, dir_flag ? USE_DIR : USE_FILE, path))
  700.               {
  701.                 warning (1, "\"%s\": Cycle after %lu clusters",
  702.                          format_path_chain (path, NULL), count);
  703.                 break;
  704.               }
  705.             if (a_what)
  706.               {
  707.                 if (what_cluster_flag && what_cluster == cluster)
  708.                   info ("Cluster %lu: Relative cluster %lu of \"%s\"\n",
  709.                         what_cluster, count, format_path_chain (path, NULL));
  710.                 else if (!what_cluster_flag
  711.                          && IN_RANGE (what_sector, CLUSTER_TO_SECTOR (cluster),
  712.                                       sectors_per_cluster))
  713.                   info ("Sector #%lu: Relative sector %lu of \"%s\"\n",
  714.                         what_sector,
  715.                         (count * sectors_per_cluster
  716.                          + what_sector - CLUSTER_TO_SECTOR (cluster)),
  717.                         format_path_chain (path, NULL));
  718.               }
  719.             if (dir_flag && (!found || !list))
  720.               {
  721.                 do_dir (d, CLUSTER_TO_SECTOR (cluster),
  722.                         bytes_per_cluster/32, path, &v,
  723.                         parent_cluster, start_cluster, cluster, dirent_index,
  724.                         found && a_dir);
  725.                 dirent_index += bytes_per_cluster/32;
  726.               }
  727.             if (a_copy && found && count * bytes_per_cluster < file_size)
  728.               {
  729.                 read_sec (d, copy_buf, CLUSTER_TO_SECTOR (cluster),
  730.                           sectors_per_cluster, FALSE);
  731.                 fwrite (copy_buf,
  732.                         MIN (file_size - count * bytes_per_cluster,
  733.                              bytes_per_cluster), 1, save_file);
  734.                 if (ferror (save_file))
  735.                   save_error ();
  736.               }
  737.             cluster = fat[cluster];
  738.             ++count;
  739.           }
  740.       }
  741.  
  742.   if (dir_flag && !found)
  743.     do_enddir (d, path, &v, FALSE);
  744.  
  745.   if (show)
  746.     {
  747.       if (ext_length != 0)
  748.         info ("File data in %s\n",
  749.               format_cluster_range (ext_start, ext_length));
  750.       info ("Number of clusters: %lu\n", count);
  751.       info ("Number of extents:  %lu\n", extents);
  752.     }
  753.  
  754.   if (ea_index != 0)
  755.     do_ea (d, path, ea_index, show);
  756.  
  757.   if (a_check)
  758.     {
  759.       if (!dir_flag)
  760.         {
  761.           if (count * bytes_per_cluster < file_size)
  762.             warning (1, "\"%s\": Not enough clusters allocated",
  763.                      format_path_chain (path, NULL));
  764.           if (count > DIVIDE_UP (file_size, bytes_per_cluster))
  765.             warning (1, "\"%s\": Too many clusters allocated",
  766.                      format_path_chain (path, NULL));
  767.         }
  768.     }
  769.   if (found)
  770.     {
  771.       if (a_copy)
  772.         save_close ();
  773.       if (!a_dir)
  774.         quit (0, FALSE);
  775.     }
  776. }
  777.  
  778.  
  779. static void do_dirent (DISKIO *d, ULONG secno, const FAT_DIRENT *p,
  780.                        const path_chain *path, struct vfat *pv,
  781.                        ULONG parent_cluster, ULONG start_cluster,
  782.                        ULONG dirent_index, int *label_flag, int show, int list)
  783. {
  784.   BYTE name[13], cs;
  785.   int i, n;
  786.   char found;
  787.   unsigned date, time;
  788.   ULONG cluster;
  789.   const VFAT_DIRENT *v;
  790.   USHORT vname[13];
  791.  
  792.   if (p->name[0] == 0xe5)
  793.     {
  794.       if (pv->flag)
  795.         {
  796.           warning (1, "\"%s\": Unused directory entry after VFAT name "
  797.                    "(sector #%lu)", format_path_chain (path, NULL), secno);
  798.           pv->flag = FALSE;
  799.         }
  800.       return;
  801.     }
  802.  
  803.   if (p->attr == 0x0f)
  804.     {
  805.       v = (const VFAT_DIRENT *)p;
  806.       for (i = 0; i < 5; ++i)
  807.         vname[i+0] = USHORT_FROM_FS (v->name1[i]);
  808.       for (i = 0; i < 6; ++i)
  809.         vname[i+5] = USHORT_FROM_FS (v->name2[i]);
  810.       for (i = 0; i < 2; ++i)
  811.         vname[i+11] = USHORT_FROM_FS (v->name3[i]);
  812.       n = 13;
  813.       while (n > 0 && vname[n-1] == 0xffff)
  814.         --n;
  815.  
  816.       if (show)
  817.         {
  818.           info ("Directory entry %lu of \"%s\":\n", dirent_index,
  819.                 format_path_chain (path, NULL));
  820.           info ("  VFAT name frag:   \"");
  821.           for (i = 0; i < n; ++i)
  822.             if (vname[i] >= 0x20 && vname[i] <= 0xff)
  823.               info ("%c", vname[i]);
  824.             else
  825.               info ("<0x%x>", vname[i]);
  826.           info ("\"\n");
  827.         }
  828.  
  829.       if (v->flag > 0x7f)
  830.         {
  831.           warning (1, "\"%s\": Invalid VFAT name (sector #%lu)",
  832.                    format_path_chain (path, NULL), secno);
  833.           pv->flag = FALSE;
  834.           return;
  835.         }
  836.       else if (v->flag & 0x40)
  837.         {
  838.           if (pv->flag)
  839.             warning (1, "\"%s\": No real directory entry after VFAT name "
  840.                      "(sector #%lu)", format_path_chain (path, NULL), secno);
  841.           else if (n == 0 || vname[n-1] != 0)
  842.             {
  843.               warning (1, "\"%s\": VFAT name not null-terminated "
  844.                        "(sector #%lu)", format_path_chain (path, NULL), secno);
  845.               return;
  846.             }
  847.           else
  848.             {
  849.               --n;
  850.               pv->flag = TRUE;
  851.               pv->unprintable = FALSE;
  852.               pv->name[256] = 0;
  853.               pv->start = 256;
  854.               pv->total = v->flag & 0x3f;
  855.               pv->index = v->flag & 0x3f;
  856.               pv->checksum = v->checksum;
  857.             }
  858.         }
  859.       if ((v->flag & 0x3f) != pv->index || pv->index == 0)
  860.         {
  861.           warning (1, "\"%s\": Incorrect VFAT name index (sector #%lu)",
  862.                    format_path_chain (path, NULL), secno);
  863.           pv->flag = FALSE;
  864.           return;
  865.         }
  866.       if (v->checksum != pv->checksum)
  867.         warning (1, "\"%s\": Incorrect VFAT checksum (sector #%lu)",
  868.                  format_path_chain (path, NULL), secno);
  869.       pv->index -= 1;
  870.       if (pv->start < n)
  871.         {
  872.           warning (1, "\"%s\": VFAT name too long (sector #%lu)",
  873.                    format_path_chain (path, NULL), secno);
  874.           pv->flag = FALSE;
  875.           return;
  876.         }
  877.       for (i = n-1; i >= 0; --i)
  878.         {
  879.           if (vname[i] < 0x20 || vname[i] > 0xff)
  880.             pv->unprintable = TRUE;
  881.           pv->name[--pv->start] = (BYTE)vname[i];
  882.         }
  883.       return;
  884.     }
  885.  
  886.   cluster = USHORT_FROM_FS (p->cluster);
  887.   found = FALSE;
  888.  
  889.   if (p->name[0] == '.')
  890.     {
  891.       i = 1;
  892.       if (p->name[1] == '.')
  893.         ++i;
  894.       memcpy (name, p->name, i);
  895.       name[i] = 0;
  896.     }
  897.   else if (p->attr & ATTR_LABEL)
  898.     {
  899.       memcpy (name, p->name, 11);
  900.       i = 11;
  901.       while (i > 0 && name[i-1] == ' ')
  902.         --i;
  903.       name[i] = 0;
  904.     }
  905.   else
  906.     {
  907.       memcpy (name, p->name + 0, 8);
  908.       i = 8;
  909.       while (i > 0 && name[i-1] == ' ')
  910.         --i;
  911.       if (memcmp (p->name + 8, "   ", 3) != 0)
  912.         {
  913.           name[i++] = '.';
  914.           memcpy (name + i, p->name + 8, 3);
  915.           i += 3;
  916.           while (name[i-1] == ' ')
  917.             --i;
  918.         }
  919.       name[i] = 0;
  920.     }
  921.   if (name[0] == 0x05)
  922.     name[0] = 0xe5;
  923.  
  924.   if (pv->flag)
  925.     {
  926.       if (pv->index != 0)
  927.         {
  928.           warning (1, "\"%s\": Incomplete VFAT name for \"%s\" (sector #%lu)",
  929.                    format_path_chain (path, NULL), name, secno);
  930.           pv->flag = FALSE;
  931.         }
  932.       /* TODO: Compare VFAT name to regular name */
  933.  
  934.       /* Compute and check the checksum. */
  935.  
  936.       cs = 0;
  937.       for (i = 0; i < 8+3; ++i)
  938.         cs = rorb1 (cs) + p->name[i];
  939.       if (cs != pv->checksum)
  940.         warning (1, "\"%s\": Checksum mismatch for \"%s\" (sector #%lu): "
  941.                  "0x%.2x vs. 0x%.2x", format_path_chain (path, NULL), name,
  942.                  secno, pv->checksum, cs);
  943.     }
  944.  
  945.   if (a_find && !show && !list)
  946.     {
  947.       if (compare_fname (name, find_comp) != 0)
  948.         goto done;
  949.       if (*find_path == 0)
  950.         {
  951.           found = TRUE;
  952.           if (a_where)
  953.             {
  954.               info ("Directory entry in sector #%lu\n", secno);
  955.               show = TRUE;
  956.             }
  957.           if (a_dir)
  958.             show = TRUE;
  959.         }
  960.     }
  961.  
  962.   date = USHORT_FROM_FS (p->date);
  963.   time = USHORT_FROM_FS (p->time);
  964.  
  965.   if (list || (a_dir && show && !(p->attr & ATTR_DIR)))
  966.     {
  967.       info ("%s %s ", format_date (date), format_time (time));
  968.       if (p->attr & ATTR_DIR)
  969.         info ("     <DIR>      ");
  970.       else
  971.         info ("%10lu %c%c%c%c%c",
  972.               ULONG_FROM_FS (p->size),
  973.               (p->attr & ATTR_READONLY) ? 'R' : '-',
  974.               (p->attr & ATTR_HIDDEN)   ? 'H' : '-',
  975.               (p->attr & ATTR_SYSTEM)   ? 'S' : '-',
  976.               (p->attr & ATTR_LABEL)    ? 'V' : '-',
  977.               (p->attr & ATTR_ARCHIVED) ? 'A' : '-');
  978.       info (" \"%s\"\n", name);
  979.     }
  980.  
  981.   if (show && !a_dir)
  982.     {
  983.       info ("Directory entry %lu of \"%s\":\n", dirent_index,
  984.             format_path_chain (path, NULL));
  985.       info ("  Name:             \"%s\"\n", name);
  986.       info ("  Attributes:       0x%.2x", p->attr);
  987.       if (p->attr & ATTR_DIR)
  988.         info (" dir");
  989.       if (p->attr & ATTR_READONLY)
  990.         info (" r/o");
  991.       if (p->attr & ATTR_HIDDEN)
  992.         info (" hidden");
  993.       if (p->attr & ATTR_SYSTEM)
  994.         info (" system");
  995.       if (p->attr & ATTR_LABEL)
  996.         info (" label");
  997.       if (p->attr & ATTR_ARCHIVED)
  998.         info (" arch");
  999.       info ("\n");
  1000.       info ("  Cluster:          %lu\n", cluster);
  1001.       info ("  Time:             0x%.4x (%s)\n", time, format_time (time));
  1002.       info ("  Date:             0x%.4x (%s)\n", date, format_date (date));
  1003.       info ("  Size:             %lu\n", ULONG_FROM_FS (p->size));
  1004.       info ("  EA pointer:       %u\n", USHORT_FROM_FS (p->ea));
  1005.       if (pv->flag)
  1006.         if (pv->unprintable)
  1007.           info ("  VFAT name:        (not printable)\n");
  1008.         else
  1009.           info ("  VFAT name:        \"%s\"\n", pv->name + pv->start);
  1010.     }
  1011.  
  1012.   if (a_check)
  1013.     {
  1014.       unsigned y, m, d, h, s;
  1015.       int i;
  1016.  
  1017.       y = ((date >> 9) & 127) + 1980;
  1018.       m = (date >> 5) & 15;
  1019.       d = date & 31;
  1020.       if (m < 1 || m > 12 || d < 1 || d > days (y, m))
  1021.         dirent_warning (0, "Invalid date (0x%.4x)", secno, path, name, date);
  1022.  
  1023.       h = (time >> 11) & 31;
  1024.       m = (time >> 5) & 63;
  1025.       s = (time & 31) << 1;
  1026.       if (h > 23 || m > 59 || s > 59)
  1027.         dirent_warning (0, "Invalid time (0x%.4x)", secno, path, name, time);
  1028.       if (p->attr & ~0x3f)
  1029.         dirent_warning (0, "Undefined attribute bit is set",
  1030.                         secno, path, name);
  1031.  
  1032.       /* Check the file name.  Note that file names starting with `.'
  1033.          are checked further down. */
  1034.  
  1035.       if (p->name[0] != '.')
  1036.         {
  1037.           for (i = 0; i < 11; ++i)
  1038.             if (p->name[i] != 0x05
  1039.                 && (p->name[i] < 0x20
  1040.                     || strchr ("\"*+,./;:<=>?[\\]|", p->name[i]) != NULL))
  1041.               break;
  1042.           if (i < 11)
  1043.             dirent_warning (1, "Invalid character in file name",
  1044.                             secno, path, name);
  1045.         }
  1046.     }
  1047.  
  1048.   if (p->name[0] == '.')
  1049.     {
  1050.       if (pv->flag)
  1051.         {
  1052.           dirent_warning (1, "Must not have a VFAT name", secno, path, name);
  1053.           pv->flag = FALSE;
  1054.         }
  1055.       if (!a_check)
  1056.         return;
  1057.       if (memcmp (p->name + i, "          ", 11 - i) != 0)
  1058.         dirent_warning (1, "File name starting with \".\"",
  1059.                         secno, path, name);
  1060.       else if (!(p->attr & ATTR_DIR))
  1061.         dirent_warning (1, "Not a directory", secno, path, name);
  1062.       else if (cluster != (i == 1 ? start_cluster : parent_cluster))
  1063.         dirent_warning (1, "Incorrect cluster (%lu vs. %lu)",
  1064.                         secno, path, name,
  1065.                         cluster, (i == 1 ? start_cluster : parent_cluster));
  1066.       return;
  1067.     }
  1068.  
  1069.  
  1070.   if (verbose)
  1071.     my_fprintf (prog_file, "%s\n", format_path_chain (path, (char *)name));
  1072.  
  1073.   if (a_check)
  1074.     {
  1075.       if (p->attr & ATTR_LABEL)
  1076.         {
  1077.           if (path->parent != NULL)
  1078.             dirent_warning (1, "Unexpected volume label",
  1079.                             secno, path, name);
  1080.           else if (*label_flag)
  1081.             dirent_warning (1, "More than one volume label",
  1082.                             secno, path, name);
  1083.           else
  1084.             *label_flag = TRUE;
  1085.         }
  1086.     }
  1087.  
  1088.   if (!(p->attr & ATTR_LABEL)
  1089.       && !list
  1090.       && !(a_what && !what_cluster_flag && what_sector < data_sector))
  1091.     {
  1092.       path_chain link, *plink;
  1093.  
  1094.       plink = PATH_CHAIN_NEW (&link, path, (char *)name);
  1095.       do_file (d, cluster, p->attr & ATTR_DIR, plink, start_cluster,
  1096.                ULONG_FROM_FS (p->size), USHORT_FROM_FS (p->ea), list);
  1097.     }
  1098.   if (found && !list)
  1099.     quit (0, FALSE);
  1100. done:
  1101.   pv->flag = FALSE;
  1102. }
  1103.  
  1104.  
  1105. static void do_dir (DISKIO *d, ULONG secno, ULONG entries,
  1106.                     const path_chain *path, struct vfat *pv,
  1107.                     ULONG parent_cluster, ULONG start_cluster,
  1108.                     ULONG this_cluster, ULONG dirent_index, int list)
  1109. {
  1110.   FAT_DIRENT dir[512/32];
  1111.   ULONG i, n, last_secno;
  1112.   int show, label_flag;
  1113.  
  1114.   if (a_find && dirent_index == 0)
  1115.     {
  1116.       const char *pdelim;
  1117.       size_t len;
  1118.  
  1119.       pdelim = strchr (find_path, '\\');
  1120.       if (pdelim == NULL)
  1121.         len = strlen (find_path);
  1122.       else
  1123.         len = pdelim - find_path;
  1124.       if (len > 255)
  1125.         error ("Path name component too long");
  1126.       memcpy (find_comp, find_path, len);
  1127.       find_comp[len] = 0;
  1128.       find_path += len;
  1129.       if (*find_path == '\\')
  1130.         {
  1131.           ++find_path;
  1132.           if (*find_path == 0)
  1133.             error ("Trailing backslash");
  1134.         }
  1135.     }
  1136.  
  1137.   label_flag = FALSE; last_secno = 0;
  1138.   while (entries != 0)
  1139.     {
  1140.       show = FALSE;
  1141.       if (a_what)
  1142.         {
  1143.           if (what_cluster_flag && what_cluster == this_cluster)
  1144.             {
  1145.               info ("Cluster %lu: Directory \"%s\"\n", what_cluster,
  1146.                     format_path_chain (path, NULL));
  1147.               show = TRUE;
  1148.             }
  1149.           else if (!what_cluster_flag && what_sector == secno)
  1150.             {
  1151.               info ("Sector #%lu: Directory \"%s\"\n", what_sector,
  1152.                     format_path_chain (path, NULL));
  1153.               show = TRUE;
  1154.             }
  1155.         }
  1156.       read_sec (d, dir, secno, 1, TRUE);
  1157.       last_secno = secno;
  1158.       n = MIN (512/32, entries);
  1159.       for (i = 0; i < n; ++i)
  1160.         {
  1161.           if (dir[i].name[0] == 0)
  1162.             goto done;
  1163.           do_dirent (d, secno, &dir[i], path, pv, parent_cluster,
  1164.                      start_cluster, dirent_index, &label_flag,
  1165.                      show, list);
  1166.           ++dirent_index;
  1167.         }
  1168.       ++secno; entries -= 512/32;
  1169.     }
  1170. done:;
  1171. }
  1172.  
  1173.  
  1174. static void find_ea_data (DISKIO *d, ULONG secno, ULONG entries)
  1175. {
  1176.   ULONG i, n;
  1177.   FAT_DIRENT dir[512/32];
  1178.  
  1179.   ea_data_start = 0xffff; ea_data_size = 0;
  1180.   while (entries != 0)
  1181.     {
  1182.       read_sec (d, dir, secno, 1, FALSE);
  1183.       n = MIN (512/32, entries);
  1184.       for (i = 0; i < n; ++i)
  1185.         {
  1186.           if (dir[i].name[0] == 0)
  1187.             return;
  1188.           if (memcmp (dir[i].name, "EA DATA  SF", 8+3) == 0
  1189.               && !(dir[i].attr & (ATTR_LABEL|ATTR_DIR)))
  1190.             {
  1191.               ea_data_start = USHORT_FROM_FS (dir[i].cluster);
  1192.               ea_data_size = ULONG_FROM_FS (dir[i].size);
  1193.               ea_data_clusters = DIVIDE_UP (ea_data_size, bytes_per_cluster);
  1194.               if (a_info)
  1195.                 {
  1196.                   info ("\"EA DATA. SF\" 1st cluster:  %lu\n", ea_data_start);
  1197.                   info ("\"EA DATA. SF\" size:         %lu\n", ea_data_size);
  1198.                 }
  1199.               return;
  1200.             }
  1201.         }
  1202.       ++secno; entries -= 512/32;
  1203.     }
  1204. }
  1205.  
  1206.  
  1207. static void do_root_dir (DISKIO *d)
  1208. {
  1209.   ULONG secno;
  1210.   int list;
  1211.  
  1212.   secno = first_sector + number_of_fats * sectors_per_fat;
  1213.   list = FALSE;
  1214.  
  1215.   if (a_find && *find_path == 0)
  1216.     {
  1217.       if (a_where)
  1218.         info ("Root directory in %s\n",
  1219.               format_sector_range (secno, root_sectors));
  1220.       if (a_dir)
  1221.         list = TRUE;
  1222.       else
  1223.         quit (0, FALSE);
  1224.     }
  1225.   if (a_info)
  1226.     info ("Root directory:             %s\n",
  1227.           format_sector_range (secno, root_sectors));
  1228.   if (a_what)
  1229.     {
  1230.       if (!what_cluster_flag && IN_RANGE (what_sector, secno, root_sectors))
  1231.         info ("Sector #%lu: Root directory (+%lu)\n", what_sector,
  1232.               what_sector - secno);
  1233.     }
  1234.  
  1235.   find_ea_data (d, secno, root_entries);
  1236.   read_ea_data (d);
  1237.  
  1238.   if (a_save || a_check || a_what || a_find)
  1239.     {
  1240.       path_chain link, *plink;
  1241.       struct vfat v;
  1242.  
  1243.       v.flag = FALSE;
  1244.       plink = PATH_CHAIN_NEW (&link, NULL, "");
  1245.       do_dir (d, secno, root_entries, plink, &v, 0, 0, 0, 0, list);
  1246.       do_enddir (d, plink, &v, list);
  1247.     }
  1248. }
  1249.  
  1250.  
  1251. #define ALLOCATED(x)  (fat[x] != 0 && fat[x] != 0xfff7)
  1252.  
  1253. static void check_alloc (void)
  1254. {
  1255.   ULONG i, start, count;
  1256.  
  1257.   i = 2; count = 0;
  1258.   while (i < total_clusters)
  1259.     {
  1260.       if (usage_vector[i] == USE_EMPTY && ALLOCATED (i))
  1261.         {
  1262.           start = i;
  1263.           do
  1264.             {
  1265.               ++i;
  1266.             } while (i < total_clusters
  1267.                      && usage_vector[i] == USE_EMPTY && ALLOCATED (i));
  1268.           if (check_unused)
  1269.             warning (0, "Unused but marked as allocated: %s",
  1270.                      format_cluster_range (start, i - start));
  1271.           count += i - start;
  1272.         }
  1273.       else
  1274.         ++i;
  1275.     }
  1276.   if (count == 1)
  1277.     warning (0, "The file system has 1 lost cluster");
  1278.   else if (count > 1)
  1279.     warning (0, "The file system has %lu lost clusters", count);
  1280. }
  1281.  
  1282.  
  1283. /* Process a FAT volume. */
  1284.  
  1285. void do_fat (DISKIO *d, const FAT_SECTOR *pboot)
  1286. {
  1287.   ULONG i;
  1288.  
  1289.   plenty_memory = TRUE;
  1290.   if (USHORT_FROM_FS (pboot->boot.bytes_per_sector) != 512)
  1291.     error ("Sector size %u is not supported",
  1292.            USHORT_FROM_FS (pboot->boot.bytes_per_sector));
  1293.   if (pboot->boot.sectors_per_cluster == 0)
  1294.     error ("Cluster size is zero");
  1295.   if (pboot->boot.fats == 0)
  1296.     error ("Number of FATs is zero");
  1297.   first_sector = USHORT_FROM_FS (pboot->boot.reserved_sectors);
  1298.   sectors_per_cluster = pboot->boot.sectors_per_cluster;
  1299.   bytes_per_cluster = sectors_per_cluster * 512;
  1300.   sectors_per_fat = USHORT_FROM_FS (pboot->boot.sectors_per_fat);
  1301.   number_of_fats = pboot->boot.fats;
  1302.  
  1303.   if (USHORT_FROM_FS (pboot->boot.sectors) != 0)
  1304.     total_sectors = USHORT_FROM_FS (pboot->boot.sectors);
  1305.   else
  1306.     total_sectors = ULONG_FROM_FS (pboot->boot.large_sectors);
  1307.   if (total_sectors < USHORT_FROM_FS (pboot->boot.reserved_sectors))
  1308.     error ("Number of reserved sectors exceeds total number of sectors");
  1309.   total_sectors -= USHORT_FROM_FS (pboot->boot.reserved_sectors);
  1310.  
  1311.   root_entries = USHORT_FROM_FS (pboot->boot.root_entries);
  1312.   root_sectors = DIVIDE_UP (root_entries, 512/32);
  1313.  
  1314.   if (total_sectors < number_of_fats * sectors_per_fat + root_sectors)
  1315.     error ("Disk too small for FATs and root directory");
  1316.   total_clusters = ((total_sectors - number_of_fats * sectors_per_fat
  1317.                      - root_sectors)
  1318.                     / sectors_per_cluster);
  1319.   total_clusters += 2;
  1320.   if (total_clusters < 2)
  1321.     error ("Disk too small, no data clusters");
  1322.   if (total_clusters > 0xffff)
  1323.     warning (0, "Too many clusters");
  1324.  
  1325.   data_sector = (first_sector + number_of_fats * sectors_per_fat
  1326.                  + root_sectors);
  1327.  
  1328.   if (a_info)
  1329.     {
  1330.       info ("Number of clusters:         %lu\n", total_clusters - 2);
  1331.       info ("First data sector:          #%lu\n", data_sector);
  1332.     }
  1333.  
  1334.   if (a_what && what_cluster_flag)
  1335.     {
  1336.       if (what_sector < 2 || what_sector >= total_clusters)
  1337.         error ("Invalid cluster number");
  1338.       what_cluster = what_sector;
  1339.       what_sector = CLUSTER_TO_SECTOR (what_sector);
  1340.     }
  1341.  
  1342.   if (a_what)
  1343.     {
  1344.       if (!what_cluster_flag && what_sector == 0)
  1345.         info ("Sector #%lu: Boot sector\n", what_sector);
  1346.     }
  1347.  
  1348.   /* Allocate usage vector.  Initially, all clusters are unused. */
  1349.  
  1350.   usage_vector = (BYTE *)xmalloc (total_clusters);
  1351.   memset (usage_vector, USE_EMPTY, total_clusters);
  1352.  
  1353.   path_vector = xmalloc (total_clusters * sizeof (*path_vector));
  1354.   for (i = 0; i < total_clusters; ++i)
  1355.     path_vector[i] = NULL;
  1356.  
  1357.   do_fats (d);
  1358.  
  1359.   if (a_what)
  1360.     {
  1361.       if (!what_cluster_flag
  1362.           && what_sector >= data_sector && what_sector < total_sectors)
  1363.         {
  1364.           i = SECTOR_TO_CLUSTER (what_sector);
  1365.           if (i >= 2 && i < total_clusters)
  1366.             {
  1367.               info ("Sector #%lu: Cluster %lu\n", what_sector, i);
  1368.               if (fat[i] == 0xfff7)
  1369.                 info ("Sector #%lu: Cluster contains bad sector\n",
  1370.                       what_sector);
  1371.               else if (fat[i] >= 0xfff8)
  1372.                 info ("Sector #%lu: In last cluster of a file or directory\n",
  1373.                       what_sector);
  1374.               else if (fat[i] == 0)
  1375.                 info ("Sector #%lu: In an unused cluster\n", what_sector);
  1376.               else
  1377.                 info ("Sector #%lu: In a used cluster\n", what_sector);
  1378.             }
  1379.         }
  1380.       else if (what_cluster_flag)
  1381.         {
  1382.           info ("Cluster %lu: %s\n", what_cluster,
  1383.                 format_sector_range (CLUSTER_TO_SECTOR (what_cluster),
  1384.                                      sectors_per_cluster));
  1385.           if (fat[what_cluster] == 0xfff7)
  1386.             info ("Cluster %lu: Cluster contains bad sector\n", what_cluster);
  1387.           else if (fat[what_cluster] >= 0xfff8)
  1388.             info ("Cluster %lu: Last cluster of a file or directory\n",
  1389.                   what_cluster);
  1390.           else if (fat[what_cluster] == 0)
  1391.             info ("Cluster %lu: Unused\n", what_cluster);
  1392.           else
  1393.             info ("Cluster %lu: Used\n", what_cluster);
  1394.         }
  1395.     }
  1396.  
  1397.   do_root_dir (d);
  1398.  
  1399.   if (a_check)
  1400.     {
  1401.       check_alloc ();
  1402.     }
  1403. }
  1404.