"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/fat.c" between
dosfstools-4.1.tar.gz and dosfstools-4.2.tar.gz

About: dosfstools are utilities to create, check and label (MS-DOS) FAT filesystems.

fat.c  (dosfstools-4.1):fat.c  (dosfstools-4.2)
/* fat.c - Read/write access to the FAT /* fat.c - Read/write access to the FAT
Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch> Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch> Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch>
Copyright (C) 2021 Pali Rohár <pali.rohar@gmail.com>
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
skipping to change at line 35 skipping to change at line 36
* by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */ * by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include "common.h" #include "common.h"
#include "fsck.fat.h" #include "fsck.fat.h"
#include "io.h" #include "io.h"
#include "boot.h"
#include "check.h" #include "check.h"
#include "fat.h" #include "fat.h"
/** /**
* Fetch the FAT entry for a specified cluster. * Fetch the FAT entry for a specified cluster.
* *
* @param[out] entry Cluster to which cluster of interest is linked * @param[out] entry Cluster to which cluster of interest is linked
* @param[in] fat FAT table for the partition * @param[in] fat FAT table for the partition
* @param[in] cluster Cluster of interest * @param[in] cluster Cluster of interest
* @param[in] fs Information from the FAT boot sectors (bits per FAT e ntry) * @param[in] fs Information from the FAT boot sectors (bits per FAT e ntry)
skipping to change at line 78 skipping to change at line 80
uint32_t e = le32toh(((unsigned int *)fat)[cluster]); uint32_t e = le32toh(((unsigned int *)fat)[cluster]);
entry->value = e & 0xfffffff; entry->value = e & 0xfffffff;
entry->reserved = e >> 28; entry->reserved = e >> 28;
} }
break; break;
default: default:
die("Bad FAT entry size: %d bits.", fs->fat_bits); die("Bad FAT entry size: %d bits.", fs->fat_bits);
} }
} }
void release_fat(DOS_FS * fs)
{
if (fs->fat)
free(fs->fat);
if (fs->cluster_owner)
free(fs->cluster_owner);
fs->fat = NULL;
fs->cluster_owner = NULL;
}
static void fix_first_cluster(DOS_FS * fs, void * first_cluster)
{
struct boot_sector b;
fs_read(0, sizeof(b), &b);
printf("Fixing first cluster in FAT.\n");
if (fs->fat_bits == 12)
*(uint16_t *)first_cluster = htole16((le16toh(*(uint16_t *)first_cluster
) & 0xf000) | FAT_EXTD(fs) | b.media);
else if (fs->fat_bits == 16)
*(uint16_t *)first_cluster = htole16(FAT_EXTD(fs) | b.media);
else
*(uint32_t *)first_cluster = htole32(FAT_EXTD(fs) | b.media);
}
/** /**
* Build a bookkeeping structure from the partition's FAT table. * Build a bookkeeping structure from the partition's FAT table.
* If the partition has multiple FATs and they don't agree, try to pick a winner , * If the partition has multiple FATs and they don't agree, try to pick a winner ,
* and queue a command to overwrite the loser. * and queue a command to overwrite the loser.
* One error that is fixed here is a cluster that links to something out of rang e. * One error that is fixed here is a cluster that links to something out of rang e.
* *
* @param[inout] fs Information about the filesystem * @param[inout] fs Information about the filesystem
* @param[in] mode 0 - read-only, 1 - read-write (no repair), 2 - repai r
*/ */
void read_fat(DOS_FS * fs) void read_fat(DOS_FS * fs, int mode)
{ {
int eff_size, alloc_size; int eff_size, alloc_size;
uint32_t i; uint32_t i;
void *first, *second = NULL; void *first, *second = NULL;
int first_ok, second_ok; int first_ok, second_ok = 0;
FAT_ENTRY first_media, second_media;
uint32_t total_num_clusters; uint32_t total_num_clusters;
if (fat_table > fs->nfats)
die("Requested FAT table %ld does not exist.", fat_table);
if (fat_table > 2)
die("Reading FAT table greather than 2 is implemented yet.");
/* Clean up from previous pass */ /* Clean up from previous pass */
if (fs->fat) release_fat(fs);
free(fs->fat);
if (fs->cluster_owner)
free(fs->cluster_owner);
fs->fat = NULL;
fs->cluster_owner = NULL;
total_num_clusters = fs->data_clusters + 2; total_num_clusters = fs->data_clusters + 2;
eff_size = (total_num_clusters * fs->fat_bits + 7) / 8ULL; eff_size = (total_num_clusters * fs->fat_bits + 7) / 8ULL;
if (fs->fat_bits != 12) if (fs->fat_bits != 12)
alloc_size = eff_size; alloc_size = eff_size;
else else
/* round up to an even number of FAT entries to avoid special /* round up to an even number of FAT entries to avoid special
* casing the last entry in get_fat() */ * casing the last entry in get_fat() */
alloc_size = (total_num_clusters * 12 + 23) / 24 * 3; alloc_size = (total_num_clusters * 12 + 23) / 24 * 3;
first = alloc(alloc_size); first = alloc(alloc_size);
fs_read(fs->fat_start, eff_size, first); fs_read(fs->fat_start, eff_size, first);
get_fat(&first_media, first, 0, fs);
first_ok = (first_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs);
if (fs->nfats > 1) { if (fs->nfats > 1) {
second = alloc(alloc_size); second = alloc(alloc_size);
fs_read(fs->fat_start + fs->fat_size, eff_size, second); fs_read(fs->fat_start + fs->fat_size, eff_size, second);
}
if (second && memcmp(first, second, eff_size) != 0) {
FAT_ENTRY first_media, second_media;
get_fat(&first_media, first, 0, fs);
get_fat(&second_media, second, 0, fs); get_fat(&second_media, second, 0, fs);
first_ok = (first_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs);
second_ok = (second_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs); second_ok = (second_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs);
}
if (mode != 0 && fat_table == 0) {
if (!first_ok && second && !second_ok)
die("Both FATs appear to be corrupt. Giving up. Run fsck.fat with no
n-zero -F option.");
if (!first_ok && !second)
die("First FAT appears to be corrupt and second FAT does not exist.
Giving up. Run fsck.fat with -F 1 option.");
}
if (mode == 0 && !first_ok && second && second_ok) {
/* In read-only mode if first FAT is corrupted and second is OK then use
second FAT */
void *first_backup = first;
first = second;
second = first_backup;
}
if (mode != 0 && fat_table == 0 && second && memcmp(first, second, eff_size)
!= 0) {
if (mode != 2)
die("FATs differ, please run fsck.fat");
if (first_ok && !second_ok) { if (first_ok && !second_ok) {
printf("FATs differ - using first FAT.\n"); printf("FATs differ - using first FAT.\n");
fs_write(fs->fat_start + fs->fat_size, eff_size, first); fs_write(fs->fat_start + fs->fat_size, eff_size, first);
} } else if (!first_ok && second_ok) {
if (!first_ok && second_ok) {
printf("FATs differ - using second FAT.\n"); printf("FATs differ - using second FAT.\n");
fs_write(fs->fat_start, eff_size, second); fs_write(fs->fat_start, eff_size, second);
memcpy(first, second, eff_size); memcpy(first, second, eff_size);
} } else {
if (first_ok && second_ok) { if (first_ok && second_ok)
if (interactive) { printf("FATs differ but appear to be intact.\n");
printf("FATs differ but appear to be intact. Use which FAT ?\n" else
"1) Use first FAT\n2) Use second FAT\n"); printf("FATs differ and both appear to be corrupt.\n");
if (get_key("12", "?") == '1') { if (get_choice(1, " Using first FAT.",
fs_write(fs->fat_start + fs->fat_size, eff_size, first); 2,
} else { 1, "Use first FAT",
fs_write(fs->fat_start, eff_size, second); 2, "Use second FAT") == 1) {
memcpy(first, second, eff_size); if (!first_ok) {
fix_first_cluster(fs, first);
fs_write(fs->fat_start, (fs->fat_bits + 7) / 8, first);
} }
} else {
printf("FATs differ but appear to be intact. Using first "
"FAT.\n");
fs_write(fs->fat_start + fs->fat_size, eff_size, first); fs_write(fs->fat_start + fs->fat_size, eff_size, first);
} else {
if (!second_ok) {
fix_first_cluster(fs, second);
fs_write(fs->fat_start + fs->fat_size, (fs->fat_bits + 7) / 8
, second);
}
fs_write(fs->fat_start, eff_size, second);
memcpy(first, second, eff_size);
} }
} }
if (!first_ok && !second_ok) { }
printf("Both FATs appear to be corrupt. Giving up.\n"); if (mode != 0 && fat_table != 0) {
exit(1); if (fat_table == 1) {
} printf("Using first FAT.\n");
if (!first_ok) {
fix_first_cluster(fs, first);
fs_write(fs->fat_start, (fs->fat_bits + 7) / 8, first);
}
if (second && memcmp(first, second, eff_size) != 0)
fs_write(fs->fat_start + fs->fat_size, eff_size, first);
} else if (fat_table == 2) {
printf("Using second FAT.\n");
if (!second_ok) {
fix_first_cluster(fs, second);
fs_write(fs->fat_start + fs->fat_size, (fs->fat_bits + 7) / 8, s
econd);
}
if (memcmp(first, second, eff_size) != 0) {
fs_write(fs->fat_start, eff_size, second);
memcpy(first, second, eff_size);
}
}
} }
if (second) { if (second) {
free(second); free(second);
} }
fs->fat = (unsigned char *)first; fs->fat = (unsigned char *)first;
fs->cluster_owner = alloc(total_num_clusters * sizeof(DOS_FILE *)); fs->cluster_owner = alloc(total_num_clusters * sizeof(DOS_FILE *));
memset(fs->cluster_owner, 0, (total_num_clusters * sizeof(DOS_FILE *))); memset(fs->cluster_owner, 0, (total_num_clusters * sizeof(DOS_FILE *)));
if (mode == 0)
return;
/* Truncate any cluster chains that link to something out of range */ /* Truncate any cluster chains that link to something out of range */
for (i = 2; i < fs->data_clusters + 2; i++) { for (i = 2; i < fs->data_clusters + 2; i++) {
FAT_ENTRY curEntry; FAT_ENTRY curEntry;
get_fat(&curEntry, fs->fat, i, fs); get_fat(&curEntry, fs->fat, i, fs);
if (curEntry.value == 1) { if (curEntry.value == 1) {
if (mode != 2)
die("Cluster %ld out of range (1), please run fsck.fat",
(long)(i - 2));
printf("Cluster %ld out of range (1). Setting to EOF.\n", printf("Cluster %ld out of range (1). Setting to EOF.\n",
(long)(i - 2)); (long)(i - 2));
set_fat(fs, i, -1); set_fat(fs, i, -1);
} }
if (curEntry.value >= fs->data_clusters + 2 && if (curEntry.value >= fs->data_clusters + 2 &&
(curEntry.value < FAT_MIN_BAD(fs))) { (curEntry.value < FAT_MIN_BAD(fs))) {
if (mode != 2)
die("Cluster %ld out of range (%ld > %ld), please run fsck.fat",
(long)(i - 2), (long)curEntry.value,
(long)(fs->data_clusters + 2 - 1));
printf("Cluster %ld out of range (%ld > %ld). Setting to EOF.\n", printf("Cluster %ld out of range (%ld > %ld). Setting to EOF.\n",
(long)(i - 2), (long)curEntry.value, (long)(i - 2), (long)curEntry.value,
(long)(fs->data_clusters + 2 - 1)); (long)(fs->data_clusters + 2 - 1));
set_fat(fs, i, -1); set_fat(fs, i, -1);
} }
} }
} }
/** /**
* Update the FAT entry for a specified cluster * Update the FAT entry for a specified cluster
skipping to change at line 296 skipping to change at line 370
get_fat(&curEntry, fs->fat, cluster, fs); get_fat(&curEntry, fs->fat, cluster, fs);
value = curEntry.value; value = curEntry.value;
if (FAT_IS_BAD(fs, value)) if (FAT_IS_BAD(fs, value))
die("Internal error: next_cluster on bad cluster"); die("Internal error: next_cluster on bad cluster");
return FAT_IS_EOF(fs, value) ? -1 : value; return FAT_IS_EOF(fs, value) ? -1 : value;
} }
off_t cluster_start(DOS_FS * fs, uint32_t cluster) off_t cluster_start(DOS_FS * fs, uint32_t cluster)
{ {
return fs->data_start + ((off_t)cluster - 2) * (uint64_t)fs->cluster_size; /* TODO: check overflow */
return fs->data_start + ((off_t)cluster - 2) * (unsigned long long)fs->clust
er_size;
} }
/** /**
* Update internal bookkeeping to show that the specified cluster belongs * Update internal bookkeeping to show that the specified cluster belongs
* to the specified dentry. * to the specified dentry.
* *
* @param[in,out] fs Information about the filesystem * @param[in,out] fs Information about the filesystem
* @param[in] cluster Cluster being assigned * @param[in] cluster Cluster being assigned
* @param[in] owner Information on dentry that owns this cluster * @param[in] owner Information on dentry that owns this cluster
* (may be NULL) * (may be NULL)
skipping to change at line 549 skipping to change at line 624
if (!fs->fsinfo_start) if (!fs->fsinfo_start)
return free; return free;
if (verbose) if (verbose)
printf("Checking free cluster summary.\n"); printf("Checking free cluster summary.\n");
if (fs->free_clusters != 0xFFFFFFFF) { if (fs->free_clusters != 0xFFFFFFFF) {
if (free != fs->free_clusters) { if (free != fs->free_clusters) {
printf("Free cluster summary wrong (%ld vs. really %ld)\n", printf("Free cluster summary wrong (%ld vs. really %ld)\n",
(long)fs->free_clusters, (long)free); (long)fs->free_clusters, (long)free);
if (interactive) if (get_choice(1, " Auto-correcting.",
printf("1) Correct\n2) Don't correct\n"); 2,
else 1, "Correct",
printf(" Auto-correcting.\n"); 2, "Don't correct") == 1)
if (!interactive || get_key("12", "?") == '1')
do_set = 1; do_set = 1;
} }
} else { } else {
printf("Free cluster summary uninitialized (should be %ld)\n", (long)free ); printf("Free cluster summary uninitialized (should be %ld)\n", (long)free );
if (rw) { if (rw) {
if (interactive) if (get_choice(1, " Auto-setting.",
printf("1) Set it\n2) Leave it uninitialized\n"); 2,
else 1, "Set it",
printf(" Auto-setting.\n"); 2, "Leave it uninitialized") == 1)
if (!interactive || get_key("12", "?") == '1')
do_set = 1; do_set = 1;
} }
} }
if (do_set) { if (do_set) {
uint32_t le_free = htole32(free); uint32_t le_free = htole32(free);
fs->free_clusters = free; fs->free_clusters = free;
fs_write(fs->fsinfo_start + offsetof(struct info_sector, free_clusters), fs_write(fs->fsinfo_start + offsetof(struct info_sector, free_clusters),
sizeof(le_free), &le_free); sizeof(le_free), &le_free);
} }
 End of changes. 23 change blocks. 
43 lines changed or deleted 124 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)