"Fossies" - the Fresh Open Source Software Archive

Member "dosfstools-4.2/src/fat.c" (31 Jan 2021, 19783 Bytes) of package /linux/misc/dosfstools-4.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "fat.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.1_vs_4.2.

    1 /* fat.c - Read/write access to the FAT
    2 
    3    Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
    4    Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
    5    Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch>
    6    Copyright (C) 2021 Pali Rohár <pali.rohar@gmail.com>
    7 
    8    This program is free software: you can redistribute it and/or modify
    9    it under the terms of the GNU General Public License as published by
   10    the Free Software Foundation, either version 3 of the License, or
   11    (at your option) any later version.
   12 
   13    This program is distributed in the hope that it will be useful,
   14    but WITHOUT ANY WARRANTY; without even the implied warranty of
   15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   16    GNU General Public License for more details.
   17 
   18    You should have received a copy of the GNU General Public License
   19    along with this program. If not, see <http://www.gnu.org/licenses/>.
   20 
   21    The complete text of the GNU General Public License
   22    can be found in /usr/share/common-licenses/GPL-3 file.
   23 */
   24 
   25 /* FAT32, VFAT, Atari format support, and various fixes additions May 1998
   26  * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
   27 
   28 #include <stdio.h>
   29 #include <stdlib.h>
   30 #include <string.h>
   31 #include <unistd.h>
   32 
   33 #include "common.h"
   34 #include "fsck.fat.h"
   35 #include "io.h"
   36 #include "boot.h"
   37 #include "check.h"
   38 #include "fat.h"
   39 
   40 /**
   41  * Fetch the FAT entry for a specified cluster.
   42  *
   43  * @param[out]  entry       Cluster to which cluster of interest is linked
   44  * @param[in]   fat     FAT table for the partition
   45  * @param[in]   cluster     Cluster of interest
   46  * @param[in]   fs          Information from the FAT boot sectors (bits per FAT entry)
   47  */
   48 void get_fat(FAT_ENTRY * entry, void *fat, uint32_t cluster, DOS_FS * fs)
   49 {
   50     unsigned char *ptr;
   51 
   52     if (cluster > fs->data_clusters + 1) {
   53     die("Internal error: cluster out of range in get_fat() (%lu > %lu).",
   54         (unsigned long)cluster, (unsigned long)(fs->data_clusters + 1));
   55     }
   56 
   57     switch (fs->fat_bits) {
   58     case 12:
   59     ptr = &((unsigned char *)fat)[cluster * 3 / 2];
   60     entry->value = 0xfff & (cluster & 1 ? (ptr[0] >> 4) | (ptr[1] << 4) :
   61                 (ptr[0] | ptr[1] << 8));
   62     break;
   63     case 16:
   64     entry->value = le16toh(((unsigned short *)fat)[cluster]);
   65     break;
   66     case 32:
   67     /* According to M$, the high 4 bits of a FAT32 entry are reserved and
   68      * are not part of the cluster number. So we cut them off. */
   69     {
   70         uint32_t e = le32toh(((unsigned int *)fat)[cluster]);
   71         entry->value = e & 0xfffffff;
   72         entry->reserved = e >> 28;
   73     }
   74     break;
   75     default:
   76     die("Bad FAT entry size: %d bits.", fs->fat_bits);
   77     }
   78 }
   79 
   80 void release_fat(DOS_FS * fs)
   81 {
   82     if (fs->fat)
   83     free(fs->fat);
   84     if (fs->cluster_owner)
   85     free(fs->cluster_owner);
   86     fs->fat = NULL;
   87     fs->cluster_owner = NULL;
   88 }
   89 
   90 static void fix_first_cluster(DOS_FS * fs, void * first_cluster)
   91 {
   92     struct boot_sector b;
   93 
   94     fs_read(0, sizeof(b), &b);
   95 
   96     printf("Fixing first cluster in FAT.\n");
   97     if (fs->fat_bits == 12)
   98         *(uint16_t *)first_cluster = htole16((le16toh(*(uint16_t *)first_cluster) & 0xf000) | FAT_EXTD(fs) | b.media);
   99     else if (fs->fat_bits == 16)
  100         *(uint16_t *)first_cluster = htole16(FAT_EXTD(fs) | b.media);
  101     else
  102         *(uint32_t *)first_cluster = htole32(FAT_EXTD(fs) | b.media);
  103 }
  104 
  105 /**
  106  * Build a bookkeeping structure from the partition's FAT table.
  107  * If the partition has multiple FATs and they don't agree, try to pick a winner,
  108  * and queue a command to overwrite the loser.
  109  * One error that is fixed here is a cluster that links to something out of range.
  110  *
  111  * @param[inout]    fs      Information about the filesystem
  112  * @param[in]       mode    0 - read-only, 1 - read-write (no repair), 2 - repair
  113  */
  114 void read_fat(DOS_FS * fs, int mode)
  115 {
  116     int eff_size, alloc_size;
  117     uint32_t i;
  118     void *first, *second = NULL;
  119     int first_ok, second_ok = 0;
  120     FAT_ENTRY first_media, second_media;
  121     uint32_t total_num_clusters;
  122 
  123     if (fat_table > fs->nfats)
  124         die("Requested FAT table %ld does not exist.", fat_table);
  125     if (fat_table > 2)
  126         die("Reading FAT table greather than 2 is implemented yet.");
  127 
  128     /* Clean up from previous pass */
  129     release_fat(fs);
  130 
  131     total_num_clusters = fs->data_clusters + 2;
  132     eff_size = (total_num_clusters * fs->fat_bits + 7) / 8ULL;
  133 
  134     if (fs->fat_bits != 12)
  135         alloc_size = eff_size;
  136     else
  137         /* round up to an even number of FAT entries to avoid special
  138          * casing the last entry in get_fat() */
  139         alloc_size = (total_num_clusters * 12 + 23) / 24 * 3;
  140 
  141     first = alloc(alloc_size);
  142     fs_read(fs->fat_start, eff_size, first);
  143     get_fat(&first_media, first, 0, fs);
  144     first_ok = (first_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs);
  145     if (fs->nfats > 1) {
  146     second = alloc(alloc_size);
  147     fs_read(fs->fat_start + fs->fat_size, eff_size, second);
  148     get_fat(&second_media, second, 0, fs);
  149     second_ok = (second_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs);
  150     }
  151     if (mode != 0 && fat_table == 0) {
  152         if (!first_ok && second && !second_ok)
  153             die("Both FATs appear to be corrupt. Giving up. Run fsck.fat with non-zero -F option.");
  154         if (!first_ok && !second)
  155             die("First FAT appears to be corrupt and second FAT does not exist. Giving up. Run fsck.fat with -F 1 option.");
  156     }
  157     if (mode == 0 && !first_ok && second && second_ok) {
  158         /* In read-only mode if first FAT is corrupted and second is OK then use second FAT */
  159         void *first_backup = first;
  160         first = second;
  161         second = first_backup;
  162     }
  163     if (mode != 0 && fat_table == 0 && second && memcmp(first, second, eff_size) != 0) {
  164     if (mode != 2)
  165         die("FATs differ, please run fsck.fat");
  166     if (first_ok && !second_ok) {
  167         printf("FATs differ - using first FAT.\n");
  168         fs_write(fs->fat_start + fs->fat_size, eff_size, first);
  169     } else if (!first_ok && second_ok) {
  170         printf("FATs differ - using second FAT.\n");
  171         fs_write(fs->fat_start, eff_size, second);
  172         memcpy(first, second, eff_size);
  173     } else {
  174         if (first_ok && second_ok)
  175         printf("FATs differ but appear to be intact.\n");
  176         else
  177         printf("FATs differ and both appear to be corrupt.\n");
  178         if (get_choice(1, "  Using first FAT.",
  179                2,
  180                1, "Use first FAT",
  181                2, "Use second FAT") == 1) {
  182         if (!first_ok) {
  183             fix_first_cluster(fs, first);
  184             fs_write(fs->fat_start, (fs->fat_bits + 7) / 8, first);
  185         }
  186         fs_write(fs->fat_start + fs->fat_size, eff_size, first);
  187         } else {
  188         if (!second_ok) {
  189             fix_first_cluster(fs, second);
  190             fs_write(fs->fat_start + fs->fat_size, (fs->fat_bits + 7) / 8, second);
  191         }
  192         fs_write(fs->fat_start, eff_size, second);
  193         memcpy(first, second, eff_size);
  194         }
  195     }
  196     }
  197     if (mode != 0 && fat_table != 0) {
  198         if (fat_table == 1) {
  199             printf("Using first FAT.\n");
  200             if (!first_ok) {
  201                 fix_first_cluster(fs, first);
  202                 fs_write(fs->fat_start, (fs->fat_bits + 7) / 8, first);
  203             }
  204             if (second && memcmp(first, second, eff_size) != 0)
  205                 fs_write(fs->fat_start + fs->fat_size, eff_size, first);
  206         } else if (fat_table == 2) {
  207             printf("Using second FAT.\n");
  208             if (!second_ok) {
  209                 fix_first_cluster(fs, second);
  210                 fs_write(fs->fat_start + fs->fat_size, (fs->fat_bits + 7) / 8, second);
  211             }
  212             if (memcmp(first, second, eff_size) != 0) {
  213                 fs_write(fs->fat_start, eff_size, second);
  214                 memcpy(first, second, eff_size);
  215             }
  216         }
  217     }
  218     if (second) {
  219     free(second);
  220     }
  221     fs->fat = (unsigned char *)first;
  222 
  223     fs->cluster_owner = alloc(total_num_clusters * sizeof(DOS_FILE *));
  224     memset(fs->cluster_owner, 0, (total_num_clusters * sizeof(DOS_FILE *)));
  225 
  226     if (mode == 0)
  227         return;
  228 
  229     /* Truncate any cluster chains that link to something out of range */
  230     for (i = 2; i < fs->data_clusters + 2; i++) {
  231     FAT_ENTRY curEntry;
  232     get_fat(&curEntry, fs->fat, i, fs);
  233     if (curEntry.value == 1) {
  234         if (mode != 2)
  235         die("Cluster %ld out of range (1), please run fsck.fat",
  236             (long)(i - 2));
  237         printf("Cluster %ld out of range (1). Setting to EOF.\n",
  238            (long)(i - 2));
  239         set_fat(fs, i, -1);
  240     }
  241     if (curEntry.value >= fs->data_clusters + 2 &&
  242         (curEntry.value < FAT_MIN_BAD(fs))) {
  243         if (mode != 2)
  244         die("Cluster %ld out of range (%ld > %ld), please run fsck.fat",
  245             (long)(i - 2), (long)curEntry.value,
  246             (long)(fs->data_clusters + 2 - 1));
  247         printf("Cluster %ld out of range (%ld > %ld). Setting to EOF.\n",
  248            (long)(i - 2), (long)curEntry.value,
  249            (long)(fs->data_clusters + 2 - 1));
  250         set_fat(fs, i, -1);
  251     }
  252     }
  253 }
  254 
  255 /**
  256  * Update the FAT entry for a specified cluster
  257  * (i.e., change the cluster it links to).
  258  * Queue a command to write out this change.
  259  *
  260  * @param[in,out]   fs          Information about the filesystem
  261  * @param[in]       cluster     Cluster to change
  262  * @param[in]       new         Cluster to link to
  263  *              Special values:
  264  *                 0 == free cluster
  265  *                -1 == end-of-chain
  266  *                -2 == bad cluster
  267  */
  268 void set_fat(DOS_FS * fs, uint32_t cluster, int32_t new)
  269 {
  270     unsigned char *data = NULL;
  271     int size;
  272     off_t offs;
  273 
  274     if (cluster > fs->data_clusters + 1) {
  275     die("Internal error: cluster out of range in set_fat() (%lu > %lu).",
  276         (unsigned long)cluster, (unsigned long)(fs->data_clusters + 1));
  277     }
  278 
  279     if (new == -1)
  280     new = FAT_EOF(fs);
  281     else if ((long)new == -2)
  282     new = FAT_BAD(fs);
  283     else if (new > fs->data_clusters + 1) {
  284     die("Internal error: new cluster out of range in set_fat() (%lu > %lu).",
  285         (unsigned long)new, (unsigned long)(fs->data_clusters + 1));
  286     }
  287 
  288     switch (fs->fat_bits) {
  289     case 12:
  290     data = fs->fat + cluster * 3 / 2;
  291     offs = fs->fat_start + cluster * 3 / 2;
  292     if (cluster & 1) {
  293         FAT_ENTRY prevEntry;
  294         get_fat(&prevEntry, fs->fat, cluster - 1, fs);
  295         data[0] = ((new & 0xf) << 4) | (prevEntry.value >> 8);
  296         data[1] = new >> 4;
  297     } else {
  298         FAT_ENTRY subseqEntry;
  299         if (cluster != fs->data_clusters + 1)
  300         get_fat(&subseqEntry, fs->fat, cluster + 1, fs);
  301         else
  302         subseqEntry.value = 0;
  303         data[0] = new & 0xff;
  304         data[1] = (new >> 8) | ((0xff & subseqEntry.value) << 4);
  305     }
  306     size = 2;
  307     break;
  308     case 16:
  309     data = fs->fat + cluster * 2;
  310     offs = fs->fat_start + cluster * 2;
  311     *(unsigned short *)data = htole16(new);
  312     size = 2;
  313     break;
  314     case 32:
  315     {
  316         FAT_ENTRY curEntry;
  317         get_fat(&curEntry, fs->fat, cluster, fs);
  318 
  319         data = fs->fat + cluster * 4;
  320         offs = fs->fat_start + cluster * 4;
  321         /* According to M$, the high 4 bits of a FAT32 entry are reserved and
  322          * are not part of the cluster number. So we never touch them. */
  323         *(uint32_t *)data = htole32((new & 0xfffffff) |
  324                          (curEntry.reserved << 28));
  325         size = 4;
  326     }
  327     break;
  328     default:
  329     die("Bad FAT entry size: %d bits.", fs->fat_bits);
  330     }
  331     fs_write(offs, size, data);
  332     if (fs->nfats > 1) {
  333     fs_write(offs + fs->fat_size, size, data);
  334     }
  335 }
  336 
  337 int bad_cluster(DOS_FS * fs, uint32_t cluster)
  338 {
  339     FAT_ENTRY curEntry;
  340     get_fat(&curEntry, fs->fat, cluster, fs);
  341 
  342     return FAT_IS_BAD(fs, curEntry.value);
  343 }
  344 
  345 /**
  346  * Get the cluster to which the specified cluster is linked.
  347  * If the linked cluster is marked bad, abort.
  348  *
  349  * @param[in]   fs          Information about the filesystem
  350  * @param[in]   cluster     Cluster to follow
  351  *
  352  * @return  -1              'cluster' is at the end of the chain
  353  * @return  Other values    Next cluster in this chain
  354  */
  355 uint32_t next_cluster(DOS_FS * fs, uint32_t cluster)
  356 {
  357     uint32_t value;
  358     FAT_ENTRY curEntry;
  359 
  360     get_fat(&curEntry, fs->fat, cluster, fs);
  361 
  362     value = curEntry.value;
  363     if (FAT_IS_BAD(fs, value))
  364     die("Internal error: next_cluster on bad cluster");
  365     return FAT_IS_EOF(fs, value) ? -1 : value;
  366 }
  367 
  368 off_t cluster_start(DOS_FS * fs, uint32_t cluster)
  369 {
  370     /* TODO: check overflow */
  371     return fs->data_start + ((off_t)cluster - 2) * (unsigned long long)fs->cluster_size;
  372 }
  373 
  374 /**
  375  * Update internal bookkeeping to show that the specified cluster belongs
  376  * to the specified dentry.
  377  *
  378  * @param[in,out]   fs          Information about the filesystem
  379  * @param[in]       cluster     Cluster being assigned
  380  * @param[in]       owner       Information on dentry that owns this cluster
  381  *                              (may be NULL)
  382  */
  383 void set_owner(DOS_FS * fs, uint32_t cluster, DOS_FILE * owner)
  384 {
  385     if (fs->cluster_owner == NULL)
  386     die("Internal error: attempt to set owner in non-existent table");
  387 
  388     if (owner && fs->cluster_owner[cluster]
  389     && (fs->cluster_owner[cluster] != owner))
  390     die("Internal error: attempt to change file owner");
  391     fs->cluster_owner[cluster] = owner;
  392 }
  393 
  394 DOS_FILE *get_owner(DOS_FS * fs, uint32_t cluster)
  395 {
  396     if (fs->cluster_owner == NULL)
  397     return NULL;
  398     else
  399     return fs->cluster_owner[cluster];
  400 }
  401 
  402 void fix_bad(DOS_FS * fs)
  403 {
  404     uint32_t i;
  405 
  406     if (verbose)
  407     printf("Checking for bad clusters.\n");
  408     for (i = 2; i < fs->data_clusters + 2; i++) {
  409     FAT_ENTRY curEntry;
  410     get_fat(&curEntry, fs->fat, i, fs);
  411 
  412     if (!get_owner(fs, i) && !FAT_IS_BAD(fs, curEntry.value))
  413         if (!fs_test(cluster_start(fs, i), fs->cluster_size)) {
  414         printf("Cluster %lu is unreadable.\n", (unsigned long)i);
  415         set_fat(fs, i, -2);
  416         }
  417     }
  418 }
  419 
  420 void reclaim_free(DOS_FS * fs)
  421 {
  422     int reclaimed;
  423     uint32_t i;
  424 
  425     if (verbose)
  426     printf("Checking for unused clusters.\n");
  427     reclaimed = 0;
  428     for (i = 2; i < fs->data_clusters + 2; i++) {
  429     FAT_ENTRY curEntry;
  430     get_fat(&curEntry, fs->fat, i, fs);
  431 
  432     if (!get_owner(fs, i) && curEntry.value &&
  433         !FAT_IS_BAD(fs, curEntry.value)) {
  434         set_fat(fs, i, 0);
  435         reclaimed++;
  436     }
  437     }
  438     if (reclaimed)
  439     printf("Reclaimed %d unused cluster%s (%llu bytes).\n", (int)reclaimed,
  440            reclaimed == 1 ? "" : "s",
  441            (unsigned long long)reclaimed * fs->cluster_size);
  442 }
  443 
  444 /**
  445  * Assign the specified owner to all orphan chains (except cycles).
  446  * Break cross-links between orphan chains.
  447  *
  448  * @param[in,out]   fs             Information about the filesystem
  449  * @param[in]       owner          dentry to be assigned ownership of orphans
  450  * @param[in,out]   num_refs       For each orphan cluster [index], how many
  451  *                 clusters link to it.
  452  * @param[in]       start_cluster  Where to start scanning for orphans
  453  */
  454 static void tag_free(DOS_FS * fs, DOS_FILE * owner, uint32_t *num_refs,
  455              uint32_t start_cluster)
  456 {
  457     int prev;
  458     uint32_t i, walk;
  459 
  460     if (start_cluster == 0)
  461     start_cluster = 2;
  462 
  463     for (i = start_cluster; i < fs->data_clusters + 2; i++) {
  464     FAT_ENTRY curEntry;
  465     get_fat(&curEntry, fs->fat, i, fs);
  466 
  467     /* If the current entry is the head of an un-owned chain... */
  468     if (curEntry.value && !FAT_IS_BAD(fs, curEntry.value) &&
  469         !get_owner(fs, i) && !num_refs[i]) {
  470         prev = 0;
  471         /* Walk the chain, claiming ownership as we go */
  472         for (walk = i; walk != -1; walk = next_cluster(fs, walk)) {
  473         if (!get_owner(fs, walk)) {
  474             set_owner(fs, walk, owner);
  475         } else {
  476             /* We've run into cross-links between orphaned chains,
  477              * or a cycle with a tail.
  478              * Terminate this orphan chain (break the link)
  479              */
  480             set_fat(fs, prev, -1);
  481 
  482             /* This is not necessary because 'walk' is owned and thus
  483              * will never become the head of a chain (the only case
  484              * that would matter during reclaim to files).
  485              * It's easier to decrement than to prove that it's
  486              * unnecessary.
  487              */
  488             num_refs[walk]--;
  489             break;
  490         }
  491         prev = walk;
  492         }
  493     }
  494     }
  495 }
  496 
  497 /**
  498  * Recover orphan chains to files, handling any cycles or cross-links.
  499  *
  500  * @param[in,out]   fs             Information about the filesystem
  501  */
  502 void reclaim_file(DOS_FS * fs)
  503 {
  504     DOS_FILE orphan;
  505     int reclaimed, files;
  506     int changed = 0;
  507     uint32_t i, next, walk;
  508     uint32_t *num_refs = NULL;  /* Only for orphaned clusters */
  509     uint32_t total_num_clusters;
  510 
  511     if (verbose)
  512     printf("Reclaiming unconnected clusters.\n");
  513 
  514     total_num_clusters = fs->data_clusters + 2;
  515     num_refs = alloc(total_num_clusters * sizeof(uint32_t));
  516     memset(num_refs, 0, (total_num_clusters * sizeof(uint32_t)));
  517 
  518     /* Guarantee that all orphan chains (except cycles) end cleanly
  519      * with an end-of-chain mark.
  520      */
  521 
  522     for (i = 2; i < total_num_clusters; i++) {
  523     FAT_ENTRY curEntry;
  524     get_fat(&curEntry, fs->fat, i, fs);
  525 
  526     next = curEntry.value;
  527     if (!get_owner(fs, i) && next && next < fs->data_clusters + 2) {
  528         /* Cluster is linked, but not owned (orphan) */
  529         FAT_ENTRY nextEntry;
  530         get_fat(&nextEntry, fs->fat, next, fs);
  531 
  532         /* Mark it end-of-chain if it links into an owned cluster,
  533          * a free cluster, or a bad cluster.
  534          */
  535         if (get_owner(fs, next) || !nextEntry.value ||
  536         FAT_IS_BAD(fs, nextEntry.value))
  537         set_fat(fs, i, -1);
  538         else
  539         num_refs[next]++;
  540     }
  541     }
  542 
  543     /* Scan until all the orphans are accounted for,
  544      * and all cycles and cross-links are broken
  545      */
  546     do {
  547     tag_free(fs, &orphan, num_refs, changed);
  548     changed = 0;
  549 
  550     /* Any unaccounted-for orphans must be part of a cycle */
  551     for (i = 2; i < total_num_clusters; i++) {
  552         FAT_ENTRY curEntry;
  553         get_fat(&curEntry, fs->fat, i, fs);
  554 
  555         if (curEntry.value && !FAT_IS_BAD(fs, curEntry.value) &&
  556         !get_owner(fs, i)) {
  557         if (!num_refs[curEntry.value]--)
  558             die("Internal error: num_refs going below zero");
  559         set_fat(fs, i, -1);
  560         changed = curEntry.value;
  561         printf("Broke cycle at cluster %lu in free chain.\n", (unsigned long)i);
  562 
  563         /* If we've created a new chain head,
  564          * tag_free() can claim it
  565          */
  566         if (num_refs[curEntry.value] == 0)
  567             break;
  568         }
  569     }
  570     }
  571     while (changed);
  572 
  573     /* Now we can start recovery */
  574     files = reclaimed = 0;
  575     for (i = 2; i < total_num_clusters; i++)
  576     /* If this cluster is the head of an orphan chain... */
  577     if (get_owner(fs, i) == &orphan && !num_refs[i]) {
  578         DIR_ENT de;
  579         off_t offset;
  580         files++;
  581         offset = alloc_rootdir_entry(fs, &de, "FSCK%04dREC", 1);
  582         de.start = htole16(i & 0xffff);
  583         if (fs->fat_bits == 32)
  584         de.starthi = htole16(i >> 16);
  585         for (walk = i; walk > 0 && walk != -1;
  586          walk = next_cluster(fs, walk)) {
  587         de.size = htole32(le32toh(de.size) + fs->cluster_size);
  588         reclaimed++;
  589         }
  590         fs_write(offset, sizeof(DIR_ENT), &de);
  591     }
  592     if (reclaimed)
  593     printf("Reclaimed %d unused cluster%s (%llu bytes) in %d chain%s.\n",
  594            reclaimed, reclaimed == 1 ? "" : "s",
  595            (unsigned long long)reclaimed * fs->cluster_size, files,
  596            files == 1 ? "" : "s");
  597 
  598     free(num_refs);
  599 }
  600 
  601 uint32_t update_free(DOS_FS * fs)
  602 {
  603     uint32_t i;
  604     uint32_t free = 0;
  605     int do_set = 0;
  606 
  607     for (i = 2; i < fs->data_clusters + 2; i++) {
  608     FAT_ENTRY curEntry;
  609     get_fat(&curEntry, fs->fat, i, fs);
  610 
  611     if (!get_owner(fs, i) && !FAT_IS_BAD(fs, curEntry.value))
  612         ++free;
  613     }
  614 
  615     if (!fs->fsinfo_start)
  616     return free;
  617 
  618     if (verbose)
  619     printf("Checking free cluster summary.\n");
  620     if (fs->free_clusters != 0xFFFFFFFF) {
  621     if (free != fs->free_clusters) {
  622         printf("Free cluster summary wrong (%ld vs. really %ld)\n",
  623            (long)fs->free_clusters, (long)free);
  624         if (get_choice(1, "  Auto-correcting.",
  625                2,
  626                1, "Correct",
  627                2, "Don't correct") == 1)
  628         do_set = 1;
  629     }
  630     } else {
  631     printf("Free cluster summary uninitialized (should be %ld)\n", (long)free);
  632     if (rw) {
  633         if (get_choice(1, "  Auto-setting.",
  634                2,
  635                1, "Set it",
  636                2, "Leave it uninitialized") == 1)
  637         do_set = 1;
  638     }
  639     }
  640 
  641     if (do_set) {
  642     uint32_t le_free = htole32(free);
  643     fs->free_clusters = free;
  644     fs_write(fs->fsinfo_start + offsetof(struct info_sector, free_clusters),
  645          sizeof(le_free), &le_free);
  646     }
  647 
  648     return free;
  649 }