home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / fst03f.zip / do_hpfs.c < prev    next >
C/C++ Source or Header  |  1996-08-13  |  115KB  |  3,160 lines

  1. /* do_hpfs.c -- HPFS-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. #define INCL_DOSDEVIOCTL
  23. #define INCL_DOSNLS
  24. #define INCL_DOSERRORS
  25. #include <os2.h>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <stdarg.h>
  29. #include <stddef.h>
  30. #include <string.h>
  31. #include <ctype.h>
  32. #include <time.h>
  33. #include "fst.h"
  34. #include "crc.h"
  35. #include "diskio.h"
  36. #include "hpfs.h"
  37.  
  38. /* Return a non-zero value if sector X is allocated. */
  39.  
  40. #define ALLOCATED(x)    !BITSETP (alloc_vector, (x))
  41.  
  42.  
  43. /* Our own representation of a code page. */
  44.  
  45. typedef struct
  46. {
  47.   CPINFOENTRY info;
  48.   char hit;
  49.   BYTE case_map[256];
  50.   BYTE case_map_changed[256];
  51. } MYCP;
  52.  
  53. /* This structure is used for checking whether DIRENTs are arranged in
  54.    ascending order by file name. */
  55.  
  56. typedef struct
  57. {
  58.   BYTE name[256];               /* Previous file name */
  59.   ULONG cpindex;                /* Code page index for previous file name */
  60. } SORT;
  61.  
  62. /* This structure is used for counting extents. */
  63.  
  64. typedef struct
  65. {
  66.   ULONG size;                   /* Number of elements in `counts' */
  67.   ULONG *counts;                /* counts[i] = # of objects with i extents */
  68. } EXTENTS;
  69.  
  70. static ULONG total_sectors;     /* Total number of sectors of HPFS volume */
  71. static ULONG total_alloc;       /* # of bytes allocated for ∩alloc_vector' */
  72. static BYTE *usage_vector;      /* One byte per sector, indicating usage */
  73. static BYTE *seen_vector;       /* One byte per sector, to avoid loops */
  74. static BYTE *alloc_vector;      /* One bit per sector, indicating allocation */
  75. static const path_chain **path_vector; /* One path name chain per sector */
  76. static BYTE alloc_ready;        /* TRUE if alloc_vector contents valid */
  77. static ULONG code_page_count;   /* Number of code pages */
  78. static MYCP *code_pages;        /* All code pages of the HPFS volume */
  79. static ULONG cpdata_count;      /* Number of code page data sectors */
  80. static ULONG *cpdata_visited;   /* Sector numbers of visited cp data sectors */
  81. static ULONG min_time;          /* Minimum valid time stamp */
  82. static ULONG dirband_start;     /* First sector of DIRBLK band */
  83. static ULONG dirband_end;       /* Last sector of DIRBLK band */
  84. static ULONG dirblk_total;      /* Total number of DIRBLKs */
  85. static ULONG dirblk_outside;    /* Number of DIRBLKs outside DIRBLK band */
  86. static ULONG alsec_count;       /* Number of ALSECs */
  87. static ULONG file_count;        /* Number of files */
  88. static ULONG dir_count;         /* Number of directories */
  89. static ULONG sectors_per_block; /* Block size (in sectors) for `multimedia' */
  90. static EXTENTS file_extents;    /* Number of extents for files */
  91. static EXTENTS ea_extents;      /* Number of extents for EAs */
  92. static char no_country_sys;     /* COUNTRY.SYS not available */
  93. static char alsec_number[100];  /* Formatted ALSEC number */
  94. static char find_comp[256];     /* Current component of `find_path' */
  95. static char copy_buf[512];      /* Buffer for `copy' action */
  96.  
  97. static void dirblk_warning (int level, const char *fmt, ULONG secno,
  98.                             const path_chain *path, ...) ATTR_PRINTF (2, 5);
  99. static void dirent_warning (int level, const char *fmt, ULONG secno,
  100.                             const path_chain *path, int dirent_no,
  101.                             const char *fname, ...) ATTR_PRINTF (2, 7);
  102. static void alsec_warning (int level, const char *fmt, ULONG secno,
  103.                            const path_chain *path, ...) ATTR_PRINTF (2, 5);
  104. static void alloc_warning (int level, const char *fmt, ULONG secno,
  105.                            const path_chain *path,
  106.                            int fnode_flag, ...) ATTR_PRINTF (2, 6);
  107. static void fnode_warning (int level, const char *fmt, ULONG secno,
  108.                            const path_chain *path, ...) ATTR_PRINTF (2, 5);
  109.  
  110.  
  111. /* Compute the checksum for SIZE bytes at P. */
  112.  
  113. static ULONG chksum (const BYTE *p, size_t size)
  114. {
  115.   ULONG sum;
  116.  
  117.   sum = 0;
  118.   while (size != 0)
  119.     {
  120.       sum += *p++;
  121.       sum = (sum << 7) | (sum >> 25); /* Rotate left by 7 bits */
  122.       --size;
  123.     }
  124.   return sum;
  125. }
  126.  
  127.  
  128. /* Return a pointer to a string containing a formatted time stamp.
  129.    Note that the pointer points to static memory; do not use
  130.    format_time() more than once in one expression! */
  131.  
  132. static const char *format_time (ULONG x)
  133. {
  134.   static char buf[100];
  135.   time_t t;
  136.   struct tm *tm;
  137.  
  138.   if (x == 0)
  139.     strcpy (buf, "never");
  140.   else if (x < min_time)
  141.     sprintf (buf, "0x%lx", x);
  142.   else
  143.     {
  144.       t = (time_t)x;
  145.       tm = gmtime (&t);
  146.       sprintf (buf, "0x%lx (%d-%.2d-%.2d %.2d:%.2d:%.2d)",
  147.                x, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
  148.                tm->tm_hour, tm->tm_min, tm->tm_sec);
  149.     }
  150.   return buf;
  151. }
  152.  
  153.  
  154. /* Return a pointer to a string containing a formatted time stamp for
  155.    the `dir' action.  Note that the pointer points to static memory;
  156.    do not use format_dir_time() more than once in one expression! */
  157.  
  158. static const char *format_dir_time (ULONG x)
  159. {
  160.   static char buf[100];
  161.   time_t t;
  162.   struct tm *tm;
  163.  
  164.   if (x < min_time)
  165.     return "????-??-?? ??:??:??";
  166.   else
  167.     {
  168.       t = (time_t)x;
  169.       tm = gmtime (&t);
  170.       sprintf (buf, "%d-%.2d-%.2d %.2d:%.2d:%.2d",
  171.                tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
  172.                tm->tm_hour, tm->tm_min, tm->tm_sec);
  173.       return buf;
  174.     }
  175. }
  176.  
  177.  
  178. /* Return TRUE iff the file name pointed to by NAME would be a valid
  179.    file name on a FAT file system.  HPFS.IFS seems to treat the space
  180.    character (0x20) as being valid on FAT. */
  181.  
  182. static int is_fat_name (const BYTE *name)
  183. {
  184.   int n, len;
  185.   const BYTE *p;
  186.  
  187.   if (name[0] == '.')
  188.     return name[1] == 0 || (name[1] == '.' && name[2] == 0);
  189.   len = strlen ((const char *)name);
  190.   p = (const BYTE *)strchr ((const char *)name, '.');
  191.   if (p == NULL)
  192.     n = len;
  193.   else
  194.     n = p - name;
  195.   if (n > 8)
  196.     return FALSE;
  197.   if (p != NULL)
  198.     {
  199.       if (p[-1] == ' ')         /* (p > name) is guaranteed */
  200.         return FALSE;
  201.       if (len - (n + 1) > 3)
  202.         return FALSE;
  203.       if (strchr ((const char *)p + 1, '.') != NULL)
  204.         return FALSE;
  205.     }
  206.   for (p = name; *p != 0; ++p)
  207.     if (*p < 0x20 || strchr ("\"*+,/;:<=>?[\\]|", *p) != NULL)
  208.       return FALSE;
  209.   return TRUE;
  210. }
  211.  
  212.  
  213. /* Return TRUE iff the file name pointed to by NAME is a valid HPFS
  214.    file name.  "." and ".." are considered invalid by this
  215.    function. */
  216.  
  217. static int is_hpfs_name (const BYTE *name)
  218. {
  219.   const BYTE *p;
  220.  
  221.   for (p = name; *p != 0; ++p)
  222.     if (*p < 0x20 || strchr ("\"*/:<>?\\|", *p) != NULL)
  223.       return FALSE;
  224.   if (p == name)
  225.     return FALSE;
  226.   if (p[-1] == '.' || p[-1] == ' ')
  227.     return FALSE;
  228.   return TRUE;
  229. }
  230.  
  231.  
  232. /* Initialize an EXTENTS structure. */
  233.  
  234. static void extents_init (EXTENTS *e)
  235. {
  236.   e->size = 0;
  237.   e->counts = NULL;
  238. }
  239.  
  240.  
  241. /* Free any resources of an EXTENTS structure. */
  242.  
  243. static void extents_exit (EXTENTS *e)
  244. {
  245.   e->size = 0;
  246.   free (e->counts);
  247.   e->counts = NULL;
  248. }
  249.  
  250.  
  251. /* Add an object to an EXTENTS structure.  The object has COUNT
  252.    extents. */
  253.  
  254. static void extents_stat (EXTENTS *e, ULONG count)
  255. {
  256.   if (count >= e->size)
  257.     {
  258.       ULONG new_size, i;
  259.  
  260.       new_size = (count | 0xff) + 1;
  261.       e->counts = realloc (e->counts, new_size * sizeof (*e->counts));
  262.       for (i = e->size; i < new_size; ++i)
  263.         e->counts[i] = 0;
  264.       e->size = new_size;
  265.     }
  266.   e->counts[count] += 1;
  267. }
  268.  
  269.  
  270. /* Show the statistics collected in an EXTENTS structure. */
  271.  
  272. static void extents_show (const EXTENTS *e, const char *msg)
  273. {
  274.   ULONG i;
  275.  
  276.   info ("\nFragmentation of %s:\n", msg);
  277.   info ("Extents | Number\n");
  278.   info ("--------+-------\n");
  279.   for (i = 0; i < e->size; ++i)
  280.     if (e->counts[i] != 0)
  281.       info ("%7lu | %lu\n", i, e->counts[i]);
  282. }
  283.  
  284.  
  285. /* Bits in seen_vector[]. */
  286.  
  287. #define SEEN_FNODE      0x01
  288. #define SEEN_DIRBLK     0x02
  289. #define SEEN_ALSEC      0x04
  290. #define SEEN_BADLIST    0x08
  291. #define SEEN_CPINFOSEC  0x10
  292.  
  293.  
  294. /* Set and check a `have seen' bit.  Return TRUE if any of the COUNT
  295.    sectors starting at SECNO has been interpreted as WHAT.  MSG is a
  296.    string describing the type of sector.  This function is used to
  297.    avoid infinite loops. */
  298.  
  299. static int have_seen (ULONG secno, ULONG count, BYTE what, const char *msg)
  300. {
  301.   int seen;
  302.   ULONG i;
  303.  
  304.   seen = FALSE;
  305.   for (i = 0; i < count && secno < total_sectors; ++i, ++secno)
  306.     if (seen_vector[secno] & what)
  307.       {
  308.         seen = TRUE;
  309.         warning (1, "Sector #%lu already used for %s", secno, msg);
  310.       }
  311.     else
  312.       seen_vector[secno] |= what;
  313.   return seen;
  314. }
  315.  
  316.  
  317. /* Types of sectors. */
  318.  
  319. #define USE_EMPTY       0
  320. #define USE_SUPER       1
  321. #define USE_SPARE       2
  322. #define USE_BITMAPIND   3
  323. #define USE_BITMAP      4
  324. #define USE_DIRBLKBITMAP 5
  325. #define USE_SPAREDIRBLK 6
  326. #define USE_BANDDIRBLK  7
  327. #define USE_DIRBLK      8
  328. #define USE_FNODE       9
  329. #define USE_SID         10
  330. #define USE_CPINFOSEC   11
  331. #define USE_CPDATASEC   12
  332. #define USE_BAD         13
  333. #define USE_HOTFIXLIST  14
  334. #define USE_HOTFIX      15
  335. #define USE_BADLIST     16
  336. #define USE_FILE        17
  337. #define USE_ALSEC       18
  338. #define USE_EA          19
  339. #define USE_BOOT        20
  340. #define USE_LOADER      21
  341. #define USE_ACL         22
  342.  
  343. /* Return a pointer to a string containing a description of a sector
  344.    type. */
  345.  
  346. static const char *sec_usage (BYTE what)
  347. {
  348.   switch (what)
  349.     {
  350.     case USE_EMPTY:
  351.       return "empty";
  352.     case USE_SUPER:
  353.       return "super block";
  354.     case USE_SPARE:
  355.       return "spare block";
  356.     case USE_BITMAPIND:
  357.       return "bitmap indirect block";
  358.     case USE_BITMAP:
  359.       return "bitmap";
  360.     case USE_DIRBLKBITMAP:
  361.       return "DIRBLK band bitmap";
  362.     case USE_SPAREDIRBLK:
  363.       return "spare DIRBLK";
  364.     case USE_BANDDIRBLK:
  365.       return "DIRBLK band";
  366.     case USE_DIRBLK:
  367.       return "DIRBLK";
  368.     case USE_FNODE:
  369.       return "FNODE";
  370.     case USE_SID:
  371.       return "SID";
  372.     case USE_CPINFOSEC:
  373.       return "code page info";
  374.     case USE_CPDATASEC:
  375.       return "code page data";
  376.     case USE_BAD:
  377.       return "bad sector";
  378.     case USE_HOTFIXLIST:
  379.       return "hotfix list";
  380.     case USE_HOTFIX:
  381.       return "hotfix sector";
  382.     case USE_BADLIST:
  383.       return "bad block list";
  384.     case USE_FILE:
  385.       return "file data";
  386.     case USE_ALSEC:
  387.       return "allocation sector";
  388.     case USE_EA:
  389.       return "extended attributes";
  390.     case USE_BOOT:
  391.       return "boot sector";
  392.     case USE_LOADER:
  393.       return "loader";
  394.     case USE_ACL:
  395.       return "ACL";
  396.     default:
  397.       return "INTERNAL_ERROR";
  398.     }
  399. }
  400.  
  401.  
  402. /* Use COUNT sectors starting at SECNO for WHAT.  PATH points to the
  403.    path name chain for the file or directory and can be NULL for
  404.    sectors not used by a file or directory. */
  405.  
  406. static void use_sectors (ULONG secno, ULONG count, BYTE what,
  407.                          const path_chain *path)
  408. {
  409.   BYTE old;
  410.  
  411.   /* Handle the sectors one by one. */
  412.  
  413.   while (count > 0)
  414.     {
  415.       /* Before indexing any array with SECNO, check if SECNO is
  416.          valid. */
  417.  
  418.       if (secno >= total_sectors)
  419.         {
  420.           if (path == NULL)
  421.             warning (1, "Sector number #%lu (%s) is too big",
  422.                      secno, sec_usage (what));
  423.           else
  424.             warning (1, "Sector number #%lu (%s for \"%s\") is too big",
  425.                      secno, sec_usage (what), format_path_chain (path, NULL));
  426.         }
  427.       else
  428.         {
  429.           /* Check usage of the sector.  A previously unused sector
  430.              (USE_EMPTY) can be turned into any type of sector, Spare
  431.              DIRBLKs and sectors in the DIRBLK band can be turned into
  432.              DIRBLK sectors.  Note that reusing a code page data
  433.              sector as code page data sector is ignored here as
  434.              do_cpdatasec() can be called more than once for a single
  435.              sector. */
  436.  
  437.           old = usage_vector[secno];
  438.           if (old != USE_EMPTY
  439.               && !(what == USE_DIRBLK
  440.                    && (old == USE_SPAREDIRBLK || old == USE_BANDDIRBLK))
  441.               && !(what == USE_CPDATASEC && old == USE_CPDATASEC))
  442.             {
  443.               warning (1, "Sector #%lu usage conflict: %s vs. %s",
  444.                        secno, sec_usage (usage_vector[secno]),
  445.                        sec_usage (what));
  446.  
  447.               /* Display path names, if possible. */
  448.  
  449.               if (path_vector != NULL && path_vector[secno] != NULL)
  450.                 warning_cont ("File 1: \"%s\"",
  451.                               format_path_chain (path_vector[secno], NULL));
  452.               if (path != NULL)
  453.                 warning_cont ("File 2: \"%s\"",
  454.                               format_path_chain (path, NULL));
  455.             }
  456.           else
  457.             {
  458.               usage_vector[secno] = what;
  459.               if (path_vector != NULL)
  460.                 path_vector[secno] = path;
  461.             }
  462.  
  463.           /* Check if the sector is marked as allocated.  Don't check
  464.              if the allocation bitmap has not yet been read completely
  465.              into memory. */
  466.  
  467.           if (alloc_ready && !ALLOCATED (secno))
  468.             {
  469.               warning (1, "Sector #%lu used (%s) but not marked as allocated",
  470.                        secno, sec_usage (what));
  471.               if (path != NULL)
  472.                 warning_cont ("File: \"%s\"",
  473.                               format_path_chain (path, NULL));
  474.             }
  475.         }
  476.       ++secno; --count;
  477.     }
  478. }
  479.  
  480.  
  481. /* Process the bad block list starting in sector SECNO.  The list has
  482.    TOTAL entries. */
  483.  
  484. static void do_bad (DISKIO *d, ULONG secno, ULONG total)
  485. {
  486.   ULONG list[512];
  487.   ULONG rest, used, i, what_index = 0;
  488.  
  489.   /* The bad block list consists of one or more 4-sector blocks.  The
  490.      first 32-bit word of such a block points to the next 4-sector
  491.      block.  The end of the chain is marked by a zero sector number.
  492.      The remaining 511 32-bit words of the blocks contain the sector
  493.      numbers of bad sectors.  Entries which are zero do not point to
  494.      bad sectors. */
  495.  
  496.   used = 0; rest = total;
  497.   while (secno != 0)
  498.     {
  499.       if (a_info)
  500.         {
  501.           info ("Sectors #%lu-#%lu: Bad block list\n", secno, secno+3);
  502.           info ("  Sector number of next bad block: #%lu\n", list[0]);
  503.         }
  504.       else if (a_what && IN_RANGE (what_sector, secno, 4))
  505.         {
  506.           info ("Sector #%lu: Bad block list (+%lu)\n",
  507.                 what_sector, what_sector - secno);
  508.           if (secno + 0 == what_sector)
  509.             info ("  Next sector in list: #%lu\n", list[0]);
  510.           what_index = (secno - what_sector) / 4;
  511.         }
  512.       if (have_seen (secno, 4, SEEN_BADLIST, "bad block list"))
  513.         break;
  514.       use_sectors (secno, 4, USE_BADLIST, NULL);
  515.       read_sec (d, list, secno, 4, TRUE);
  516.       for (i = 1; i < 512 && i <= rest; ++i)
  517.         if (list[i] != 0)
  518.           {
  519.             ++used;
  520.             if (a_info || (a_what && IN_RANGE (i, what_index, 512/4)))
  521.               info ("  Bad sector: #%lu\n", list[i]);
  522.             else if (a_what && list[i] == what_sector)
  523.               info ("Sector #%lu: Bad sector\n", list[i]);
  524.             use_sectors (list[i], 1, USE_BAD, NULL);
  525.           }
  526.       secno = list[0];
  527.       if (rest > 511)
  528.         rest -= 511;
  529.       else
  530.         rest = 0;
  531.     }
  532.   if (rest != 0 || secno != 0)
  533.     warning (1, "Wrong length of bad block list");
  534.   if (used != total)
  535.     warning (1, "Wrong number of bad blocks");
  536. }
  537.  
  538.  
  539. /* Process the hotfix list starting at sector SECNO.  The list has
  540.    TOTAL entries. */
  541.  
  542. static void do_hotfix_list (DISKIO *d, ULONG secno, ULONG total)
  543. {
  544.   ULONG list[512];              /* 4 Sectors */
  545.   ULONG i, hsecno;
  546.  
  547.   if (total > 512 / 3)
  548.     {
  549.       warning (1, "Maximum number of hotfixes is too big");
  550.       total = 512 / 3;
  551.     }
  552.   if (a_info)
  553.     info ("Sectors #%lu-#%lu: Hotfix list\n", secno, secno + 3);
  554.   else if (a_what && IN_RANGE (what_sector, secno, 4))
  555.     info ("Sector #%lu: Hotfix list (+%lu)\n",
  556.           what_sector, what_sector - secno);
  557.   use_sectors (secno, 4, USE_HOTFIXLIST, NULL);
  558.   read_sec (d, list, secno, 4, TRUE);
  559.   for (i = 0; i < total; ++i)
  560.     {
  561.       hsecno = list[i+total];
  562.       if (hsecno == 0)
  563.         warning (1, "Hotfix sector number is zero");
  564.       else if (hsecno >= total_sectors)
  565.         warning (1, "Hotfix sector number #%lu is too big", hsecno);
  566.       else if (usage_vector[hsecno] == USE_EMPTY)
  567.         {
  568.           if (a_info)
  569.             info ("  Hotfix sector: #%lu for #%lu, FNODE #%lu\n",
  570.                   hsecno, list[i], list[i+2*total]);
  571.           if (a_what && hsecno == what_sector)
  572.             info ("Sector #%lu: Hotfix sector for #%lu, FNODE #%lu\n",
  573.                   hsecno, list[i], list[i+2*total]);
  574.           use_sectors (hsecno, 1, USE_HOTFIX, NULL);
  575.           if (alloc_vector != NULL && !ALLOCATED (hsecno))
  576.             warning (1, "Hotfix sector #%lu not marked as allocated", hsecno);
  577.         }
  578.     }
  579.   if (a_what && IN_RANGE (what_sector, secno, 4))
  580.     {
  581.       for (i = 0; i < total; ++i)
  582.         if (list[i] != 0 && what_sector == secno + i / (512 / 4))
  583.           info ("  Bad sector: #%lu\n", list[i]);
  584.       for (i = 0; i < total; ++i)
  585.         if (what_sector == secno + (i + total) / (512 / 4))
  586.           info ("  Hotfix sector: #%lu\n", list[i+total]);
  587.     }
  588. }
  589.  
  590.  
  591. /* One band has 8 MBytes, a run of free sectors may not span more than
  592.    two bands.  Therefore, there are at most 32768 successive free
  593.    sectors. */
  594.  
  595. #define MAX_FREE_SIZE   32768
  596.  
  597. /* Show fragmentation of free space. */
  598.  
  599. static void do_free_frag (void)
  600. {
  601.   ULONG *counts, count, start, end, j, k;
  602.  
  603.   counts = xmalloc (MAX_FREE_SIZE * sizeof (*counts));
  604.   for (j = 0; j < MAX_FREE_SIZE; ++j)
  605.     counts[j] = 0;
  606.   count = 0;
  607.   for (j = 0; j < total_sectors; ++j)
  608.     if (!ALLOCATED (j))
  609.       ++count;
  610.     else if (count != 0)
  611.       {
  612.         if (count < MAX_FREE_SIZE)
  613.           counts[count] += 1;
  614.         count = 0;
  615.       }
  616.   if (count != 0)
  617.     {
  618.       if (count < MAX_FREE_SIZE)
  619.         counts[count] += 1;
  620.     }
  621.   info ("\nFragmentation of free space:\n");
  622.   info ("Fragment size | Number of fragments of that size\n");
  623.   info ("--------------+---------------------------------\n");
  624.   for (j = 0; (1 << j) < MAX_FREE_SIZE; ++j)
  625.     {
  626.       start = 1 << j;
  627.       end = 2 * start;
  628.       if (end > MAX_FREE_SIZE)
  629.         end = MAX_FREE_SIZE;
  630.       count = 0;
  631.       for (k = start; k < end; ++k)
  632.         count += counts[k];
  633.       info (" %5lu-%-5lu  | %lu\n", start, end - 1, count);
  634.     }
  635.   info ("\n");
  636. }
  637.  
  638.  
  639. /* Show a range of COUNT free sectors starting at sector START. */
  640.  
  641. static void do_bitmap_show (ULONG start, ULONG count)
  642. {
  643.   if (count == 1)
  644.     info ("  Unallocated: 1 sector #%lu\n", start);
  645.   else
  646.     info ("  Unallocated: %lu sectors #%lu-#%lu\n",
  647.           count, start, start + count - 1);
  648. }
  649.  
  650.  
  651. /* Show free sectors in the bitmap pointed to by BITMAP.  BASE is the
  652.    number of the first sector mapped by the bitmap.  SIZE is the
  653.    number of bits in the bitmap.  Return the number of free
  654.    sectors. */
  655.  
  656. static ULONG do_bitmap2 (const BYTE *bitmap, ULONG base, ULONG size)
  657. {
  658.   ULONG start, count, j, total;
  659.  
  660.   start = count = total = 0;
  661.   for (j = 0; j < size; ++j)
  662.     if (BITSETP (bitmap, j))
  663.       {
  664.         if (count == 0)
  665.           start = j + base;
  666.         ++count;
  667.       }
  668.     else if (count != 0)
  669.       {
  670.         do_bitmap_show (start, count);
  671.         total += count;
  672.         count = 0;
  673.       }
  674.   if (count != 0)
  675.     {
  676.       do_bitmap_show (start, count);
  677.       total += count;
  678.     }
  679.   return total;
  680. }
  681.  
  682.  
  683. /* Process the bitmap block starting in sector SECNO for band BAND. */
  684.  
  685. static void do_bitmap (DISKIO *d, ULONG secno, ULONG band, int show)
  686. {
  687.   BYTE bitmap[2048];
  688.   ULONG pos, total, first_sec, rel_sec;
  689.  
  690.   if (a_info || show)
  691.     info ("Bitmap for band %lu is in sectors #%lu-#%lu\n",
  692.           band, secno, secno + 3);
  693.   if (a_what && IN_RANGE (what_sector, secno, 4))
  694.     info ("Sector #%lu: Bitmap for band %lu (+%lu)\n",
  695.           what_sector, band, what_sector - secno);
  696.   use_sectors (secno, 4, USE_BITMAP, NULL);
  697.   read_sec (d, bitmap, secno, 4, TRUE);
  698.   pos = band * 2048;
  699.   first_sec = band * 2048 * 8;
  700.   if (a_info || a_check || a_what)
  701.     {
  702.       if (pos + 2048 <= total_alloc)
  703.         memcpy (alloc_vector + pos, bitmap, 2048);
  704.       else if (pos < total_alloc)
  705.         memcpy (alloc_vector + pos, bitmap, total_alloc - pos);
  706.     }
  707.   if (a_info && show_unused)
  708.     {
  709.       total = do_bitmap2 (bitmap, first_sec, 2048 * 8);
  710.       info ("  Unallocated sectors in band %lu: %lu\n", band, total);
  711.     }
  712.   else if (a_what && IN_RANGE (what_sector, secno, 4))
  713.     do_bitmap2 (bitmap + (what_sector - secno) * 512,
  714.                 (band * 2048 + (what_sector - secno) * 512) * 8,
  715.                 512 * 8);
  716.   if (a_what && IN_RANGE (what_sector, first_sec, 2048 * 8))
  717.     {
  718.       rel_sec = what_sector - first_sec;
  719.       info ("Allocation bit for sector #%lu (%s) is in sector #%lu,\n"
  720.             "  byte 0x%lx, bit %lu\n",
  721.             what_sector,
  722.             (BITSETP (bitmap, rel_sec) ? "unallocated" : "allocated"),
  723.             secno + rel_sec / (512 * 8),
  724.             (rel_sec % (512 * 8)) / 8, rel_sec % 8);
  725.     }
  726. }
  727.  
  728.  
  729. /* Process the bitmap indirect block starting in sector SECNO. */
  730.  
  731. static void do_bitmap_indirect (DISKIO *d, ULONG secno)
  732. {
  733.   BYTE bit_count[256];
  734.   ULONG *list;
  735.   ULONG i, bsecno, nfree, resvd, bands, blocks;
  736.  
  737.   bands = DIVIDE_UP (total_sectors, 2048 * 8);
  738.   blocks = DIVIDE_UP (bands, 512);
  739.   if (a_info)
  740.     info ("Sectors #%lu-#%lu: Bitmap indirect block\n",
  741.           secno, secno + 4 * blocks - 1);
  742.   else if (a_what && IN_RANGE (what_sector, secno, 4 * blocks))
  743.     info ("Sector #%lu: Bitmap indirect block (+%lu)\n",
  744.           what_sector, what_sector - secno);
  745.   use_sectors (secno, 4 * blocks, USE_BITMAPIND, NULL);
  746.   list = xmalloc (2048 * blocks);
  747.   read_sec (d, list, secno, 4 * blocks, TRUE);
  748.   for (i = 0; i < bands; ++i)
  749.     {
  750.       bsecno = list[i];
  751.       if (bsecno == 0)
  752.         {
  753.           warning (1, "Bitmap indirect block starting at #%lu: "
  754.                    "Entry %lu is zero", secno, i);
  755.           break;
  756.         }
  757.       do_bitmap (d, bsecno, i,
  758.                  (a_what && what_sector == secno + i / (512 / 4)));
  759.     }
  760.   if (a_check)
  761.     {
  762.       for (i = bands; i < blocks * 512; ++i)
  763.         if (list[i] != 0)
  764.           {
  765.             warning (1, "Bitmap indirect block starting at #%lu: "
  766.                      "Too many entries", secno);
  767.             break;
  768.           }
  769.     }
  770.   free (list); list = NULL;
  771.  
  772.   if (a_check || a_info)
  773.     {
  774.       for (i = 0; i < 256; ++i)
  775.         {
  776.           bit_count[i] = 0;
  777.           if (i & 0x01) ++bit_count[i];
  778.           if (i & 0x02) ++bit_count[i];
  779.           if (i & 0x04) ++bit_count[i];
  780.           if (i & 0x08) ++bit_count[i];
  781.           if (i & 0x10) ++bit_count[i];
  782.           if (i & 0x20) ++bit_count[i];
  783.           if (i & 0x40) ++bit_count[i];
  784.           if (i & 0x80) ++bit_count[i];
  785.         }
  786.       nfree = 0;
  787.       for (i = 0; i < total_alloc; ++i)
  788.         nfree += bit_count[alloc_vector[i]];
  789.       resvd = total_sectors / 50;
  790.       if (resvd > 4096) resvd = 4096;
  791.       if (a_info)
  792.         info ("Number of reserved sectors:    %lu (%lu used)\n",
  793.               resvd, resvd > nfree ? resvd - nfree : 0);
  794.       if (resvd > nfree)
  795.         {
  796.           if (a_check)
  797.             warning (0, "Reserved sectors are in used (%lu)", resvd - nfree);
  798.           resvd = 0;
  799.         }
  800.       if (a_info)
  801.         info ("Number of unallocated sectors: %lu (%lu available)\n",
  802.               nfree, nfree - resvd);
  803.     }
  804.   if (alloc_vector != NULL)
  805.     alloc_ready = TRUE;
  806. }
  807.  
  808.  
  809. /* Compare two file names pointed to by P1 and P2.  CPIDX1 and CPIDX2
  810.    are the code page indices of the files.  If a code page index is
  811.    out of range, the current code page will be used. */
  812.  
  813. static int compare_fname (const BYTE *p1, const BYTE *p2,
  814.                           ULONG cpidx1, ULONG cpidx2)
  815. {
  816.   const BYTE *map1, *map2;
  817.  
  818.   map1 = (cpidx1 >= code_page_count
  819.           ? cur_case_map : code_pages[cpidx1].case_map);
  820.   map2 = (cpidx2 >= code_page_count
  821.           ? cur_case_map : code_pages[cpidx2].case_map);
  822.   for (;;)
  823.     {
  824.       if (*p1 == 0 && *p2 == 0)
  825.         return 0;
  826.       if (*p2 == 0)
  827.         return 1;
  828.       if (*p1 == 0)
  829.         return -1;
  830.       if (map1[*p1] > map2[*p2])
  831.         return 1;
  832.       if (map1[*p1] < map2[*p2])
  833.         return -1;
  834.       ++p1; ++p2;
  835.     }
  836. }
  837.  
  838.  
  839. /* Process one entry of a code page data sector.  The entry is in
  840.    sector SECNO (used for messages, only).  PD points to the code page
  841.    entry.  The length of the entry is LEN bytes.  CS is the expected
  842.    checksum for the entry.  Update our code page entry pointed to by
  843.    DST. */
  844.  
  845. static void do_cpdata (ULONG secno, const CPDATAENTRY *pd, ULONG len,
  846.                        ULONG cs, MYCP *dst)
  847. {
  848.   ULONG rc, i, diffs, cs2;
  849.   COUNTRYCODE cc;
  850.   BYTE map[128], c;
  851.   BYTE dbcs[12];
  852.  
  853.   memcpy (dst->case_map + 128, pd->bCaseMapTable, 128);
  854.   for (c = 0; c < 128; ++c)
  855.     map[c] = (BYTE)(c + 128);
  856.   cc.country = USHORT_FROM_FS (pd->usCountryCode);
  857.   cc.codepage = USHORT_FROM_FS (pd->usCodePageID);
  858.   rc = DosMapCase (128, &cc, (PCHAR)map);
  859.   if (rc == ERROR_NLS_NO_COUNTRY_FILE)
  860.     {
  861.       if (!no_country_sys)
  862.         warning (0, "COUNTRY.SYS not found -- "
  863.                  "cannot check case mapping tables");
  864.       no_country_sys = TRUE;
  865.     }
  866.   if (!no_country_sys)
  867.     {
  868.       if (rc != 0)
  869.         warning (1, "DosMapCase failed for %lu/%lu, rc=%lu",
  870.                  cc.country, cc.codepage, rc);
  871.       else
  872.         {
  873.           diffs = 0;
  874.           for (i = 0; i < 128; ++i)
  875.             if (map[i] != pd->bCaseMapTable[i])
  876.               {
  877.                 ++diffs;
  878.                 dst->case_map_changed[i+128] = TRUE;
  879.               }
  880.  
  881.           /* Don't complain about up to 2 differences in a case
  882.              mapping table unless -p is given. */
  883.  
  884.           if (diffs != 0 && (check_pedantic || diffs > 2))
  885.             warning (diffs > 2 ? 1 : 0,
  886.                      "CPDATASEC #%lu: Case mapping table does not match "
  887.                      "DosMapCase for %lu/%lu (%lu difference%s)",
  888.                      secno, cc.country, cc.codepage, diffs,
  889.                      diffs == 1 ? "" : "s");
  890.         }
  891.       rc = DosQueryDBCSEnv (sizeof (dbcs), &cc, (PCHAR)&dbcs);
  892.       if (rc != 0)
  893.         warning (1, "DosQueryDBCSEnv failed for %lu/%lu, rc=%lu",
  894.                  cc.country, cc.codepage, rc);
  895.       else
  896.         {
  897.           for (i = 0; i < 6; ++i)
  898.             if (dbcs[2*i+0] == 0 && dbcs[2*i+1] == 0)
  899.               break;
  900.           if (i != USHORT_FROM_FS (pd->cDBCSRange))
  901.             warning (1, "CPDATASEC #%lu: Number of DBCS ranges does not match "
  902.                      "DosQueryDBCSEnv for %lu/%lu",
  903.                      secno, cc.country, cc.codepage);
  904.           else if (memcmp (dbcs, (const char *)pd->DBCSRange, 2 * i) != 0)
  905.             warning (1, "CPDATASEC #%lu: DBCS ranges does not match "
  906.                      "DosQueryDBCSEnv for %lu/%lu",
  907.                      secno, cc.country, cc.codepage);
  908.         }
  909.     }
  910.   cs2 = chksum ((const BYTE *)pd, len);
  911.   if (cs != cs2)
  912.     warning (1, "CPDATASEC #%lu: Incorrect checksum for %lu/%lu",
  913.              secno, cc.country, cc.codepage);
  914. }
  915.  
  916.  
  917. /* Process a code page data sector.  DI is an index into
  918.    code_pages[]. */
  919.  
  920. static void do_cpdatasec (DISKIO *d, ULONG di)
  921. {
  922.   ULONG secno, dcount, index, offset, len, j, c;
  923.   HPFS_SECTOR cpdatasec;
  924.   char used[512];
  925.   const CPDATAENTRY *pd;
  926.  
  927.   secno = ULONG_FROM_FS (code_pages[di].info.lsnCPData);
  928.   for (j = 0; j < cpdata_count; ++j)
  929.     if (cpdata_visited[j] == secno)
  930.       return;
  931.   cpdata_visited[cpdata_count++] = secno;
  932.   if (a_info || (a_what && secno == what_sector))
  933.     info ("Sector #%lu: Code page data sector\n", secno);
  934.   use_sectors (secno, 1, USE_CPDATASEC, NULL);
  935.   read_sec (d, &cpdatasec, secno, 1, TRUE);
  936.   if (ULONG_FROM_FS (cpdatasec.cpdatasec.sig) != CPDATA_SIG1)
  937.     {
  938.       warning (1, "CPDATASEC #%lu: Bad signature", secno);
  939.       return;
  940.     }
  941.   dcount = USHORT_FROM_FS (cpdatasec.cpdatasec.cCodePage);
  942.   if (dcount > 3)
  943.     {
  944.       warning (1, "CPDATASEC #%lu: Too many code pages", secno);
  945.       dcount = 3;
  946.     }
  947.   memset (used, FALSE, 512);
  948.   for (j = 0; j < dcount; ++j)
  949.     {
  950.       index = USHORT_FROM_FS (cpdatasec.cpdatasec.iFirstCP) + j;
  951.       if (index >= code_page_count)
  952.         warning (1, "CPDATASEC #%lu: Index too big", secno);
  953.       else
  954.         {
  955.           code_pages[index].hit = TRUE;
  956.           for (c = 0; c < 256; ++c)
  957.             {
  958.               code_pages[index].case_map[c] = (BYTE)c;
  959.               code_pages[index].case_map_changed[c] = FALSE;
  960.             }
  961.           for (c = 'a'; c <= 'z'; ++c)
  962.             code_pages[index].case_map[c] = (BYTE)toupper (c);
  963.           if (ULONG_FROM_FS (cpdatasec.cpdatasec.cksCP[j])
  964.               != ULONG_FROM_FS (code_pages[index].info.cksCP))
  965.             warning (1, "CPDATASEC #%lu: Wrong checksum for code page %lu",
  966.                      secno, index);
  967.           offset = USHORT_FROM_FS (cpdatasec.cpdatasec.offCPData[j]);
  968.           len = sizeof (CPDATAENTRY) - sizeof (DBCSRG);
  969.           if (offset < sizeof (cpdatasec.cpdatasec) || offset + len > 512)
  970.             warning (1, "CPDATASEC #%lu: Invalid offset: %lu", secno, offset);
  971.           else
  972.             {
  973.               pd = (const CPDATAENTRY *)(cpdatasec.raw + offset);
  974.               if (USHORT_FROM_FS (pd->cDBCSRange)
  975.                   != USHORT_FROM_FS (code_pages[index].info.cDBCSRange))
  976.                 warning (1, "CPDATASEC #%lu: Incorrect number of DBCS ranges",
  977.                          secno);
  978.               else
  979.                 {
  980.                   len += ((USHORT_FROM_FS (pd->cDBCSRange) + 1)
  981.                           * sizeof (DBCSRG));
  982.                   if (offset + len > 512)
  983.                     warning (1, "CPDATASEC #%lu: Invalid offset: %lu",
  984.                              secno, offset);
  985.                   else if (memchr (used + offset, TRUE, len) != NULL)
  986.                     warning (1, "CPDATASEC #%lu: Overlapping data", secno);
  987.                   else
  988.                     {
  989.                       memset (used + offset, TRUE, len);
  990.                       do_cpdata (secno, pd, len,
  991.                                  ULONG_FROM_FS (cpdatasec.cpdatasec.cksCP[j]),
  992.                                  &code_pages[index]);
  993.                     }
  994.                 }
  995.             }
  996.         }
  997.     }
  998. }
  999.  
  1000.  
  1001. /* Process one code page information sector.  PSECNO points to an
  1002.    object containing the sector number of the code page information
  1003.    sector.  The object will be modified to contain the sector number
  1004.    of the next code page information sector.  The object pointed to by
  1005.    PCOUNT contains the number of code pages.  The object will be
  1006.    updated.  Return TRUE iff successful. */
  1007.  
  1008. static int do_one_cpinfosec (DISKIO *d, ULONG *psecno, ULONG *pcount)
  1009. {
  1010.   ULONG secno, i, n, next;
  1011.   HPFS_SECTOR cpinfosec;
  1012.   const CPINFOENTRY *pi;
  1013.  
  1014.   secno = *psecno;
  1015.   if (a_info || (a_what && secno == what_sector))
  1016.     info ("Sector #%lu: Code page information sector\n", secno);
  1017.   if (have_seen (secno, 1, SEEN_CPINFOSEC, "code page information"))
  1018.     return FALSE;
  1019.   use_sectors (secno, 1, USE_CPINFOSEC, NULL);
  1020.   read_sec (d, &cpinfosec, secno, 1, TRUE);
  1021.   if (ULONG_FROM_FS (cpinfosec.cpinfosec.sig) != CPINFO_SIG1)
  1022.     {
  1023.       warning (1, "CPINFOSEC #%lu: Bad signature", secno);
  1024.       return FALSE;
  1025.     }
  1026.   if (ULONG_FROM_FS (cpinfosec.cpinfosec.iFirstCP) != *pcount)
  1027.     warning (1, "CPINFOSEC #%lu: Wrong code page index", secno);
  1028.   n = ULONG_FROM_FS (cpinfosec.cpinfosec.cCodePage);
  1029.   if (n > 31)
  1030.     {
  1031.       warning (1, "CPINFOSEC #%lu: Too many code pages", secno);
  1032.       n = 31;
  1033.     }
  1034.   for (i = 0; i < n; ++i)
  1035.     {
  1036.       pi = &cpinfosec.cpinfosec.CPInfoEnt[i];
  1037.       code_pages[*pcount].info = *pi;
  1038.       code_pages[*pcount].hit = FALSE;
  1039.       if (a_info || (a_what && what_sector == secno))
  1040.         info ("  Code page index %lu: code page %u, country %u\n",
  1041.               i, USHORT_FROM_FS (pi->usCodePageID),
  1042.               USHORT_FROM_FS (pi->usCountryCode));
  1043.       if (USHORT_FROM_FS (pi->iCPVol) != *pcount)
  1044.         warning (1, "CPINFOSEC #%lu: Incorrect index", secno);
  1045.       ++*pcount;
  1046.     }
  1047.   next = ULONG_FROM_FS (cpinfosec.cpinfosec.lsnNext);
  1048.   if (next == 0)
  1049.     return FALSE;
  1050.   *psecno = next;
  1051.   return TRUE;
  1052. }
  1053.  
  1054.  
  1055. /* Process the list of code page information sectors starting in
  1056.    sector SECNO. */
  1057.  
  1058. static void do_cpinfosec (DISKIO *d, ULONG secno)
  1059. {
  1060.   ULONG count, i;
  1061.  
  1062.   code_pages = xmalloc (code_page_count * sizeof (MYCP));
  1063.   count = 0;
  1064.   while (do_one_cpinfosec (d, &secno, &count))
  1065.     ;
  1066.   if (count != code_page_count)
  1067.     {
  1068.       warning (1, "Wrong number of code pages in code page information "
  1069.                "sectors");
  1070.       if (count < code_page_count)
  1071.         code_page_count = count;
  1072.     }
  1073.  
  1074.   cpdata_count = 0;
  1075.   cpdata_visited = xmalloc (code_page_count * sizeof (*cpdata_visited));
  1076.   for (i = 0; i < code_page_count; ++i)
  1077.     do_cpdatasec (d, i);
  1078.   free (cpdata_visited); cpdata_visited = NULL;
  1079.  
  1080.   for (i = 0; i < code_page_count; ++i)
  1081.     if (!code_pages[i].hit)
  1082.       warning (1, "No code page data for code page index %lu", i);
  1083.   /* TODO: check for duplicate code pages */
  1084. }
  1085.  
  1086.  
  1087. static void do_fnode (DISKIO *d, ULONG secno, const path_chain *path,
  1088.                       int dir_flag, ULONG parent_fnode, ULONG file_size,
  1089.                       ULONG ea_size, int check_ea_size, int need_eas,
  1090.                       int list);
  1091. static int do_storage (DISKIO *d, ULONG secno, const ALBLK *header,
  1092.                        ULONG leaf_count, const path_chain *path,
  1093.                        ULONG *pexp_file_sec, ULONG *pnext_disk_sec,
  1094.                        ULONG total_sectors, ULONG parent_fnode,
  1095.                        int alsec_level, BYTE what, ULONG *pextents,
  1096.                        int show, ULONG copy_size, BYTE *buf, ULONG buf_size);
  1097. static void do_dirblk (DISKIO *d, ULONG secno, const path_chain *path,
  1098.                        ULONG parent_fnode, ULONG parent, SORT *psort,
  1099.                        int *down_ptr, int level, int *pglobal_dirent_index,
  1100.                        int *pdotdot, int list);
  1101.  
  1102.  
  1103. /* Display a warning message for the DIRBLK starting in sector SECNO.
  1104.    PATH points to the path name chain of the directory. */
  1105.  
  1106. static void dirblk_warning (int level, const char *fmt, ULONG secno,
  1107.                             const path_chain *path, ...)
  1108. {
  1109.   va_list arg_ptr;
  1110.  
  1111.   warning_prolog (level);
  1112.   my_fprintf (diag_file, "DIRBLK #%lu (\"%s\"): ",
  1113.               secno, format_path_chain (path, NULL));
  1114.   va_start (arg_ptr, path);
  1115.   my_vfprintf (diag_file, fmt, arg_ptr);
  1116.   va_end (arg_ptr);
  1117.   fputc ('\n', diag_file);
  1118.   warning_epilog ();
  1119. }
  1120.  
  1121.  
  1122. /* Display a warning message for directory entry number DIRENT_NO of
  1123.    the DIRBLK starting in sector SECNO.  PATH points to the path name
  1124.    chain of the directory, FNAME is the file name in the directory
  1125.    entry. */
  1126.  
  1127. static void dirent_warning (int level, const char *fmt, ULONG secno,
  1128.                             const path_chain *path, int dirent_no,
  1129.                             const char *fname, ...)
  1130. {
  1131.   va_list arg_ptr;
  1132.  
  1133.   warning_prolog (level);
  1134.   my_fprintf (diag_file, "DIRBLK #%lu (\"%s\"): ",
  1135.               secno, format_path_chain (path, NULL));
  1136.   if (fname == NULL)
  1137.     my_fprintf (diag_file, "DIRENT %d: ", dirent_no);
  1138.   else
  1139.     my_fprintf (diag_file, "DIRENT %d (\"%s\"): ", dirent_no, fname);
  1140.   va_start (arg_ptr, fname);
  1141.   my_vfprintf (diag_file, fmt, arg_ptr);
  1142.   va_end (arg_ptr);
  1143.   fputc ('\n', diag_file);
  1144.   warning_epilog ();
  1145. }
  1146.  
  1147.  
  1148. /* Show the directory entry pointed to by P.  Indent with INDENT
  1149.    spaces. */
  1150.  
  1151. static void show_dirent (const DIRENT *p, int indent)
  1152. {
  1153.   ULONG cpindex, length, gap_size, ace_size, i;
  1154.   const BYTE *pb;
  1155.  
  1156.   length = USHORT_FROM_FS (p->cchThisEntry);
  1157.   infoi ("Length:                      %lu\n", indent, length);
  1158.   infoi ("Flags:                       0x%.2x", indent, p->bFlags);
  1159.   if (p->bFlags & DF_SPEC) info (" ..");
  1160.   if (p->bFlags & DF_END) info (" end");
  1161.   if (p->bFlags & DF_ATTR) info (" EA");
  1162.   if (p->bFlags & DF_NEEDEAS) info (" need-EA");
  1163.   if (p->bFlags & DF_PERM) info (" perms");
  1164.   if (p->bFlags & DF_ACL) info (" ACL");
  1165.   if (p->bFlags & DF_XACL) info (" explicit-ACL");
  1166.   info ("\n");
  1167.   if (!(p->bFlags & DF_END))
  1168.     {
  1169.       infoi ("Attributes:                  0x%.2x", indent, p->bAttr);
  1170.       if (p->bAttr & ATTR_DIR)
  1171.         info (" dir");
  1172.       if (p->bAttr & ATTR_READONLY)
  1173.         info (" r/o");
  1174.       if (p->bAttr & ATTR_HIDDEN)
  1175.         info (" hidden");
  1176.       if (p->bAttr & ATTR_SYSTEM)
  1177.         info (" system");
  1178.       if (p->bAttr & ATTR_LABEL)
  1179.         info (" label");
  1180.       if (p->bAttr & ATTR_ARCHIVED)
  1181.         info (" arch");
  1182.       if (p->bAttr & ATTR_NONFAT)
  1183.         info (" non-FAT");
  1184.       info ("\n");
  1185.       infoi ("FNODE:                       #%lu\n", indent,
  1186.              ULONG_FROM_FS (p->lsnFNode));
  1187.       infoi ("Time of creation:            %s\n", indent,
  1188.              format_time (ULONG_FROM_FS (p->timCreate)));
  1189.       infoi ("Time of last modification:   %s\n", indent,
  1190.              format_time (ULONG_FROM_FS (p->timLastMod)));
  1191.       infoi ("Time of last access:         %s\n", indent,
  1192.              format_time (ULONG_FROM_FS (p->timLastAccess)));
  1193.       infoi ("Size of file:                %lu\n", indent,
  1194.              ULONG_FROM_FS (p->cchFSize));
  1195.       infoi ("Size of extended attributes: %lu\n", indent,
  1196.              ULONG_FROM_FS (p->ulEALen));
  1197.       infoi ("Number of ACEs:              %u\n", indent, p->bFlex & 7);
  1198.       cpindex = p->bCodePage & 0x7f;
  1199.       if (cpindex >= code_page_count)
  1200.         infoi ("Code page index:             %lu\n", indent, cpindex);
  1201.       else
  1202.         infoi ("Code page:                   %u\n", indent,
  1203.                USHORT_FROM_FS (code_pages[cpindex].info.usCodePageID));
  1204.       if (p->bCodePage & 0x80)
  1205.         infoi ("Name contains DBCS characters\n", indent);
  1206.       if (p->bFlags & DF_ACL)
  1207.         {
  1208.           gap_size = length - (sizeof (DIRENT) + p->cchName - 1);
  1209.           if (p->bFlags & DF_BTP)
  1210.             gap_size -= sizeof (ULONG);
  1211.           ace_size = (p->bFlex & 7) * 4;
  1212.           pb = p->bName + p->cchName;
  1213.           for (i = 0; i < gap_size; ++i)
  1214.             {
  1215.               if (i % 16 == 0)
  1216.                 {
  1217.                   if (i != 0)
  1218.                     info ("\n");
  1219.                   infoi ("ACE data:                   ", indent);
  1220.                 }
  1221.               info ("%c%.2x",
  1222.                     i == ace_size ? '|' : i > 0 && i % 4 == 0 ? '.' : ' ',
  1223.                     pb[i]);
  1224.             }
  1225.           if (gap_size != 0)
  1226.             info ("\n");
  1227.         }
  1228.     }
  1229.   if (p->bFlags & DF_BTP)
  1230.     infoi ("Down pointer:                #%lu\n", indent,
  1231.            ((ULONG *)((const char *)p + length))[-1]);
  1232. }
  1233.  
  1234.  
  1235. /* Show the directory entry pointed to by P, formatted for the `dir'
  1236.    action. */
  1237.  
  1238. static void show_dir (const DIRENT *p, const char *name)
  1239. {
  1240.   info ("%s ", format_dir_time (ULONG_FROM_FS (p->timLastMod)));
  1241.   if (p->bAttr & ATTR_DIR)
  1242.     info ("     <DIR>      ");
  1243.   else
  1244.     info ("%10lu %c%c%c%c%c",
  1245.           ULONG_FROM_FS (p->cchFSize),
  1246.           (p->bAttr & ATTR_READONLY) ? 'R' : '-',
  1247.           (p->bAttr & ATTR_HIDDEN)   ? 'H' : '-',
  1248.           (p->bAttr & ATTR_SYSTEM)   ? 'S' : '-',
  1249.           (p->bAttr & ATTR_LABEL)    ? 'V' : '-',
  1250.           (p->bAttr & ATTR_ARCHIVED) ? 'A' : '-');
  1251.   info (" \"%s\"\n", name);
  1252. }
  1253.  
  1254.  
  1255. /* Perform basic checks for the DIRENT at offset POS of the DIRBLK
  1256.    pointed to by PDIR.  Return a pointer to the DIRENT if it looks
  1257.    reasonably good.  Return NULL if it looks bad.  Store the name to
  1258.    NAME, which points to an array of 256 characters.  Print warnings
  1259.    if WARN is TRUE.  The remaining arguments are passed to
  1260.    dirent_warning(). */
  1261.  
  1262. const DIRENT *check_dirent (const DIRBLK *pdir, size_t pos, char *name,
  1263.                             int warn, ULONG secno, const path_chain *path,
  1264.                             int index)
  1265. {
  1266.   const DIRENT *p;
  1267.   ULONG length;
  1268.  
  1269.   *name = 0;
  1270.   p = (const DIRENT *)((const char *)pdir + pos);
  1271.   if (pos + sizeof (DIRENT) > 2048)
  1272.     {
  1273.       if (warn)
  1274.         dirent_warning (1, "Extends beyond end of DIRBLK",
  1275.                         secno, path, index, NULL);
  1276.       return NULL;
  1277.     }
  1278.   length = USHORT_FROM_FS (p->cchThisEntry);
  1279.   if (pos + length > 2048)
  1280.     {
  1281.       if (warn)
  1282.         dirent_warning (1, "Extends beyond end of DIRBLK",
  1283.                         secno, path, index, NULL);
  1284.       return NULL;
  1285.     }
  1286.   if (length < sizeof (DIRENT))
  1287.     {
  1288.       if (warn)
  1289.         dirent_warning (1, "Length too small (case 1)",
  1290.                         secno, path, index, NULL);
  1291.       return NULL;
  1292.     }
  1293.   if (length < (ROUND_UP (sizeof (DIRENT) + p->cchName - 1, 4)
  1294.                 + ((p->bFlags & DF_BTP) ? sizeof (ULONG) : 0)))
  1295.     {
  1296.       if (warn)
  1297.         dirent_warning (1, "Length too small (case 2)",
  1298.                         secno, path, index, NULL);
  1299.       return NULL;
  1300.     }
  1301.   if (length & 3)
  1302.     {
  1303.       if (warn)
  1304.         dirent_warning (1, "Length is not a multiple of 4",
  1305.                         secno, path, index, NULL);
  1306.       return NULL;
  1307.     }
  1308.  
  1309.   if (p->bFlags & DF_END)
  1310.     {
  1311.       strcpy (name, "[END]");
  1312.       if (a_check && (p->cchName != 1 || memcmp (p->bName, "\377", 1) != 0))
  1313.         dirent_warning (0, "Wrong name for end entry",
  1314.                         secno, path, index, NULL);
  1315. #if 0
  1316.       /* This error(?) seems to be quite normal with HPFS386. */
  1317.       if (a_check && (p->bFlags & DF_SPEC))
  1318.         dirent_warning (1, "Both DF_END and DF_SPEC are set",
  1319.                         secno, path, index, NULL);
  1320. #endif
  1321.     }
  1322.   else if (p->bFlags & DF_SPEC)
  1323.     {
  1324.       strcpy (name, "..");
  1325.       if (a_check && (p->cchName != 2 || memcmp (p->bName, "\1\1", 2) != 0))
  1326.         dirent_warning (0, "Wrong name for \"..\" entry",
  1327.                         secno, path, index, NULL);
  1328.     }
  1329.   else
  1330.     {
  1331.       memcpy (name, p->bName, p->cchName);
  1332.       name[p->cchName] = 0;
  1333.     }
  1334.  
  1335.   return p;
  1336. }
  1337.  
  1338.  
  1339. /* Show DIRENTs for the `info <number>' action. */
  1340.  
  1341. static void do_dirblk_what (DISKIO *d, const DIRBLK *pdir, ULONG secno,
  1342.                             const path_chain *path)
  1343. {
  1344.   const DIRENT *p;
  1345.   char name[256];
  1346.   int dirent_index;
  1347.   size_t pos;
  1348.   ULONG length;
  1349.  
  1350.   if (what_sector == secno)
  1351.     {
  1352.       info ("  Change count(?):           %lu\n",
  1353.             ULONG_FROM_FS (pdir->dirblk.culChange) >> 1);
  1354.       info ("  Offset to first free byte: 0x%lx\n",
  1355.             ULONG_FROM_FS (pdir->dirblk.offulFirstFree));
  1356.       info ("  Pointer to parent:         #%lu\n",
  1357.             ULONG_FROM_FS (pdir->dirblk.lsnParent));
  1358.       info ("  Pointer to this directory: #%lu\n",
  1359.             ULONG_FROM_FS (pdir->dirblk.lsnThisDir));
  1360.     }
  1361.  
  1362.   pos = offsetof (DIRBLK, dirblk.dirent);
  1363.   for (dirent_index = 0;; ++dirent_index)
  1364.     {
  1365.       p = check_dirent (pdir, pos, name, FALSE, secno, path, dirent_index);
  1366.       if (p == NULL)
  1367.         break;
  1368.       length = USHORT_FROM_FS (p->cchThisEntry);
  1369.       if (secno + pos / 512 <= what_sector
  1370.           && secno + (pos + length - 1) / 512 >= what_sector)
  1371.         {
  1372.           info ("  ");
  1373.           if (secno + pos / 512 != what_sector
  1374.               || secno + (pos + length - 1) / 512 != what_sector)
  1375.             info ("Partial ");
  1376.           info ("DIRENT %d (offset 0x%lx):\n", dirent_index, pos);
  1377.           info ("    Name: \"%s\"\n", name);
  1378.           show_dirent (p, 4);
  1379.         }
  1380.       pos += length;
  1381.       if (p->bFlags & DF_END)
  1382.         break;
  1383.     }
  1384. }
  1385.  
  1386.  
  1387. /* Handle actions which search for a file: Search the DIRBLK for the
  1388.    current path name component.  If found, either show information
  1389.    about the path name (for `info <path>') or recurse. */
  1390.  
  1391. static void do_dirblk_find (DISKIO *d, const DIRBLK *pdir, ULONG secno,
  1392.                             const path_chain *path, ULONG parent_fnode)
  1393. {
  1394.   const DIRENT *p;
  1395.   char name[256];
  1396.   int dirent_index, cmp, list;
  1397.   ULONG cpindex, length;
  1398.   size_t pos;
  1399.   path_chain link, *plink;
  1400.  
  1401.   pos = offsetof (DIRBLK, dirblk.dirent);
  1402.   for (dirent_index = 0;; ++dirent_index)
  1403.     {
  1404.       p = check_dirent (pdir, pos, name, TRUE, secno, path, dirent_index);
  1405.       if (p == NULL)
  1406.         break;
  1407.  
  1408.       length = USHORT_FROM_FS (p->cchThisEntry);
  1409.       if (p->bFlags & DF_END)
  1410.         cmp = 1;
  1411.       else if (p->bFlags & DF_SPEC)
  1412.         cmp = -1;
  1413.       else
  1414.         {
  1415.           cpindex = p->bCodePage & 0x7f;
  1416.           cmp = compare_fname ((const BYTE *)name,
  1417.                                (const BYTE *)find_comp,
  1418.                                cpindex, code_page_count);
  1419.         }
  1420.  
  1421.       if (cmp < 0)
  1422.         {
  1423.           /* The DIRENT's name is less than the name we are looking
  1424.              for.  Examine the next DIRENT.  This is the only case in
  1425.              which the loop is continued. */
  1426.  
  1427.           pos += length;
  1428.         }
  1429.       else if (cmp == 0)
  1430.         {
  1431.           /* We found the name.  If it's the last component of the
  1432.              path name we are looking for, show information about the
  1433.              DIRENT. */
  1434.  
  1435.           if (*find_path == 0)
  1436.             {
  1437.               if (a_where)
  1438.                 {
  1439.                   info ("Directory entry %d of DIRBLK #%lu+%lu (#%lu)\n",
  1440.                         dirent_index, secno, pos / 512, secno + pos / 512);
  1441.                   show_dirent (p, 2);
  1442.                 }
  1443.               list = FALSE;
  1444.               if (a_dir)
  1445.                 {
  1446.                   if (p->bAttr & ATTR_DIR)
  1447.                     list = TRUE;
  1448.                   else
  1449.                     {
  1450.                       show_dir (p, name);
  1451.                       quit (0, FALSE);
  1452.                     }
  1453.                 }
  1454.               if (!(p->bFlags & DF_SPEC))
  1455.                 {
  1456.                   plink = PATH_CHAIN_NEW (&link, path, name);
  1457.                   do_fnode (d, ULONG_FROM_FS (p->lsnFNode), plink,
  1458.                             (p->bAttr & ATTR_DIR), parent_fnode,
  1459.                             ULONG_FROM_FS (p->cchFSize),
  1460.                             ULONG_FROM_FS (p->ulEALen), TRUE,
  1461.                             p->bFlags & DF_NEEDEAS, list);
  1462.                 }
  1463.               quit (0, FALSE);
  1464.             }
  1465.  
  1466.           /* There is at least one more component in the path name, so
  1467.              this DIRENT must be a directory. */
  1468.  
  1469.           if (!(p->bAttr & ATTR_DIR))
  1470.             error ("\"%s\" is not a directory",
  1471.                    format_path_chain (path, name));
  1472.  
  1473.           /* Recurse into the directory. */
  1474.  
  1475.           plink = PATH_CHAIN_NEW (&link, path, name);
  1476.           do_fnode (d, ULONG_FROM_FS (p->lsnFNode), plink, TRUE,
  1477.                     parent_fnode, 0, ULONG_FROM_FS (p->ulEALen), TRUE,
  1478.                     p->bFlags & DF_NEEDEAS, FALSE);
  1479.           return;
  1480.         }
  1481.       else
  1482.         {
  1483.           /* The DIRENT's name is greater than the name we are looking
  1484.              for.  If there is a down pointer, recurse into the next
  1485.              level of the B-tree.  Otherwise, there is no such
  1486.              name. */
  1487.  
  1488.           if (!(p->bFlags & DF_BTP))
  1489.             break;
  1490.           do_dirblk (d, ((ULONG *)((const char *)p + length))[-1],
  1491.                      path, parent_fnode, secno,
  1492.                      NULL, NULL, 0, NULL, NULL, FALSE);
  1493.           return;
  1494.         }
  1495.     }
  1496.   error ("\"%s\" not found in \"%s\"",
  1497.          find_comp, format_path_chain (path, NULL));
  1498. }
  1499.  
  1500.  
  1501. #define MAX_DIRBLK_LEVELS       32
  1502.  
  1503. /* Check the down pointer of a DIRENT.  INDEX is the index of the
  1504.    DIRENT, FLAG is zero for a leaf, one for a node.  See do_dirblk()
  1505.    for the remaining arguments. */
  1506.  
  1507. static void check_dirent_down (int *down_ptr, int level, ULONG secno,
  1508.                                const path_chain *path, int index, int flag)
  1509. {
  1510.   if (level < MAX_DIRBLK_LEVELS)
  1511.     {
  1512.       if (down_ptr[level] == -1)
  1513.         down_ptr[level] = flag;
  1514.       else if (down_ptr[level] != flag)
  1515.         dirent_warning (1, "%s down pointer",
  1516.                         secno, path, index, NULL,
  1517.                         flag == 0 ? "Undesired" : "Missing");
  1518.     }
  1519. }
  1520.  
  1521.  
  1522. /* Recurse into the next DIRBLK level for `check' action etc.  See
  1523.    do_dirbkl() for a description of the arguments. */
  1524.  
  1525. static void do_dirblk_recurse (DISKIO *d, const DIRBLK *pdir, ULONG secno,
  1526.                                const path_chain *path, ULONG parent_fnode,
  1527.                                ULONG parent, SORT *psort, int *down_ptr,
  1528.                                int level, int *pglobal_dirent_index,
  1529.                                int *pdotdot, int list)
  1530. {
  1531.   const DIRENT *p;
  1532.   const char *pname;
  1533.   char name[256];
  1534.   int dirent_index;
  1535.   ULONG cpindex, length;
  1536.   size_t pos;
  1537.   path_chain link, *plink;
  1538.  
  1539.   pos = offsetof (DIRBLK, dirblk.dirent);
  1540.   for (dirent_index = 0;; ++dirent_index)
  1541.     {
  1542.       p = check_dirent (pdir, pos, name, TRUE, secno, path, dirent_index);
  1543.       if (p == NULL)
  1544.         break;
  1545.       length = USHORT_FROM_FS (p->cchThisEntry);
  1546.       if (p->bFlags & DF_BTP)
  1547.         {
  1548.           do_dirblk (d, ((ULONG *)((const char *)p + length))[-1],
  1549.                      path, parent_fnode, secno, psort,
  1550.                      down_ptr, level + 1, pglobal_dirent_index, pdotdot,
  1551.                      list);
  1552.           check_dirent_down (down_ptr, level, secno, path,
  1553.                              dirent_index, 1);
  1554.         }
  1555.       else
  1556.         check_dirent_down (down_ptr, level, secno, path, dirent_index, 0);
  1557.       if (!(p->bFlags & DF_END))
  1558.         {
  1559.           if (p->bFlags & DF_SPEC)
  1560.             {
  1561.               pname = "";
  1562.               if (*pdotdot)
  1563.                 dirent_warning (1, "More than one \"..\" entry",
  1564.                                 secno, path, dirent_index, name);
  1565.               else if (*pglobal_dirent_index != 0)
  1566.                 dirent_warning (1, "\"..\" entry is not the first entry",
  1567.                                 secno, path, dirent_index, name);
  1568.               *pdotdot = TRUE;
  1569.             }
  1570.           else
  1571.               pname = name;
  1572.           if (verbose)
  1573.             my_fprintf (prog_file, "%s\n", format_path_chain (path, name));
  1574.           if (a_check && strlen (name) + path_chain_len (path) > 255)
  1575.             dirent_warning (1, "Path name too long",
  1576.                             secno, path, dirent_index, name);
  1577.           cpindex = p->bCodePage & 0x7f;
  1578.           if (cpindex >= code_page_count)
  1579.             dirent_warning (1, "Code page index too big",
  1580.                             secno, path, dirent_index, name);
  1581.           else if (check_pedantic)
  1582.             {
  1583.               const BYTE *s;
  1584.               int changed;
  1585.  
  1586.               changed = FALSE;
  1587.               for (s = (const BYTE *)pname; *s != 0; ++s)
  1588.                 if (code_pages[cpindex].case_map_changed[*s])
  1589.                   changed = TRUE;
  1590.               if (changed)
  1591.                 dirent_warning (0, "Case mapping changed",
  1592.                                 secno, path, dirent_index, name);
  1593.             }
  1594.           if (compare_fname (psort->name, (const BYTE *)pname,
  1595.                              psort->cpindex, cpindex) > 0)
  1596.             dirent_warning (1, "File names are not in ascending order "
  1597.                             "(\"%s\" vs \"%s\")",
  1598.                             secno, path, dirent_index, NULL,
  1599.                             psort->name, pname);
  1600.           strcpy ((char *)psort->name, pname);
  1601.           psort->cpindex = cpindex;
  1602.           if (a_check)
  1603.             {
  1604.               ULONG t, gap, ace_size, temp_size;
  1605.  
  1606.               t = ULONG_FROM_FS (p->timLastMod);
  1607.               if (t != 0 && t < min_time)
  1608.                 dirent_warning (1, "Modification time is out of range (%lu)",
  1609.                                 secno, path, dirent_index, name, t);
  1610.               t = ULONG_FROM_FS (p->timLastAccess);
  1611.               if (t != 0 && t < min_time)
  1612.                 dirent_warning (1, "Access time is out of range (%lu)",
  1613.                                 secno, path, dirent_index, name, t);
  1614.               t = ULONG_FROM_FS (p->timCreate);
  1615.               if (t != 0 && t < min_time)
  1616.                 dirent_warning (1, "Creation time is out of range (%lu)",
  1617.                                 secno, path, dirent_index, name, t);
  1618.  
  1619.               if (!(p->bFlags & DF_SPEC))
  1620.                 {
  1621.                   if (!is_hpfs_name ((const BYTE *)name))
  1622.                     dirent_warning (1, "Invalid character in file name",
  1623.                                     secno, path, dirent_index, name);
  1624.                   else if (!is_fat_name ((const BYTE *)name)
  1625.                            != !!(p->bAttr & ATTR_NONFAT))
  1626.                     dirent_warning (1, "Incorrect FAT compatibility bit",
  1627.                                     secno, path, dirent_index, name);
  1628.                 }
  1629.               if (p->bAttr & (0x80 | ATTR_LABEL))
  1630.                 dirent_warning (0, "Undefined attribute bit is set",
  1631.                                 secno, path, dirent_index, name);
  1632.  
  1633.               /* The following ACL stuff is based on a few examples;
  1634.                  the meaning of all this is quite unknown. */
  1635.  
  1636.               if (p->bFlags & DF_PERM)
  1637.                 dirent_warning (0, "DF_PERM bit is set -- meaning unknown",
  1638.                                 secno, path, dirent_index, name);
  1639.               if ((p->bFlags & (DF_ACL|DF_XACL)) == DF_XACL)
  1640.                 dirent_warning (0, "DF_XACL is set without DF_ACL",
  1641.                                 secno, path, dirent_index, name);
  1642.               gap = USHORT_FROM_FS (p->cchThisEntry);
  1643.               gap -= (sizeof (DIRENT) + p->cchName - 1);
  1644.               if (p->bFlags & DF_BTP)
  1645.                 gap -= sizeof (ULONG);
  1646.               if (gap > 3 && !(p->bFlags & DF_ACL))
  1647.                 dirent_warning (0, "DF_ACL should be set "
  1648.                                 "(up to %lu bytes of ACEs)",
  1649.                                 secno, path, dirent_index, name, gap);
  1650.               if ((p->bFlex & 7) && !(p->bFlags & DF_ACL))
  1651.                 dirent_warning (0, "DF_ACL should be set (ACE count: %u)",
  1652.                                 secno, path, dirent_index, name, p->bFlex & 7);
  1653.               ace_size = (p->bFlex & 7) * 4;
  1654.               temp_size = ROUND_UP (sizeof (DIRENT) + p->cchName - 1
  1655.                                     + ace_size, 4);
  1656.               if (p->bFlags & DF_BTP)
  1657.                 temp_size += sizeof (ULONG);
  1658.               if (temp_size != USHORT_FROM_FS (p->cchThisEntry))
  1659.                 dirent_warning (0, "ACE count/size mismatch (%u/%lu)",
  1660.                                 secno, path, dirent_index, name,
  1661.                                 p->bFlex & 7, gap);
  1662.               if (p->bFlex & ~7)
  1663.                 dirent_warning (0, "Bits with unknown meaning are set "
  1664.                                 "in bFlex (0x%.2x)",
  1665.                                 secno, path, dirent_index, name,
  1666.                                 p->bFlex & ~7);
  1667.             }
  1668.  
  1669.           if (list)
  1670.             show_dir (p, name);
  1671.           else if (!(p->bFlags & DF_SPEC))
  1672.             {
  1673.               plink = PATH_CHAIN_NEW (&link, path, name);
  1674.               do_fnode (d, ULONG_FROM_FS (p->lsnFNode), plink,
  1675.                         (p->bAttr & ATTR_DIR), parent_fnode,
  1676.                         ULONG_FROM_FS (p->cchFSize),
  1677.                         ULONG_FROM_FS (p->ulEALen), TRUE,
  1678.                         p->bFlags & DF_NEEDEAS, list);
  1679.             }
  1680.         }
  1681.  
  1682.       pos += length;
  1683.       if (p->bFlags & DF_END)
  1684.         break;
  1685.       *pglobal_dirent_index += 1;
  1686.     }
  1687.   if (pos != ULONG_FROM_FS (pdir->dirblk.offulFirstFree))
  1688.     dirblk_warning (1, "Wrong offset to first free byte", secno, path);
  1689. }
  1690.  
  1691.  
  1692. /* Process the DIRBLK starting in sector SECNO.  PATH points to the
  1693.    path name chain of the file or directory.  PARENT_FNODE is the
  1694.    sector number of the FNODE of the directory containing the DIRBLK.
  1695.    PARENT is the sector number of the parent DIRBLK or (for a DIRBLK
  1696.    in the top level) of the directory's FNODE.  PATH points to the
  1697.    path name chain of the directory.  PSORT points to a structure
  1698.    containing a file name which must compare less than (or equal to,
  1699.    for "") the first DIRENT of the current DIRBLK sub-tree.  The
  1700.    structure will be updated to contain the file name of the last
  1701.    DIRENT of this DIRBLK.  DOWN_PTR points to an array recording
  1702.    down-pointer usage.  The array has LEVEL entries.
  1703.    PGLOBAL_DIRENT_INDEX points to an object containing the index of
  1704.    the first DIRENT entry of this DIRBLK sub-tree, relative to the
  1705.    directory.  The object will be updated.  PDOTDOT points to an
  1706.    object recording presence of the ".." entry.  The object will be
  1707.    updated.  List the directory if LIST is true. */
  1708.  
  1709. static void do_dirblk (DISKIO *d, ULONG secno, const path_chain *path,
  1710.                        ULONG parent_fnode, ULONG parent, SORT *psort,
  1711.                        int *down_ptr, int level, int *pglobal_dirent_index,
  1712.                        int *pdotdot, int list)
  1713. {
  1714.   DIRBLK dir;
  1715.  
  1716.   if (a_what && IN_RANGE (what_sector, secno, 4))
  1717.     info ("Sector #%lu: DIRBLK of \"%s\" (+%lu)\n",
  1718.           what_sector, format_path_chain (path, NULL), what_sector - secno);
  1719.   if (have_seen (secno, 4, SEEN_DIRBLK, "DIRBLK"))
  1720.     return;
  1721.   use_sectors (secno, 4, USE_DIRBLK, path);
  1722.   if (secno & 3)
  1723.     dirblk_warning (1, "Sector number is not a multiple of 4", secno, path);
  1724.   read_sec (d, &dir, secno, 4, TRUE);
  1725.   if (ULONG_FROM_FS (dir.dirblk.sig) != DIRBLK_SIG1)
  1726.     {
  1727.       dirblk_warning (1, "Bad signature", secno, path);
  1728.       return;
  1729.     }
  1730.   ++dirblk_total;
  1731.   if (secno < dirband_start || secno > dirband_end)
  1732.     ++dirblk_outside;
  1733.   if (ULONG_FROM_FS (dir.dirblk.lsnThisDir) != secno)
  1734.     dirblk_warning (1, "Wrong self pointer", secno, path);
  1735.   if (ULONG_FROM_FS (dir.dirblk.lsnParent) != parent)
  1736.     dirblk_warning (1, "Wrong parent pointer", secno, path);
  1737.   if (a_check)
  1738.     {
  1739.       if (!(ULONG_FROM_FS (dir.dirblk.culChange) & 1) != (level != 0))
  1740.         dirblk_warning (1, "`top-most' bit is incorrect", secno, path);
  1741.     }
  1742.  
  1743.   /* Show DIRENTs for the `info <number>' action. */
  1744.  
  1745.   if (a_what && IN_RANGE (what_sector, secno, 4))
  1746.     do_dirblk_what (d, &dir, secno, path);
  1747.  
  1748.   /* Handle action which search for a file. */
  1749.  
  1750.   if (a_find && !list)
  1751.     {
  1752.       do_dirblk_find (d, &dir, secno, path, parent_fnode);
  1753.       return;
  1754.     }
  1755.  
  1756.   /* Recurse into the next level of the B-tree. */
  1757.  
  1758.   do_dirblk_recurse (d, &dir, secno, path, parent_fnode, parent, psort,
  1759.                      down_ptr, level, pglobal_dirent_index, pdotdot, list);
  1760. }
  1761.  
  1762.  
  1763. /* Display a warning message for the ALSEC in sector SECNO.  PATH
  1764.    points to the path name chain of the file or directory. */
  1765.  
  1766. static void alsec_warning (int level, const char *fmt, ULONG secno,
  1767.                            const path_chain *path, ...)
  1768. {
  1769.   va_list arg_ptr;
  1770.  
  1771.   warning_prolog (level);
  1772.   my_fprintf (diag_file, "ALSEC #%lu (\"%s\"): ",
  1773.               secno, format_path_chain (path, NULL));
  1774.   va_start (arg_ptr, path);
  1775.   my_vfprintf (diag_file, fmt, arg_ptr);
  1776.   va_end (arg_ptr);
  1777.   fputc ('\n', diag_file);
  1778.   warning_epilog ();
  1779. }
  1780.  
  1781.  
  1782. /* Process the ALSEC in sector SECNO.  PATH points to the path name
  1783.    chain of the file or directory.  PEXP_FILE_SEC points to an object
  1784.    containing the first logical sector number mapped by the ALSEC.
  1785.    The object's value will be incremented by the number of sectors
  1786.    mapped by the ALSEC.  The sector number of the sector following the
  1787.    previous run is passed in the object pointed to by PNEXT_DISK_SEC.
  1788.    The object will be updated to contain the sector number of the
  1789.    sector following the last run.  TOTAL_SECTORS is the number of
  1790.    sectors expected for the object.  PARENT_FNODE is the sector number
  1791.    of the FNODE of the directory containing the object.  PARENT_ALBLK
  1792.    is the sector number of the parent ALSEC or FNODE.  ALSEC_LEVEL
  1793.    contains the current level in the ALSEC tree.  WHAT is USE_FILE,
  1794.    USE_EA, or USE_ACL for file data, extended attributes, or ACL,
  1795.    respectively.  PEXTENTS points to an object counting the number of
  1796.    extents.  The value of that object will be updated.  Show
  1797.    information if SHOW is non-zero.  Copy up to COPY_SIZE bytes of
  1798.    data to the save file.  Read sectors to BUF (of BUF_SIZE bytes) if
  1799.    BUF is non-NULL.
  1800.  
  1801.    Return the height of the ALSEC sub-tree. */
  1802.  
  1803. static int do_alsec (DISKIO *d, ULONG secno, const path_chain *path,
  1804.                      ULONG *pexp_file_sec, ULONG *pnext_disk_sec,
  1805.                      ULONG total_sectors, ULONG parent_fnode,
  1806.                      ULONG parent_alblk, int alsec_level, BYTE what,
  1807.                      ULONG *pextents, int show, ULONG copy_size,
  1808.                      BYTE *buf, ULONG buf_size)
  1809. {
  1810.   HPFS_SECTOR alsec;
  1811.   int height;
  1812.  
  1813.   if (show)
  1814.     info ("ALSEC(%s): #%lu\n", alsec_number, secno);
  1815.   if (a_what && secno == what_sector)
  1816.     info ("Sector #%lu: Allocation sector (ALSEC) for \"%s\"\n",
  1817.           secno, format_path_chain (path, NULL));
  1818.   if (have_seen (secno, 1, SEEN_ALSEC, "ALSEC"))
  1819.     return 1;
  1820.   use_sectors (secno, 1, USE_ALSEC, path);
  1821.   read_sec (d, &alsec, secno, 1, TRUE);
  1822.   if (ULONG_FROM_FS (alsec.alsec.sig) != ALSEC_SIG1)
  1823.     {
  1824.       alsec_warning (1, "Bad signature", secno, path);
  1825.       return 1;
  1826.     }
  1827.   ++alsec_count;
  1828.   if (ULONG_FROM_FS (alsec.alsec.lsnSelf) != secno)
  1829.     alsec_warning (1, "Incorrect self pointer", secno, path);
  1830.   if (ULONG_FROM_FS (alsec.alsec.lsnRent) != parent_alblk)
  1831.     alsec_warning (1, "Incorrect parent pointer", secno, path);
  1832.  
  1833.   height = do_storage (d, secno, &alsec.alsec.alb, 40, path,
  1834.                        pexp_file_sec, pnext_disk_sec, total_sectors,
  1835.                        parent_fnode, alsec_level + 1, what, pextents,
  1836.                        show, copy_size, buf, buf_size);
  1837.   return height + 1;
  1838. }
  1839.  
  1840.  
  1841. /* Display a warning message for the allocation structure in sector
  1842.    SECNO.  PATH points to the path name chain of the file or
  1843.    directory. */
  1844.  
  1845. static void alloc_warning (int level, const char *fmt, ULONG secno,
  1846.                            const path_chain *path, int fnode_flag, ...)
  1847. {
  1848.   va_list arg_ptr;
  1849.  
  1850.   warning_prolog (level);
  1851.   my_fprintf (diag_file, "%s #%lu (\"%s\"): ",
  1852.               fnode_flag ? "FNODE" : "ALSEC",
  1853.               secno, format_path_chain (path, NULL));
  1854.   va_start (arg_ptr, path);
  1855.   my_vfprintf (diag_file, fmt, arg_ptr);
  1856.   va_end (arg_ptr);
  1857.   fputc ('\n', diag_file);
  1858.   warning_epilog ();
  1859. }
  1860.  
  1861.  
  1862. /* Process the allocation structure pointed to by HEADER.  It is in
  1863.    sector SECNO (used for messages only).  The structure contains
  1864.    LEAF_COUNT leaves (the number of nodes is computed from
  1865.    LEAF_COUNT).  PATH points to the path name chain of the file or
  1866.    directory.  PEXP_FILE_SEC points to an object containing the first
  1867.    logical sector number mapped by the allocation structure.  The
  1868.    object's value will be incremented by the number of sectors mapped
  1869.    by the allocation strcuture.  The sector number of the sector
  1870.    following the previous run is passed in the object pointed to by
  1871.    PNEXT_DISK_SEC.  The object will be updated to contain the sector
  1872.    number of the sector following the last run.  TOTAL_SECTORS is the
  1873.    number of sectors expected for the object.  PARENT_FNODE is the
  1874.    sector number of the FNODE of the directory containing the object.
  1875.    ALSEC_LEVEL contains the current level in the ALSEC tree.  WHAT is
  1876.    USE_FILE, USE_EA, or USE_ACL for file data, extended attributes, or
  1877.    ACL, respectively.  PEXTENTS points to an object counting the
  1878.    number of extents.  The value of that object will be updated.  Show
  1879.    information if SHOW is non-zero.  Copy up to COPY_SIZE bytes of
  1880.    data to the save file.  Read sectors to BUF (of BUF_SIZE bytes) if
  1881.    BUF is non-NULL.
  1882.  
  1883.    Return the height of the ALSEC tree. */
  1884.  
  1885. static int do_storage (DISKIO *d, ULONG secno, const ALBLK *header,
  1886.                        ULONG leaf_count, const path_chain *path,
  1887.                        ULONG *pexp_file_sec, ULONG *pnext_disk_sec,
  1888.                        ULONG total_sectors, ULONG parent_fnode,
  1889.                        int alsec_level, BYTE what, ULONG *pextents,
  1890.                        int show, ULONG copy_size, BYTE *buf, ULONG buf_size)
  1891. {
  1892.   const ALLEAF *pleaf = (ALLEAF *)((char *)header + sizeof (ALBLK));
  1893.   const ALNODE *pnode = (ALNODE *)((char *)header + sizeof (ALBLK));
  1894.   ULONG i, j, n, start, count, pos;
  1895.   const char *what_text;
  1896.   int fnode_flag = (leaf_count == 8);
  1897.  
  1898.   if (show)
  1899.     info ("  %s count:                  %u\n",
  1900.           (header->bFlag & ABF_NODE) ? "Node" : "Leaf", header->cUsed);
  1901.  
  1902.   /* Note: Do not check for underflow -- as the root node has a
  1903.      smaller maximum node/leaf count than the other nodes of the tree,
  1904.      it's impossible to make all (non-root) nodes at least half
  1905.      filled. */
  1906.  
  1907.   switch (what)
  1908.     {
  1909.     case USE_EA:
  1910.       what_text = "EA data";
  1911.       break;
  1912.     case USE_FILE:
  1913.       what_text = "file data";
  1914.       break;
  1915.     case USE_ACL:
  1916.       what_text = "ACL";
  1917.       break;
  1918.     default:
  1919.       what_text = "???";
  1920.       break;
  1921.     }
  1922.   if (!(header->bFlag & ABF_FNP) != !(alsec_level == 1))
  1923.     alloc_warning (1, "ABF_FNP bit is wrong (%d)", secno, path, fnode_flag,
  1924.                    !!(header->bFlag & ABF_FNP));
  1925.   n = header->cUsed;
  1926.   if (header->bFlag & ABF_NODE)
  1927.     {
  1928.       ULONG node_count = leaf_count + leaf_count / 2;
  1929.       size_t nlen;
  1930.       int height, max_height;
  1931.  
  1932.       if ((ULONG)header->cFree + (ULONG)header->cUsed != node_count)
  1933.         {
  1934.           alloc_warning (1, "Wrong number of ALNODEs",
  1935.                          secno, path, fnode_flag);
  1936.           if (n > node_count)
  1937.             n = node_count;
  1938.         }
  1939.       if (n * sizeof (ALNODE) + sizeof (ALBLK)
  1940.           != USHORT_FROM_FS (header->oFree))
  1941.         alloc_warning (1, "Offset to free entry is wrong",
  1942.                        secno, path, fnode_flag);
  1943.       nlen = strlen (alsec_number);
  1944.       max_height = 0;
  1945.       for (i = 0; i < n; ++i)
  1946.         {
  1947.           sprintf (alsec_number + nlen, ".%lu", i);
  1948.           height = do_alsec (d, ULONG_FROM_FS (pnode[i].lsnPhys), path,
  1949.                              pexp_file_sec, pnext_disk_sec, total_sectors,
  1950.                              parent_fnode, secno, alsec_level, what,
  1951.                              pextents, show, copy_size, buf, buf_size);
  1952.           if (ULONG_FROM_FS (pnode[i].lsnLog)
  1953.               != (i + 1 == n ? 0xffffffff : *pexp_file_sec))
  1954.             alloc_warning (1, "Wrong file sector in ALNODE (%lu vs. %lu)",
  1955.                            secno, path, fnode_flag,
  1956.                            ULONG_FROM_FS (pnode[i].lsnLog),
  1957.                            (i + 1 == n ? 0xffffffff : *pexp_file_sec));
  1958.           if (i == 0)
  1959.             max_height = height;
  1960.           else
  1961.             {
  1962.               if (height != max_height)
  1963.                 alloc_warning (1, "Unbalanced allocation tree",
  1964.                                secno, path, fnode_flag);
  1965.               if (height > max_height)
  1966.                 max_height = height;
  1967.             }
  1968.         }
  1969.       alsec_number[nlen] = 0;
  1970.       return max_height;
  1971.     }
  1972.   else
  1973.     {
  1974.       if ((ULONG)header->cFree + (ULONG)header->cUsed != leaf_count)
  1975.         {
  1976.           alloc_warning (1, "Wrong number of ALLEAFs",
  1977.                          secno, path, fnode_flag);
  1978.           if (n > leaf_count)
  1979.             n = leaf_count;
  1980.         }
  1981.       if (n * sizeof (ALLEAF) + sizeof (ALBLK)
  1982.           != USHORT_FROM_FS (header->oFree))
  1983.         alloc_warning (1, "Offset to free entry is wrong",
  1984.                        secno, path, fnode_flag);
  1985.       *pextents += n;
  1986.       for (i = 0; i < n; ++i)
  1987.         {
  1988.           start = ULONG_FROM_FS (pleaf[i].lsnPhys);
  1989.           count = ULONG_FROM_FS (pleaf[i].csecRun);
  1990.           if (ULONG_FROM_FS (pleaf[i].lsnLog) != *pexp_file_sec)
  1991.             alloc_warning (1, "Wrong file sector (%lu vs. %lu)",
  1992.                            secno, path, fnode_flag,
  1993.                            ULONG_FROM_FS (pleaf[i].lsnLog), *pexp_file_sec);
  1994.           if (check_pedantic && *pnext_disk_sec != 0
  1995.               && start == *pnext_disk_sec)
  1996.             alloc_warning (0, "Contiguous runs of disk sectors",
  1997.                            secno, path, fnode_flag);
  1998.           *pnext_disk_sec = start + count;
  1999.           if (show)
  2000.             info ("  %c%s in %s (file sector %lu)\n",
  2001.                   toupper (what_text[0]), what_text+1,
  2002.                   format_sector_range (start, count),
  2003.                   ULONG_FROM_FS (pleaf[i].lsnLog));
  2004.           if (a_what && IN_RANGE (what_sector, start, count))
  2005.             info ("Sector #%lu: Sector %lu of %s for \"%s\" (+%lu)\n",
  2006.                   what_sector,
  2007.                   *pexp_file_sec + what_sector - start,
  2008.                   what_text, format_path_chain (path, NULL),
  2009.                   what_sector - start);
  2010.           if (a_check && sectors_per_block > 1 && what == USE_FILE)
  2011.             {
  2012.               /* These criterions have been guessed... */
  2013.               if (count < sectors_per_block
  2014.                   && *pexp_file_sec + count < total_sectors)
  2015.                 alloc_warning (1, "Too fragmented for the `multimedia format'",
  2016.                                secno, path, fnode_flag);
  2017.               if (start & 3)
  2018.                 alloc_warning (1, "Run not properly aligned for the "
  2019.                                "`multimedia format'", secno, path, fnode_flag);
  2020.             }
  2021.           use_sectors (start, count, what, path);
  2022.           pos = *pexp_file_sec * 512;
  2023.           if (buf != NULL)
  2024.             {
  2025.               if ((*pexp_file_sec + count) * 512 > buf_size)
  2026.                 abort ();
  2027.               read_sec (d, buf + pos, start, count, TRUE);
  2028.             }
  2029.           for (j = 0; j < count && pos < copy_size; ++j)
  2030.             {
  2031.               read_sec (d, copy_buf, start, 1, FALSE);
  2032.               fwrite (copy_buf, MIN (copy_size - pos, 512), 1, save_file);
  2033.               if (ferror (save_file))
  2034.                 save_error ();
  2035.               ++start; pos += 512;
  2036.             }
  2037.           *pexp_file_sec += count;
  2038.         }
  2039.       return 0;
  2040.     }
  2041. }
  2042.  
  2043.  
  2044. /* Display a warning message for the FNODE in sector SECNO.  PATH
  2045.    points to the path name chain of the file or directory. */
  2046.  
  2047. static void fnode_warning (int level, const char *fmt, ULONG secno,
  2048.                            const path_chain *path, ...)
  2049. {
  2050.   va_list arg_ptr;
  2051.  
  2052.   warning_prolog (level);
  2053.   my_fprintf (diag_file, "FNODE #%lu (\"%s\"): ",
  2054.               secno, format_path_chain (path, NULL));
  2055.   va_start (arg_ptr, path);
  2056.   my_vfprintf (diag_file, fmt, arg_ptr);
  2057.   va_end (arg_ptr);
  2058.   fputc ('\n', diag_file);
  2059.   warning_epilog ();
  2060. }
  2061.  
  2062.  
  2063. /* Process extended attributes.  BUF points to a buffer containing
  2064.    BUF_SIZE bytes of data.  SECNO is the sector number of the FNODE.
  2065.    PATH is the path name chain for the file or directory.  EA_SIZE is
  2066.    the expected size of the extended attributes.  Ignore EA_SIZE if
  2067.    CHECK_EA_SIZE is FALSE.  EA_NEED is the expected number of `need'
  2068.    EAs.  Display information if SHOW is true. */
  2069.  
  2070. static void do_auxinfo_ea (DISKIO *d, const BYTE *buf, ULONG buf_size,
  2071.                            ULONG secno, const path_chain *path,
  2072.                            ULONG ea_size, int check_ea_size, ULONG ea_need,
  2073.                            int show)
  2074. {
  2075.   ULONG pos, size, need_ea_count, start, count, bytes;
  2076.   ULONG value_size, file_sec, disk_sec, extents;
  2077.   const FEA *pfea;
  2078.   const SPTR *psptr;
  2079.  
  2080.   /* BUF points to a list of FEA structures.  Process them one by
  2081.      one. */
  2082.  
  2083.   pos = 0; size = 0; need_ea_count = 0;
  2084.   while (pos < buf_size)
  2085.     {
  2086.       /* Stop if the FEA structure is not completely inside the
  2087.          buffer. */
  2088.  
  2089.       if (pos + sizeof (FEA) > buf_size)
  2090.         {
  2091.           fnode_warning (1, "Truncated FEA structure", secno, path);
  2092.           break;
  2093.         }
  2094.       pfea = (FEA *)(buf + pos);
  2095.       value_size = USHORT_FROM_FS (pfea->cbValue);
  2096.       if (pos + sizeof (FEA) + pfea->cbName + 1 + value_size > buf_size)
  2097.         {
  2098.           fnode_warning (1, "Truncated FEA structure", secno, path);
  2099.           break;
  2100.         }
  2101.  
  2102.       /* The name of an EA must be terminated by a null character. */
  2103.  
  2104.       if (buf[pos + sizeof (FEA) + pfea->cbName] != 0)
  2105.         fnode_warning (1, "Name of extended attribute not terminated by "
  2106.                        "a null character", secno, path);
  2107.  
  2108.       /* Count the number of `need' EAs.  After processing all FEA
  2109.          structures, we'll compare the actual count to the expected
  2110.          count (from the FNODE). */
  2111.  
  2112.       if (pfea->fEA & FEA_NEEDEA)
  2113.         ++need_ea_count;
  2114.  
  2115.       /* There are three methods for storing the value of an extended
  2116.          attribute.  The fEA member of the FEA structure tells us
  2117.          which one is used for the current EA.  Note that bit 7
  2118.          (FEA_NEEDEA) is used to mark `need' EAs. */
  2119.  
  2120.       if ((pfea->fEA & 0x7f) == 0x00)
  2121.         {
  2122.           /* Method 1: The EA value is stored inline, directly
  2123.              following the FEA structure. */
  2124.  
  2125.           if (show_eas >= 1)
  2126.             info ("  Extended attribute %s (%lu bytes) is stored inline\n",
  2127.                   format_ea_name (pfea), value_size);
  2128.           size += sizeof (FEA) + pfea->cbName + 1 + value_size;
  2129.           if (show_frag)
  2130.             extents_stat (&ea_extents, 0);
  2131.         }
  2132.       else if ((pfea->fEA & 0x7f) == 0x01)
  2133.         {
  2134.           /* Method 2: The EA value is stored externally, in one run
  2135.              of sectors pointed to by a SPTR structure directly
  2136.              following the FEA structure. */
  2137.  
  2138.           if (value_size != sizeof (SPTR))
  2139.             fnode_warning (1, "Incorrect size of FEA structure", secno, path);
  2140.           else
  2141.             {
  2142.               psptr = (const SPTR *)(buf + pos + sizeof (FEA)
  2143.                                      + pfea->cbName + 1);
  2144.               start = ULONG_FROM_FS (psptr->lsn);
  2145.               bytes = ULONG_FROM_FS (psptr->cbRun);
  2146.               count = DIVIDE_UP (bytes, 512);
  2147.               if (show_eas >= 1)
  2148.                 info ("  Extended attribute %s (%lu bytes) is stored "
  2149.                       "in %s\n",
  2150.                       format_ea_name (pfea), bytes,
  2151.                       format_sector_range (start, count));
  2152.               else if (show)
  2153.                 info ("  Extended attributes in %s\n",
  2154.                       format_sector_range (start, count));
  2155.               if (a_what && IN_RANGE (what_sector, start, count))
  2156.                 info ("Sector #%lu: EA data for \"%s\"\n",
  2157.                       what_sector, format_path_chain (path, NULL));
  2158.               use_sectors (start, count, USE_EA, path);
  2159.               size += sizeof (FEA) + pfea->cbName + 1 + bytes;
  2160.               if (show_frag)
  2161.                 extents_stat (&ea_extents, 1);
  2162.             }
  2163.         }
  2164.       else if ((pfea->fEA & 0x7f) == 0x03)
  2165.         {
  2166.           /* Method 3: The EA value is stored externally, in several
  2167.              runs of sectors, mapped by an ALSEC which is pointed to
  2168.              by a SPTR structure directly following the FEA
  2169.              structure. */
  2170.  
  2171.           if (value_size != sizeof (SPTR))
  2172.             fnode_warning (1, "Incorrect size of FEA structure", secno, path);
  2173.           else
  2174.             {
  2175.               psptr = (const SPTR *)(buf + pos + sizeof (FEA)
  2176.                                      + pfea->cbName + 1);
  2177.               start = ULONG_FROM_FS (psptr->lsn);
  2178.               bytes = ULONG_FROM_FS (psptr->cbRun);
  2179.               if (show_eas >= 1)
  2180.                 info ("  Extended attribute %s (%lu bytes) is stored in "
  2181.                       "sectors mapped by ALSEC #%lu\n",
  2182.                       format_ea_name (pfea), bytes, start);
  2183.  
  2184.               /* Process the ALSEC. */
  2185.  
  2186.               file_sec = 0; disk_sec = 0; extents = 0;
  2187.               strcpy (alsec_number, "0");
  2188.               do_alsec (d, start, path, &file_sec, &disk_sec,
  2189.                         DIVIDE_UP (bytes, 512), secno, secno,
  2190.                         0, USE_EA, &extents, show, 0, NULL, 0);
  2191.               if (show_eas >= 1)
  2192.                 info ("  Number of sectors for this EA: %lu\n", file_sec);
  2193.  
  2194.               /* Check the number of sectors.  There is a bug in
  2195.                  HPFS.IFS: It never assigns more than 40 extents (that
  2196.                  is, one ALSEC), truncating EAs. */
  2197.  
  2198.               if (file_sec * 512 < bytes)
  2199.                 fnode_warning (1, "Not enough sectors allocated for EA %s",
  2200.                                secno, path, format_ea_name (pfea));
  2201.               if (file_sec > DIVIDE_UP (bytes, 512))
  2202.                 fnode_warning (1, "Too many sectors allocated for EA %s (%lu)",
  2203.                                secno, path, format_ea_name (pfea),
  2204.                                file_sec - DIVIDE_UP (bytes, 512));
  2205.  
  2206.               size += sizeof (FEA) + pfea->cbName + 1 + bytes;
  2207.               if (show_frag)
  2208.                 extents_stat (&ea_extents, extents);
  2209.             }
  2210.         }
  2211.       else
  2212.         {
  2213.           /* No other methods are known. */
  2214.  
  2215.           fnode_warning (1, "Invalid FEA flag 0x%.2x for extended "
  2216.                          "attribute %s", secno, path, pfea->fEA,
  2217.                          format_ea_name (pfea));
  2218.           break;
  2219.         }
  2220.       pos += sizeof (FEA) + pfea->cbName + 1 + value_size;
  2221.     }
  2222.  
  2223.   /* Compare the actual EA size and `need' EA count to the values
  2224.      stored in the FNODE. */
  2225.  
  2226.   if (check_ea_size && size != ea_size)
  2227.     fnode_warning (1, "Incorrect EA size (%lu vs. %lu)",
  2228.                    secno, path, size, ea_size);
  2229.   if (need_ea_count != ea_need)
  2230.     fnode_warning (1, "Incorrect number of `need' EAs (%lu vs. %lu)",
  2231.                    secno, path, need_ea_count, ea_need);
  2232. }
  2233.  
  2234.  
  2235. /* Process the data pointed to by an AUXINFO structure (for EAs or
  2236.    ACL).  BUF points to a buffer containing BUF_SIZE bytes of data.
  2237.    WHAT indicates whether the AUXINFO structure is for EAs or an ACL.
  2238.    SECNO is the sector number of the FNODE.  PATH is the path name
  2239.    chain for the file or directory.  EA_SIZE is the expected size of
  2240.    the extended attributes.  Ignore EA_SIZE if CHECK_EA_SIZE is FALSE.
  2241.    EA_NEED is the expected number of `need' EAs.  Display information
  2242.    if SHOW is true. */
  2243.  
  2244. static void do_auxinfo_buf (DISKIO *d, const BYTE *buf, ULONG buf_size,
  2245.                             BYTE what, ULONG secno, const path_chain *path,
  2246.                             ULONG ea_size, int check_ea_size, ULONG ea_need,
  2247.                             int show)
  2248. {
  2249.   switch (what)
  2250.     {
  2251.     case USE_EA:
  2252.       do_auxinfo_ea (d, buf, buf_size, secno, path, ea_size, check_ea_size,
  2253.                      ea_need, show);
  2254.       break;
  2255.     case USE_ACL:
  2256.       /* I do not yet know anything about ACLs. */
  2257.       break;
  2258.     }
  2259. }
  2260.  
  2261.  
  2262. /* Process the AUXINFO structure (for EAs or ACL) pointed to by PAI.
  2263.    If the structure indicates that the data is stored inside the
  2264.    FNODE, take the data from PFNODE, starting at offset BASE.  SECNO
  2265.    is the sector number of the FNODE.  PATH is the path name chain for
  2266.    the file or directory.  WHAT indicates whether the AUXINFO
  2267.    structure is for EAs or an ACL.  EA_SIZE is the expected size of
  2268.    the extended attributes, EA_NEED is the expected number of `need'
  2269.    EAs.  Display information if SHOW is true. */
  2270.  
  2271. static void do_auxinfo (DISKIO *d, const FNODE *pfnode, const AUXINFO *pai,
  2272.                         unsigned base, ULONG secno, const path_chain *path,
  2273.                         BYTE what, ULONG ea_size, int check_ea_size,
  2274.                         ULONG ea_need, int show)
  2275. {
  2276.   BYTE *buf;
  2277.   ULONG buf_size, file_sec, disk_sec, extents, run_length, start, fnode_length;
  2278.  
  2279.   /* Fetch some values from the AUXINFO structure. */
  2280.  
  2281.   run_length = ULONG_FROM_FS (pai->sp.cbRun);
  2282.   start = ULONG_FROM_FS (pai->sp.lsn);
  2283.   fnode_length = USHORT_FROM_FS (pai->usFNL);
  2284.  
  2285.   /* EAs and ACLs are either stored entirely inside the FNODE or
  2286.      entirely externally.  Having data both inside the FNODE and
  2287.      externally is an error. */
  2288.  
  2289.   if (run_length != 0 && fnode_length != 0)
  2290.     fnode_warning (1, "Both internal and external %s",
  2291.                    secno, path, (what == USE_EA ? "EA" : "ACL"));
  2292.  
  2293.   /* No buffer has been allocated so far. */
  2294.  
  2295.   buf = NULL; buf_size = 0;
  2296.  
  2297.   /* Data is stored outside the FNODE if pai->sp.cbRun is non-zero. */
  2298.  
  2299.   if (run_length != 0)
  2300.     {
  2301.       buf_size = run_length;
  2302.  
  2303.       /* If pai->bDat is non-zero, pai->sp.lsn points to an ALSEC.
  2304.          Otherwise it directly points to the first data sector. */
  2305.  
  2306.       if (pai->bDat)
  2307.         {
  2308.           /* The data is mapped by an ALSEC. */
  2309.  
  2310.           if (a_where)
  2311.             {
  2312.               if (what == USE_EA)
  2313.                 info ("  Extended attributes (FEA structures, %lu bytes) "
  2314.                       "in sectors mapped by ALSEC #%lu\n",
  2315.                       run_length, start);
  2316.               else
  2317.                 info ("  ACL (%lu bytes) in sectors mapped by ALSEC #%lu\n",
  2318.                       run_length, start);
  2319.             }
  2320.           if (buf_size <= 0x100000)
  2321.             buf = malloc (ROUND_UP (buf_size, 512));
  2322.           if (buf != NULL)
  2323.             memset (buf, 0, buf_size);
  2324.           file_sec = 0; disk_sec = 0; extents = 0;
  2325.           strcpy (alsec_number, "0");
  2326.           do_alsec (d, start, path, &file_sec, &disk_sec,
  2327.                     DIVIDE_UP (run_length, 512), secno, secno, 0,
  2328.                     what, &extents, show, 0, buf, ROUND_UP (buf_size, 512));
  2329.           if (file_sec * 512 < run_length)
  2330.             fnode_warning (1, "Not enough sectors allocated for %s",
  2331.                            secno, path, (what == USE_EA ? "EAs" : "ACLs"));
  2332.           if (file_sec > DIVIDE_UP (run_length, 512))
  2333.             fnode_warning (1, "Too many sectors allocated for %s (%lu)",
  2334.                            secno, path, (what == USE_EA ? "EAs" : "ACLs"),
  2335.                            file_sec - DIVIDE_UP (run_length, 512));
  2336.         }
  2337.       else
  2338.         {
  2339.           /* The data is stored in one run of sectors. */
  2340.  
  2341.           ULONG count;
  2342.  
  2343.           count = DIVIDE_UP (run_length, 512);
  2344.           if (a_where)
  2345.             {
  2346.               if (what == USE_EA)
  2347.                 info ("  Extended attributes (FEA structures, %lu bytes) "
  2348.                       "in %s\n",
  2349.                       run_length, format_sector_range (start, count));
  2350.               else
  2351.                 info ("  ACL (%lu bytes) in %s\n",
  2352.                       run_length, format_sector_range (start, count));
  2353.             }
  2354.  
  2355.           if (a_what && IN_RANGE (what_sector, start, count))
  2356.             {
  2357.               if (what == USE_EA)
  2358.                 info ("Sector #%lu: Extended attributes (FEA structures) for "
  2359.                       "\"%s\" (+%lu)\n",
  2360.                       what_sector, format_path_chain (path, NULL),
  2361.                       what_sector - start);
  2362.               else
  2363.                 info ("Sector #%lu: ACL for \"%s\" (+%lu)\n",
  2364.                       what_sector, format_path_chain (path, NULL),
  2365.                       what_sector - start);
  2366.             }
  2367.           use_sectors (start, count, what, path);
  2368.           if (buf_size <= 0x100000)
  2369.             {
  2370.               buf = xmalloc (count * 512);
  2371.               read_sec (d, buf, start, count, TRUE);
  2372.             }
  2373.         }
  2374.     }
  2375.   else if (fnode_length != 0)
  2376.     {
  2377.       /* The data is stored inside the FNODE. */
  2378.  
  2379.       if (a_where)
  2380.         {
  2381.           if (what == USE_EA)
  2382.             info ("  Extended attributes (FEA structures, %lu bytes at 0x%x) "
  2383.                   "in FNODE #%lu\n", fnode_length, base, secno);
  2384.           else
  2385.             info ("  ACL (%lu bytes at 0x%x) in FNODE #%lu\n",
  2386.                   fnode_length, base, secno);
  2387.         }
  2388.       if (base < offsetof (FNODE, abFree))
  2389.         fnode_warning (1, "%s offset invalid", secno, path,
  2390.                        (what == USE_EA ? "EA" : "ACL"));
  2391.       else if (base + fnode_length > 512)
  2392.         fnode_warning (1, "%s beyond end of FNODE", secno, path,
  2393.                        (what == USE_EA ? "EA list" : "ACL"));
  2394.       else
  2395.         do_auxinfo_buf (d, (const BYTE *)pfnode + base, fnode_length,
  2396.                         what, secno, path, ea_size, check_ea_size, ea_need,
  2397.                         show);
  2398.     }
  2399.  
  2400.   /* If we have copied the data to a buffer, call do_auxinfo_buf() on
  2401.      that buffer.  (For data stored inside the FNODE, do_auxinfo_buf()
  2402.      has already been called above, without allocating a buffer.) */
  2403.  
  2404.   if (buf_size != 0)
  2405.     {
  2406.       if (buf == NULL)
  2407.         fnode_warning (1, "%s too big for examination", secno, path,
  2408.                        (what == USE_EA ? "EAs" : "ACL"));
  2409.       else
  2410.         {
  2411.           do_auxinfo_buf (d, buf, buf_size, what, secno, path,
  2412.                           ea_size, check_ea_size, ea_need, show);
  2413.           free (buf);
  2414.         }
  2415.     }
  2416. }
  2417.  
  2418.  
  2419. /* Process the FNODE in sector SECNO.  PATH points to the path name
  2420.    chain for the file or directory.  DIR_FLAG is true if the FNODE
  2421.    belongs to a directory.  PARENT_FNODE is the sector number of the
  2422.    FNODE of the directory containing the object.  FILE_SIZE is the
  2423.    size of the file.  EA_SIZE is the total size of the extended
  2424.    attributes of the file or directory.  Ignore EA_SIZE if
  2425.    CHECK_EA_SIZE is FALSE (the root directory is the only object for
  2426.    which EA_SIZE is unknown).  NEED_EAS is non-zero if the object has
  2427.    `need' EAs.  List the directory if LIST is true. */
  2428.  
  2429. static void do_fnode (DISKIO *d, ULONG secno, const path_chain *path,
  2430.                       int dir_flag, ULONG parent_fnode, ULONG file_size,
  2431.                       ULONG ea_size, int check_ea_size, int need_eas, int list)
  2432. {
  2433.   HPFS_SECTOR fnode;
  2434.   ULONG file_sec, disk_sec, extents, fn_fsize, i;
  2435.   size_t name_len;
  2436.   int show, found, height;
  2437.  
  2438.   found = (a_find && *find_path == 0);
  2439.   show = (found && a_where);
  2440.   if (show)
  2441.     info ("FNODE: #%lu\n", secno);
  2442.   if (a_what && secno == what_sector)
  2443.     {
  2444.       info ("Sector #%lu: FNODE for \"%s\"\n",
  2445.             secno, format_path_chain (path, NULL));
  2446.       show = TRUE;
  2447.     }
  2448.   if (have_seen (secno, 1, SEEN_FNODE, "FNODE"))
  2449.     return;
  2450.   use_sectors (secno, 1, USE_FNODE, path);
  2451.   read_sec (d, &fnode, secno, 1, TRUE);
  2452.   if (ULONG_FROM_FS (fnode.fnode.sig) != FNODE_SIG1)
  2453.     {
  2454.       fnode_warning (1, "Bad signature", secno, path);
  2455.       if (found)
  2456.         quit (0, FALSE);
  2457.       return;
  2458.     }
  2459.   if (dir_flag)
  2460.     ++dir_count;
  2461.   else
  2462.     ++file_count;
  2463.   fn_fsize = ULONG_FROM_FS (fnode.fnode.fst.ulVLen);
  2464.   if (!(fnode.fnode.bFlag & FNF_DIR) != !dir_flag)
  2465.     fnode_warning (1, "Incorrect directory bit", secno, path);
  2466.   if (ULONG_FROM_FS (fnode.fnode.lsnContDir) != parent_fnode)
  2467.     fnode_warning (1, "Wrong pointer to containing directory", secno, path);
  2468.   if (a_check)
  2469.     {
  2470.       if ((ULONG_FROM_FS (fnode.fnode.ulRefCount) == 0) != !need_eas)
  2471.         fnode_warning (1, "Need-EA bit of DIRENT is wrong", secno, path);
  2472.       name_len = strlen (path->name);
  2473.       if (fnode.fnode.achName[0] != name_len
  2474.           && memcmp (fnode.fnode.achName, path->name, MIN (name_len, 16)) == 0)
  2475.         fnode_warning (0, "Truncated name mangled by OS/2 2.0 bug",
  2476.                        secno, path);
  2477.       else if (fnode.fnode.achName[0] != name_len)
  2478.         fnode_warning (1, "Wrong full name length (%lu vs. %lu)",
  2479.                        secno, path, (ULONG)fnode.fnode.achName[0],
  2480.                        (ULONG)name_len);
  2481.       else if (memcmp (fnode.fnode.achName+1, path->name,
  2482.                        MIN (name_len, 15)) != 0)
  2483.         fnode_warning (1, "Wrong truncated name", secno, path);
  2484.       if (!dir_flag && file_size != fn_fsize)
  2485.         fnode_warning (1, "File size does not match DIRENT", secno, path);
  2486.       if (check_pedantic)
  2487.         {
  2488.           for (i = 0; i < sizeof (fnode.fnode.abSpare); ++i)
  2489.             if (fnode.fnode.abSpare[i] != 0)
  2490.               fnode_warning (0, "abSpare[%lu] is 0x%.2x", secno, path,
  2491.                              i, fnode.fnode.abSpare[i]);
  2492.         }
  2493.     }
  2494.  
  2495.   if (show)
  2496.     {
  2497.       info ("  Flags:                       0x%.2x", fnode.fnode.bFlag);
  2498.       if (fnode.fnode.bFlag & FNF_DIR)
  2499.         info (" dir");
  2500.       info ("\n");
  2501.       info ("  Size of file:                %lu\n", fn_fsize);
  2502.       info ("  Number of `need' EAs:        %lu\n",
  2503.             ULONG_FROM_FS (fnode.fnode.ulRefCount));
  2504.       info ("  Offset of first ACE:         %u\n",
  2505.             USHORT_FROM_FS (fnode.fnode.usACLBase));
  2506.       info ("  ACL size in FNODE:           %u\n",
  2507.             USHORT_FROM_FS (fnode.fnode.aiACL.usFNL));
  2508.       info ("  External ACL size:           %lu\n",
  2509.             ULONG_FROM_FS (fnode.fnode.aiACL.sp.cbRun));
  2510.     }
  2511.  
  2512.   if (dir_flag)
  2513.     {
  2514.       if (show)
  2515.         info ("  Root DIRBLK sector:          #%lu\n",
  2516.               ULONG_FROM_FS (fnode.fnode.fst.a.aall[0].lsnPhys));
  2517.       if (a_copy && found)
  2518.         error ("Directories cannot be copied");
  2519.       if (a_find && !found && !list)
  2520.         {
  2521.           const char *pdelim;
  2522.           size_t len;
  2523.  
  2524.           pdelim = strchr (find_path, '\\');
  2525.           if (pdelim == NULL)
  2526.             len = strlen (find_path);
  2527.           else
  2528.             len = pdelim - find_path;
  2529.           if (len > 255)
  2530.             error ("Path name component too long");
  2531.           memcpy (find_comp, find_path, len);
  2532.           find_comp[len] = 0;
  2533.           find_path += len;
  2534.           if (*find_path == '\\')
  2535.             {
  2536.               ++find_path;
  2537.               if (*find_path == 0)
  2538.                 error ("Trailing backslash");
  2539.             }
  2540.         }
  2541.       if (!found || list)
  2542.         {
  2543.           SORT sort;
  2544.           int index, dotdot, i;
  2545.           int down_ptr[MAX_DIRBLK_LEVELS];
  2546.  
  2547.           sort.name[0] = 0;
  2548.           sort.cpindex = code_page_count;
  2549.           index = 0; dotdot = FALSE;
  2550.           for (i = 0; i < MAX_DIRBLK_LEVELS; ++i)
  2551.             down_ptr[i] = -1; /* Existence of down pointer is unknown */
  2552.           do_dirblk (d,
  2553.                      ULONG_FROM_FS (fnode.fnode.fst.a.aall[0].lsnPhys),
  2554.                      path, secno, secno, &sort, down_ptr, 0, &index, &dotdot,
  2555.                      list);
  2556.           if (!dotdot)
  2557.             warning (1, "Missing \"..\" entry in directory \"%s\"",
  2558.                      format_path_chain (path, NULL));
  2559.         }
  2560.       if (a_find && !found)
  2561.         error ("\"%s\" not found in \"%s\"",
  2562.                find_comp, format_path_chain (path, NULL));
  2563.     }
  2564.   else
  2565.     {
  2566.       file_sec = 0; disk_sec = 0; extents = 0;
  2567.       alsec_number[0] = 0;
  2568.       height = do_storage (d, secno, &fnode.fnode.fst.alb, 8, path,
  2569.                            &file_sec, &disk_sec, DIVIDE_UP (fn_fsize, 512),
  2570.                            secno, 0, USE_FILE, &extents,
  2571.                            show, found && a_copy ? fn_fsize : 0, NULL, 0);
  2572.       if (show)
  2573.         {
  2574.           info ("  Allocation tree height:      %d\n", height);
  2575.           info ("  Number of sectors:           %lu\n", file_sec);
  2576.           info ("  Number of extents:           %lu\n", extents);
  2577.         }
  2578.       if (show_frag)
  2579.         extents_stat (&file_extents, extents);
  2580.       if (file_sec * 512 < fn_fsize)
  2581.         fnode_warning (1, "Not enough sectors allocated", secno, path);
  2582.       if (file_sec > DIVIDE_UP (fn_fsize, 512))
  2583.         fnode_warning (1, "Too many sectors allocated (%lu)",
  2584.                        secno, path, file_sec - DIVIDE_UP (fn_fsize, 512));
  2585.     }
  2586.  
  2587.   /* Extended attributes are stored in the FNODE.  ACL entries are
  2588.      stored first (at offset usACLBase), followed by the extended
  2589.      attributes. */
  2590.  
  2591.   do_auxinfo (d, &fnode.fnode, &fnode.fnode.aiEA,
  2592.               (USHORT_FROM_FS (fnode.fnode.usACLBase)
  2593.                + USHORT_FROM_FS (fnode.fnode.aiACL.usFNL)),
  2594.               secno, path, USE_EA, ea_size, check_ea_size,
  2595.               ULONG_FROM_FS (fnode.fnode.ulRefCount), show);
  2596.  
  2597.   do_auxinfo (d, &fnode.fnode, &fnode.fnode.aiACL,
  2598.               USHORT_FROM_FS (fnode.fnode.usACLBase),
  2599.               secno, path, USE_ACL, 0, FALSE, 0, show);
  2600.  
  2601.   if (found)
  2602.     {
  2603.       if (a_copy)
  2604.         save_close ();
  2605.       quit (0, TRUE);
  2606.     }
  2607. }
  2608.  
  2609.  
  2610. /* Complain about sectors which are used but marked unallocated.
  2611.    Optionally complain about sectors which are not in use but marked
  2612.    allocated. */
  2613.  
  2614. static void check_alloc (void)
  2615. {
  2616.   ULONG i, start, count;
  2617.   BYTE start_what, first;
  2618.   const path_chain *start_path;
  2619.  
  2620.   /* List used sectors not marked as allocated. */
  2621.  
  2622.   i = 0; first = TRUE;
  2623.   while (i < total_sectors)
  2624.     {
  2625.       if (usage_vector[i] != USE_EMPTY && !ALLOCATED (i))
  2626.         {
  2627.           start = i; start_what = usage_vector[i];
  2628.           start_path = path_vector != NULL ? path_vector[i] : NULL;
  2629.           do
  2630.             {
  2631.               ++i;
  2632.             } while (i < total_sectors
  2633.                      && usage_vector[i] != USE_EMPTY && !ALLOCATED (i)
  2634.                      && usage_vector[i] == start_what
  2635.                      && ((path_vector != NULL ? path_vector[i] : NULL)
  2636.                          == start_path));
  2637.           if (first)
  2638.             {
  2639.               warning (1, "There are used sectors which are not marked "
  2640.                        "as allocated:");
  2641.               first = FALSE;
  2642.             }
  2643.           warning (1, "Used (%s) but not marked as allocated: %s",
  2644.                    sec_usage (start_what),
  2645.                    format_sector_range (start, i - start));
  2646.           if (start_path != NULL)
  2647.             warning_cont ("File: \"%s\"",
  2648.                           format_path_chain (start_path, NULL));
  2649.         }
  2650.       else
  2651.         ++i;
  2652.     }
  2653.  
  2654.   /* List unused sectors marked as allocated. */
  2655.  
  2656.   i = 0; count = 0;
  2657.   while (i < total_sectors)
  2658.     {
  2659.       if (usage_vector[i] == USE_EMPTY && ALLOCATED (i))
  2660.         {
  2661.           start = i;
  2662.           do
  2663.             {
  2664.               ++i;
  2665.             } while (i < total_sectors
  2666.                      && usage_vector[i] == USE_EMPTY && ALLOCATED (i));
  2667.           if (check_unused)
  2668.             warning (0, "Unused but marked as allocated: %s",
  2669.                      format_sector_range (start, i - start));
  2670.           count += i - start;
  2671.           if (IN_RANGE (18, start, i - start))
  2672.             count -= 1;
  2673.           if (IN_RANGE (19, start, i - start))
  2674.             count -= 1;
  2675.         }
  2676.       else
  2677.         ++i;
  2678.     }
  2679.   if (count == 1)
  2680.     warning (0, "The file system has 1 lost sector");
  2681.   else if (count > 1)
  2682.     warning (0, "The file system has %lu lost sectors", count);
  2683. }
  2684.  
  2685.  
  2686. /* Process the DIRBLK band bitmap starting in sector BSECNO.  The
  2687.    bitmap describes COUNT DIRBLKs starting at sector number START. */
  2688.  
  2689. static void do_dirblk_bitmap (DISKIO *d, ULONG bsecno, ULONG start,
  2690.                               ULONG count)
  2691. {
  2692.   ULONG sectors, i, dsecno;
  2693.   BYTE bitmap[2048];
  2694.  
  2695.   /* Compute the number of sectors used for the DIRBLK bitmap. */
  2696.  
  2697.   sectors = DIVIDE_UP (count, 512 * 8);
  2698.  
  2699.   /* The DIRBLK band bitmap always occupies 4 sectors. */
  2700.  
  2701.   if (sectors > 4)
  2702.     {
  2703.       warning (1, "DIRBLK band too big\n");
  2704.       sectors = 4;
  2705.     }
  2706.   read_sec (d, bitmap, bsecno, sectors, TRUE);
  2707.  
  2708.   /* Compare the bitmap to our usage_vector[]. */
  2709.  
  2710.   dsecno = start;
  2711.   for (i = 0; i < count; ++i)
  2712.     {
  2713.       if (BITSETP (bitmap, i))
  2714.         {
  2715.           if (usage_vector[dsecno] != USE_BANDDIRBLK)
  2716.             warning (1, "Sector #%lu is marked available in the "
  2717.                      "DIRBLK bitmap, but is used as %s\n",
  2718.                      dsecno, sec_usage (usage_vector[dsecno]));
  2719.         }
  2720.       else
  2721.         {
  2722.           if (usage_vector[dsecno] != USE_DIRBLK)
  2723.             warning (1, "Sector #%lu is marked used in the DIRBLK bitmap, "
  2724.                      "but is used as %s\n",
  2725.                      dsecno, sec_usage (usage_vector[dsecno]));
  2726.         }
  2727.       dsecno += 4;
  2728.     }
  2729. }
  2730.  
  2731.  
  2732. /* Check the spare DIRBLKs.  LIST points to an array of COUNT sector
  2733.    numbers (in HPFS representation). */
  2734.  
  2735. static void check_sparedirblk (const ULONG *list, ULONG total, ULONG free)
  2736. {
  2737.   ULONG i, secno;
  2738.  
  2739.   for (i = free; i < total; ++i)
  2740.     {
  2741.       secno = ULONG_FROM_FS (list[i]);
  2742.       if (secno < total_sectors && usage_vector[secno] != USE_DIRBLK)
  2743.         warning (1, "Spare DIRBLK #%lu is not used for a DIRBLK", secno);
  2744.     }
  2745. }
  2746.  
  2747.  
  2748. /* Build a time stamp for year D, month M, day D. */
  2749.  
  2750. static ULONG make_time (int y, int m, int d)
  2751. {
  2752.   struct tm tm;
  2753.  
  2754.   tm.tm_sec = 0; tm.tm_min = 0; tm.tm_hour = 0;
  2755.   tm.tm_mday = d; tm.tm_mon = m - 1; tm.tm_year = y - 1900;
  2756.   tm.tm_isdst = 0;
  2757.   return (ULONG)mktime (&tm);
  2758. }
  2759.  
  2760.  
  2761. /* Process an HPFS volume. */
  2762.  
  2763. void do_hpfs (DISKIO *d)
  2764. {
  2765.   HPFS_SECTOR superb;
  2766.   HPFS_SECTOR spareb, spareb_tmp;
  2767.   ULONG i, n, superb_chksum, spareb_chksum;
  2768.   ULONG dirband_sectors;
  2769.  
  2770.   if (a_what && what_cluster_flag)
  2771.     error ("Cluster numbers not supported on HPFS");
  2772.  
  2773.   min_time = make_time (1980, 1, 1);
  2774.   alloc_ready = FALSE;
  2775.  
  2776.   /* All structures of HPFS are anchored in the Superblock (LSN 16)
  2777.      and Spare block (LSN 17) sectors. */
  2778.  
  2779.   /* Superblock. */
  2780.  
  2781.   read_sec (d, &superb, 16, 1, TRUE);
  2782.   if (ULONG_FROM_FS (superb.superb.sig1) != SUPER_SIG1
  2783.       || ULONG_FROM_FS (superb.superb.sig2) != SUPER_SIG2)
  2784.     error ("Invalid signature of superblock -- this is not an HPFS partition");
  2785.  
  2786.   /* Spare block. */
  2787.  
  2788.   read_sec (d, &spareb, 17, 1, TRUE);
  2789.   if (ULONG_FROM_FS (spareb.spareb.sig1) != SPARE_SIG1
  2790.       || ULONG_FROM_FS (spareb.spareb.sig2) != SPARE_SIG2)
  2791.     error ("Invalid signature of spare block");
  2792.  
  2793.   /* Compute and check the total number of sectors. */
  2794.  
  2795.   total_sectors = ULONG_FROM_FS (superb.superb.culSectsOnVol);
  2796.   if (a_what && what_sector >= total_sectors)
  2797.     warning (0, "Sector number #%lu is too big", what_sector);
  2798.   if (diskio_type (d) == DIO_DISK && total_sectors > diskio_total_sectors (d))
  2799.     warning (1, "HPFS extends beyond end of partition indicated by BPB");
  2800.  
  2801.   /* Set the block size.  The meaning of bAlign[1] is guessed. */
  2802.  
  2803.   sectors_per_block = 1;
  2804.   if (superb.superb.bFuncVersion == 4)
  2805.     sectors_per_block = 1 << spareb.spareb.bAlign[1];
  2806.  
  2807.   /* Allocate usage vector.  Initially, all sectors are unused. */
  2808.  
  2809.   usage_vector = (BYTE *)xmalloc (total_sectors);
  2810.   memset (usage_vector, USE_EMPTY, total_sectors);
  2811.  
  2812.   /* Allocate `have seen' vector which is used for avoiding loops.
  2813.      Initially, no sector has been seen. */
  2814.  
  2815.   seen_vector = (BYTE *)xmalloc (total_sectors);
  2816.   memset (seen_vector, 0, total_sectors);
  2817.  
  2818.   /* Allocate vector of path chains for improving error messages.
  2819.      Note that the following condition must match the condition in the
  2820.      PATH_CHAIN_NEW macro! */
  2821.  
  2822.   if (a_check && plenty_memory)
  2823.     {
  2824.       path_vector = xmalloc (total_sectors * sizeof (*path_vector));
  2825.       for (i = 0; i < total_sectors; ++i)
  2826.         path_vector[i] = NULL;
  2827.     }
  2828.   else
  2829.     path_vector = NULL;
  2830.  
  2831.   code_page_count = ULONG_FROM_FS (spareb.spareb.culCP);
  2832.  
  2833.   /* Compute the checksums. */
  2834.  
  2835.   superb_chksum = chksum (superb.raw, 512);
  2836.   spareb_tmp = spareb;
  2837.   spareb_tmp.spareb.bFlag &= (SPF_VER | SPF_FASTFMT);
  2838.   spareb_tmp.spareb.aulExtra[1] = 0; /* TODO: ULONG_TO_HPFS */
  2839.   spareb_chksum = chksum (spareb_tmp.raw, 512);
  2840.  
  2841.   /* Boot sector. */
  2842.  
  2843.   if (a_what && what_sector == 0)
  2844.     info ("Sector #%lu: Boot sector\n", what_sector);
  2845.   use_sectors (0, 1, USE_BOOT, NULL);
  2846.  
  2847.   /* Boot loader. */
  2848.  
  2849.   if (a_what && IN_RANGE (what_sector, 1, 15))
  2850.     info ("Sector #%lu: Boot loader\n", what_sector);
  2851.   use_sectors (1, 15, USE_LOADER, NULL);
  2852.  
  2853.   /* Superblock. */
  2854.  
  2855.   use_sectors (16, 1, USE_SUPER, NULL);
  2856.   if (a_info || (a_what && what_sector == 16))
  2857.     {
  2858.       info ("Sector #%lu: Super block\n", (ULONG)16);
  2859.       info ("  HPFS Version:                       %d\n",
  2860.             superb.superb.bVersion);
  2861.       info ("  Functional version:                 %d",
  2862.             superb.superb.bFuncVersion);
  2863.       if (superb.superb.bFuncVersion == 2)
  2864.         info (" (<=4GB)\n");
  2865.       else if (superb.superb.bFuncVersion == 3)
  2866.         info (" (>4GB)\n");
  2867.       else if (superb.superb.bFuncVersion == 4)
  2868.         info (" (multimedia)\n");
  2869.       else
  2870.         info ("\n");
  2871.       info ("  Root directory FNODE at:            #%lu\n",
  2872.             ULONG_FROM_FS (superb.superb.lsnRootFNode));
  2873.       info ("  Total number of sectors:            %lu\n",
  2874.             ULONG_FROM_FS (superb.superb.culSectsOnVol));
  2875.       if (sector_number_format != 0
  2876.           && ULONG_FROM_FS (superb.superb.culSectsOnVol) != 0)
  2877.         info (  "Last sector:                        #%lu\n",
  2878.               ULONG_FROM_FS (superb.superb.culSectsOnVol) - 1);
  2879.       info ("  Number of bad sectors:              %lu\n",
  2880.             ULONG_FROM_FS (superb.superb.culNumBadSects));
  2881.       info ("  Bitmap indirect block at:           #%lu\n",
  2882.             ULONG_FROM_FS (superb.superb.rspBitMapIndBlk.lsnMain));
  2883.       info ("  Bad block list starts at:           #%lu\n",
  2884.             ULONG_FROM_FS (superb.superb.rspBadBlkList.lsnMain));
  2885.       info ("  Time of last chkdsk:                %s\n",
  2886.             format_time (ULONG_FROM_FS (superb.superb.datLastChkdsk)));
  2887.       info ("  Time of last optimization:          %s\n",
  2888.             format_time (ULONG_FROM_FS (superb.superb.datLastOptimize)));
  2889.       info ("  Number of sectors in DIRBLK band:   %lu\n",
  2890.             ULONG_FROM_FS (superb.superb.clsnDirBlkBand));
  2891.       info ("  First sector in DIRBLK band:        #%lu\n",
  2892.             ULONG_FROM_FS (superb.superb.lsnFirstDirBlk));
  2893.       info ("  Last sector in DIRBLK band:         #%lu\n",
  2894.             ULONG_FROM_FS (superb.superb.lsnLastDirBlk));
  2895.       info ("  First sector of DIRBLK band bitmap: #%lu\n",
  2896.             ULONG_FROM_FS (superb.superb.lsnDirBlkMap));
  2897.       info ("  Sector number of user ID table:     #%lu\n",
  2898.             ULONG_FROM_FS (superb.superb.lsnSidTab));
  2899.       info ("  Check sum (computed):               0x%.8lx\n", superb_chksum);
  2900.     }
  2901.  
  2902.   /* The Superblock points to the DIRBLK band. */
  2903.  
  2904.   dirband_sectors = ULONG_FROM_FS (superb.superb.clsnDirBlkBand);
  2905.   dirband_start = ULONG_FROM_FS (superb.superb.lsnFirstDirBlk);
  2906.   dirband_end = ULONG_FROM_FS (superb.superb.lsnLastDirBlk);
  2907.   dirblk_total = 0; dirblk_outside = 0;
  2908.  
  2909.   /* Spare block. */
  2910.  
  2911.   use_sectors (17, 1, USE_SPARE, NULL);
  2912.   if (a_info || (a_what && what_sector == 17))
  2913.     {
  2914.       info ("Sector #%lu: Spare block\n", (ULONG)17);
  2915.       info ("  Spare block flags:                  0x%.2x (",
  2916.             spareb.spareb.bFlag);
  2917.       if (spareb.spareb.bFlag & SPF_DIRT)
  2918.         info ("dirty");
  2919.       else
  2920.         info ("clean");
  2921.       if (spareb.spareb.bFlag & SPF_SPARE) info (" spare");
  2922.       if (spareb.spareb.bFlag & SPF_HFUSED) info (" hotfix");
  2923.       if (spareb.spareb.bFlag & SPF_BADSEC) info (" badsec");
  2924.       if (spareb.spareb.bFlag & SPF_BADBM) info (" badbmp");
  2925.       if (spareb.spareb.bFlag & SPF_FASTFMT) info (" fastfmt");
  2926.       if (spareb.spareb.bFlag & SPF_VER) info (" version");
  2927.       info (")\n");
  2928.       info ("  Block size:                         %lu\n",
  2929.             sectors_per_block * 512);
  2930.       info ("  Hotfix sector mapping table at:     #%lu\n",
  2931.             ULONG_FROM_FS (spareb.spareb.lsnHotFix));
  2932.       info ("  Number of hotfixes used:            %lu\n",
  2933.             ULONG_FROM_FS (spareb.spareb.culHotFixes));
  2934.       info ("  Maximum number of hotfixes:         %lu\n",
  2935.             ULONG_FROM_FS (spareb.spareb.culMaxHotFixes));
  2936.       info ("  Number of free spare DIRBLKs:       %lu\n",
  2937.             ULONG_FROM_FS (spareb.spareb.cdbSpares));
  2938.       info ("  Total number of spare DIRBLKs:      %lu\n",
  2939.             ULONG_FROM_FS (spareb.spareb.cdbMaxSpare));
  2940.       info ("  Code page information sector at:    #%lu\n",
  2941.             ULONG_FROM_FS (spareb.spareb.lsnCPInfo));
  2942.       info ("  Number of code pages:               %lu\n",
  2943.             ULONG_FROM_FS (spareb.spareb.culCP));
  2944.       info ("  Checksum of Super block:            0x%.8lx\n",
  2945.             ULONG_FROM_FS (spareb.spareb.aulExtra[0]));
  2946.       info ("  Checksum of Spare block:            0x%.8lx\n",
  2947.             ULONG_FROM_FS (spareb.spareb.aulExtra[1]));
  2948.       info ("  Check sum (computed):               0x%.8lx\n", spareb_chksum);
  2949.       n = ULONG_FROM_FS (spareb.spareb.cdbMaxSpare);
  2950.       for (i = 0; i < n; ++i)
  2951.         info ("  Spare DIRBLK at #%lu\n",
  2952.               ULONG_FROM_FS (spareb.spareb.alsnSpareDirBlks[i]));
  2953.     }
  2954.  
  2955.   /* DIRBLK band.  This is where DIRBLK preferably live.  If there is
  2956.      not enough space in the DIRBLK band, HPFS puts DIRBLKs in other
  2957.      available sectors (starting at a 4-sector boundary). */
  2958.  
  2959.   i = dirband_end - dirband_start + 1;
  2960.   if (a_what && IN_RANGE (what_sector, dirband_start, i))
  2961.     info ("Sector #%lu is in the DIRBLK band\n", what_sector);
  2962.   use_sectors (dirband_start, i, USE_BANDDIRBLK, NULL);
  2963.  
  2964.   /* The DIRBLK band has its own allocation bitmap.  Each bit maps one
  2965.      DIRBLK, that is, 4 sectors. */
  2966.  
  2967.   i = ULONG_FROM_FS (superb.superb.lsnDirBlkMap);
  2968.   if (a_info)
  2969.     info ("Sectors #%lu-#%lu: DIRBLK band bitmap\n", i, i + 3);
  2970.   if (a_what && IN_RANGE (what_sector, i, 4))
  2971.     info ("Sector #%lu is in the DIRBLK band bitmap (+%lu)\n",
  2972.           what_sector, what_sector - i);
  2973.   use_sectors (i, 4, USE_DIRBLKBITMAP, NULL);
  2974.  
  2975.   /* 8 sectors are reserved for storing user IDs.  Currently
  2976.      unused. */
  2977.  
  2978.   i = ULONG_FROM_FS (superb.superb.lsnSidTab);
  2979.   if (a_what && IN_RANGE (what_sector, i, 8))
  2980.     info ("Sector #%lu: User ID\n", what_sector);
  2981.   use_sectors (i, 8, USE_SID, NULL);
  2982.  
  2983.   /* Spare DIRBLKs.  If HPFS runs out of disk space for DIRBLKs in a
  2984.      split operation, it will use preallocated Spare DIRBLKs. */
  2985.  
  2986.   n = ULONG_FROM_FS (spareb.spareb.cdbMaxSpare);
  2987.   for (i = 0; i < n; ++i)
  2988.     {
  2989.       if (a_what
  2990.           && IN_RANGE (what_sector,
  2991.                        ULONG_FROM_FS (spareb.spareb.alsnSpareDirBlks[i]), 4))
  2992.         info ("Sector #%lu: Spare DIRBLK (+%lu)\n",
  2993.               what_sector,
  2994.               what_sector
  2995.               - ULONG_FROM_FS (spareb.spareb.alsnSpareDirBlks[i]));
  2996.       use_sectors (ULONG_FROM_FS (spareb.spareb.alsnSpareDirBlks[i]),
  2997.                    4, USE_SPAREDIRBLK, NULL);
  2998.     }
  2999.  
  3000.   /* Allocate the allocation vector which contains one bit per sector,
  3001.      indicating whether the sector is in use (0) or available (1).
  3002.      The vector will be filled in by do_bitmap(), called by
  3003.      do_bitmap_indirect().  */
  3004.  
  3005.   if (a_check || a_info || a_what)
  3006.     {
  3007.       total_alloc = DIVIDE_UP (total_sectors, 8);
  3008.       alloc_vector = (BYTE *)xmalloc (total_alloc);
  3009.       memset (alloc_vector, 0, total_alloc);
  3010.     }
  3011.  
  3012.   /* Check the Superblock and the Spare block. */
  3013.  
  3014.   if (a_check)
  3015.     {
  3016.       if (dirband_start > dirband_end)
  3017.         warning (1, "SUPERBLK #%lu: DIRBLK band start greater than "
  3018.                  "DIRBLK band end", (ULONG)16);
  3019.       if (dirband_sectors & 3)
  3020.         warning (1, "SUPERBLK #%lu: Number of DIRBLK band sectors is "
  3021.                  "not a multiple of 4", (ULONG)16);
  3022.       if (dirband_start + dirband_sectors - 1 != dirband_end)
  3023.         warning (1, "SUPERBLK #%lu: Wrong DIRBLK band size", (ULONG)16);
  3024.       if (ULONG_FROM_FS (superb.superb.lsnDirBlkMap) & 3)
  3025.         warning (1, "SUPERBLK #%lu: DIRBLK band bitmap not on a 2K boundary",
  3026.                  (ULONG)16);
  3027.  
  3028.       if (!(spareb.spareb.bFlag & SPF_HFUSED)
  3029.           != (ULONG_FROM_FS (spareb.spareb.culHotFixes) == 0))
  3030.         warning (1, "SPAREBLK #%lu: Hotfix bit is wrong", (ULONG)17);
  3031.       if (!(spareb.spareb.bFlag & SPF_BADSEC)
  3032.           != (ULONG_FROM_FS (superb.superb.culNumBadSects) == 0))
  3033.         warning (1, "SPAREBLK #%lu: Bad sector bit is wrong", (ULONG)17);
  3034.       if (!(spareb.spareb.bFlag & SPF_SPARE)
  3035.           != (ULONG_FROM_FS (spareb.spareb.cdbSpares)
  3036.               == ULONG_FROM_FS (spareb.spareb.cdbMaxSpare)))
  3037.         warning (1, "SPAREBLK #%lu: Spare DIRBLK bit is wrong", (ULONG)17);
  3038.       if (ULONG_FROM_FS (spareb.spareb.cdbSpares)
  3039.           > ULONG_FROM_FS (spareb.spareb.cdbMaxSpare))
  3040.         warning (1, "SPAREBLK #%lu: Number of free spare DIRBLKs exceeds "
  3041.                  "maximum number", (ULONG)17);
  3042.       if (ULONG_FROM_FS (spareb.spareb.aulExtra[0]) != superb_chksum)
  3043.         warning (1, "SPAREBLK #%lu: Incorrect checksum for Super block",
  3044.                  (ULONG)17);
  3045.       if (ULONG_FROM_FS (spareb.spareb.aulExtra[1]) != spareb_chksum)
  3046.         warning (1, "SPAREBLK #%lu: Incorrect checksum for Spare block",
  3047.                  (ULONG)17);
  3048.  
  3049.       if (superb.superb.bFuncVersion == 4)
  3050.         {
  3051.           /* `Multimedia format'.  To collect more samples for
  3052.              .bAlign, we display warnings if the values differ from
  3053.              those on my test partition.  I guess that .bAlign[0] is a
  3054.              set of flag bits and .bAlign[1] indicates the block size
  3055.              (1 << .bAlign[1] sectors per block).  .bAlign[2] is still
  3056.              unused. */
  3057.  
  3058.           if (spareb.spareb.bAlign[0] != 8)
  3059.             warning (0, "SPAREBLK #%lu: .bAlign[0] is %u", (ULONG)17,
  3060.                      spareb.spareb.bAlign[0]);
  3061.           if (spareb.spareb.bAlign[1] != 9)
  3062.             warning (0, "SPAREBLK #%lu: .bAlign[1] is %u", (ULONG)17,
  3063.                      spareb.spareb.bAlign[1]);
  3064.         }
  3065.       if (check_pedantic)
  3066.         {
  3067.           if (spareb.spareb.bAlign[2] != 0)
  3068.             warning (0, "SPAREBLK #%lu: .bAlign[2] is %u", (ULONG)17,
  3069.                      spareb.spareb.bAlign[2]);
  3070.         }
  3071.     }
  3072.  
  3073.   if (a_check || a_info || a_save || a_what)
  3074.     {
  3075.       /* Process the bad block list. */
  3076.  
  3077.       do_bad (d, ULONG_FROM_FS (superb.superb.rspBadBlkList.lsnMain),
  3078.               ULONG_FROM_FS (superb.superb.culNumBadSects));
  3079.  
  3080.       /* Process the hotfix list. */
  3081.  
  3082.       do_hotfix_list (d, ULONG_FROM_FS (spareb.spareb.lsnHotFix),
  3083.                       ULONG_FROM_FS (spareb.spareb.culMaxHotFixes));
  3084.     }
  3085.  
  3086.   /* Process the allocation bitmaps.  This fills in alloc_vector. */
  3087.  
  3088.   if (a_check || a_info || a_save || a_what)
  3089.     do_bitmap_indirect (d, ULONG_FROM_FS (superb.superb.rspBitMapIndBlk.lsnMain));
  3090.  
  3091.   /* Process the list of code page sectors. */
  3092.  
  3093.   if (a_check || a_info || a_save || a_what || a_find)
  3094.     do_cpinfosec (d, ULONG_FROM_FS (spareb.spareb.lsnCPInfo));
  3095.  
  3096.   /* Now comes the most interesting part: Walk through all directories
  3097.      and files, starting in the root directory. */
  3098.  
  3099.   file_count = 0; dir_count = 0;
  3100.   extents_init (&file_extents);
  3101.   extents_init (&ea_extents);
  3102.   if (a_check || a_save || a_what || a_find)
  3103.     {
  3104.       path_chain link, *plink;
  3105.  
  3106.       plink = PATH_CHAIN_NEW (&link, NULL, "");
  3107.       do_fnode (d, ULONG_FROM_FS (superb.superb.lsnRootFNode), plink,
  3108.                 TRUE, ULONG_FROM_FS (superb.superb.lsnRootFNode),
  3109.                 0, 0, FALSE, FALSE, (a_dir && *find_path == 0));
  3110.     }
  3111.  
  3112.   /* Process the DIRBLK bitmap. */
  3113.  
  3114.   if (a_check || a_save)
  3115.     do_dirblk_bitmap (d, ULONG_FROM_FS (superb.superb.lsnDirBlkMap),
  3116.                       dirband_start, dirband_sectors / 4);
  3117.  
  3118.   if (a_check)
  3119.     {
  3120.       /* Check the Spare DIRBLKs. */
  3121.  
  3122.       check_sparedirblk (spareb.spareb.alsnSpareDirBlks,
  3123.                          ULONG_FROM_FS (spareb.spareb.cdbMaxSpare),
  3124.                          ULONG_FROM_FS (spareb.spareb.cdbSpares));
  3125.  
  3126.       /* Check allocation bits. */
  3127.  
  3128.       check_alloc ();
  3129.  
  3130.       /* Show a summary. */
  3131.  
  3132.       if (show_summary)
  3133.         {
  3134.           info ("Number of directories: %lu\n", dir_count);
  3135.           info ("Number of files:       %lu\n", file_count);
  3136.           info ("Number of DIRBLKs:     %lu (%lu outside DIRBLK band)\n",
  3137.                 dirblk_total, dirblk_outside);
  3138.           info ("Number of ALSECs:      %lu\n", alsec_count);
  3139.         }
  3140.     }
  3141.  
  3142.   /* Show fragmentation of free space. */
  3143.  
  3144.   if (a_info && show_free_frag)
  3145.     do_free_frag ();
  3146.  
  3147.   /* Show fragmentation of files and extended attributes. */
  3148.  
  3149.   if (show_frag)
  3150.     {
  3151.       extents_show (&file_extents, "file data");
  3152.       extents_show (&ea_extents, "extended attributes");
  3153.     }
  3154.  
  3155.   /* Clean up. */
  3156.  
  3157.   extents_exit (&file_extents);
  3158.   extents_exit (&ea_extents);
  3159. }
  3160.