Skip to content

Commit

Permalink
Bug fix: The FAT server should handle modern FAT16 volumes (#111)
Browse files Browse the repository at this point in the history
The number of reserved sectors _used_ to be always 1, but this is no longer the case. The FAT volume we format ourselves with `mkdosfs` no longer follows this age-old convention, which is mentioned in e.g. the classical FAT: General Overview of On-Disk Format specification by Microsoft. So we must read this value from the BPB instead of just blindly making foolish assumptions.
  • Loading branch information
perlun authored Dec 9, 2017
1 parent 44a89f9 commit 065c304
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 32 deletions.
2 changes: 1 addition & 1 deletion servers/file_system/fat/Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ LIBRARIES = %w(
).freeze

OUTPUT = 'fat'.freeze
EXTRA_LDFLAGS_PRE = '-Wl,--defsym=PROCESS_VM_BASE=0x50000000'.freeze
EXTRA_LDFLAGS_PRE = '-Wl,--defsym=PROCESS_VM_BASE=0x48000000'.freeze

load '../../servers.rake'
29 changes: 21 additions & 8 deletions servers/file_system/fat/clusters.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#include "fat.h"

#undef DEBUG

// Get the next cluster number in the chain for the given cluster.
uint32_t get_next_cluster(uint32_t cluster_number, void *fat, int bits)
{
Expand All @@ -17,7 +19,7 @@ uint32_t get_next_cluster(uint32_t cluster_number, void *fat, int bits)
uint32_t new_cluster_number = FAT12_READ_ENTRY(fat12, cluster_number);

if (new_cluster_number == FAT12_BAD_CLUSTER ||
new_cluster_number >= FAT12_END_OF_CLUSTER_CHAIN)
new_cluster_number >= FAT12_END_OF_CLUSTER_CHAIN)
{
return UINT32_MAX;
}
Expand Down Expand Up @@ -83,16 +85,27 @@ uint32_t read_clusters(fat_info_type *fat_info, void *output, uint32_t start_clu
}
else
{
// log_print_formatted (&log_structure, LOG_URGENCY_DEBUG,
// "Reading cluster number %lu", cluster_number);
read_single_cluster(fat_info, cluster_number, (void *)
((uint32_t) output + (clusters_read *
fat_info->bytes_per_sector *
fat_info->sectors_per_cluster)));
#ifdef DEBUG
log_print_formatted (
&log_structure,
LOG_URGENCY_DEBUG,
"Reading cluster number %u",
cluster_number
);
#endif
void *data_buffer = (void *) (
(uint32_t) output + (
clusters_read *
fat_info->bytes_per_sector *
fat_info->sectors_per_cluster
)
);

read_single_cluster(fat_info, cluster_number, data_buffer);
clusters_read++;
}
cluster_number = get_next_cluster(cluster_number, fat_info->fat, fat_info->bits);
} while (cluster_number != MAX_uint32_t && clusters_read < number_of_clusters);
} while (cluster_number != UINT32_MAX && clusters_read < number_of_clusters);

return cluster_number;
}
Expand Down
88 changes: 77 additions & 11 deletions servers/file_system/fat/detect_fat.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
static uint8_t global_fat[16384];

// Detects wether there is a FAT file system on the given mailbox ID.
// TODO: Should be able to return error conditions also, not only TRUE or FALSE.
bool detect_fat(fat_info_type *fat_info)
{
message_parameter_type message_parameter;
Expand All @@ -28,11 +29,30 @@ bool detect_fat(fat_info_type *fat_info)

ipc_block_read.start_block_number = 0;
ipc_block_read.number_of_blocks = 1;
ipc_send(fat_info->block_structure.output_mailbox_id, &message_parameter);
if (ipc_send(fat_info->block_structure.output_mailbox_id, &message_parameter) != IPC_RETURN_SUCCESS)
{
log_print_formatted(
&log_structure, LOG_URGENCY_ERROR,
"ipc_send to mailbox %u failed (from %s:%u)",
fat_info->block_structure.output_mailbox_id,
__FILE__, __LINE__
);
return FALSE;
}

message_parameter.length = 1024;
message_parameter.data = sector;
ipc_receive(fat_info->block_structure.input_mailbox_id, &message_parameter, NULL);
if (ipc_receive(fat_info->block_structure.input_mailbox_id, &message_parameter, NULL) != IPC_RETURN_SUCCESS)
{
log_print_formatted(
&log_structure,
LOG_URGENCY_ERROR,
"ipc_receive from mailbox %u failed (from %s:%u)",
fat_info->block_structure.input_mailbox_id,
__FILE__, __LINE__
);
return FALSE;
}

// Make sure this is a valid FAT filesystem.
if (sector[510] == 0x55 &&
Expand Down Expand Up @@ -98,15 +118,36 @@ bool detect_fat(fat_info_type *fat_info)
message_parameter.data = &ipc_block_read;
message_parameter.length = sizeof(ipc_block_read_type);

ipc_block_read.start_block_number = 1;
ipc_block_read.start_block_number = bios_parameter_block->reserved_sectors;
ipc_block_read.number_of_blocks = bios_parameter_block->fat_size_16;

ipc_send(fat_info->block_structure.output_mailbox_id, &message_parameter);
if (ipc_send(fat_info->block_structure.output_mailbox_id, &message_parameter) != IPC_RETURN_SUCCESS)
{
log_print_formatted(
&log_structure,
LOG_URGENCY_ERROR,
"ipc_send to mailbox %u failed (from %s:%u)",
fat_info->block_structure.output_mailbox_id,
__FILE__, __LINE__
);
return FALSE;
}

memory_set_uint8_t((uint8_t *) &global_fat, 0, 16384);
memory_set_uint32_t((uint32_t *) &global_fat, 0xDEADBEEF, 16384 / sizeof(uint32_t));
message_parameter.length = 16384;
message_parameter.data = &global_fat;
ipc_receive(fat_info->block_structure.input_mailbox_id, &message_parameter, NULL);
if (ipc_receive(fat_info->block_structure.input_mailbox_id, &message_parameter, NULL) != IPC_RETURN_SUCCESS)
{
log_print_formatted(
&log_structure,
LOG_URGENCY_ERROR,
"ipc_receive from mailbox %u failed (from %s:%u)",
fat_info->block_structure.input_mailbox_id,
__FILE__, __LINE__
);
return FALSE;
}

fat_info->fat = &global_fat;

// Also, read the root directory.
Expand All @@ -121,16 +162,41 @@ bool detect_fat(fat_info_type *fat_info)
message_parameter.data = &ipc_block_read;
message_parameter.block = TRUE;

// log_print_formatted (&log_structure, LOG_URGENCY_DEBUG,
// "Reading %u sectors",
// fat_info->root_directory_sectors);
#ifdef DEBUG
log_print_formatted(
&log_structure, LOG_URGENCY_DEBUG,
"Reading %u sectors",
fat_info->root_directory_sectors
);
#endif

ipc_block_read.start_block_number = block_number;
ipc_block_read.number_of_blocks = fat_info->root_directory_sectors;
ipc_send(fat_info->block_structure.output_mailbox_id, &message_parameter);
if (ipc_send(fat_info->block_structure.output_mailbox_id, &message_parameter) != IPC_RETURN_SUCCESS)
{
log_print_formatted(
&log_structure,
LOG_URGENCY_ERROR,
"ipc_send to mailbox %u failed (from %s:%u)",
fat_info->block_structure.output_mailbox_id,
__FILE__, __LINE__
);
return FALSE;
}

message_parameter.length = (fat_info->root_directory_sectors * fat_info->bytes_per_sector);
message_parameter.data = &global_root;
ipc_receive(fat_info->block_structure.input_mailbox_id, &message_parameter, NULL);
if (ipc_receive(fat_info->block_structure.input_mailbox_id, &message_parameter, NULL) != IPC_RETURN_SUCCESS)
{
log_print_formatted(
&log_structure,
LOG_URGENCY_ERROR,
"ipc_receive from mailbox %u failed (from %s:%u)",
fat_info->block_structure.input_mailbox_id,
__FILE__, __LINE__
);
return FALSE;
}
fat_info->root = &global_root;

return TRUE;
Expand Down
2 changes: 1 addition & 1 deletion servers/file_system/fat/fat.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ static tag_type empty_tag =
0, 0, ""
};

// Split a path name to its components.
// Split a path name to its components. Note that this method will modify the input string.
void path_split(char *path_name, char **output, unsigned int *elements)
{
unsigned int index;
Expand Down
4 changes: 3 additions & 1 deletion servers/file_system/fat/fat.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ typedef struct
// applications will stop working. ;-)
uint8_t sectors_per_cluster;

// Number of reserved sectors in the reserved region of the volume starting at the first sector of the volume. Must be 1 for
// Number of reserved sectors in the reserved region of the volume starting at the first sector of the volume. Typically, but not always, 1 for
// FAT12/16. Usually 32 for FAT32.
uint16_t reserved_sectors;

Expand Down Expand Up @@ -182,6 +182,8 @@ typedef struct
uint8_t reserved : 2;

// Reserved for Windows NT. Set to zero when creating, and never rely upon.
// FIXME: This is the case flag. We should support it, to properly handle lowercase file names on FAT volumes.
// See https://github.com/chaos4ever/chaos/issues/107
uint8_t nt_reserved;

// Time stamp the file was created.
Expand Down
39 changes: 32 additions & 7 deletions servers/file_system/fat/fat_directory_read.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,45 @@ bool fat_directory_read(fat_info_type *fat_info, char *path[], int elements, fat
entry = get_entry_by_name(directory, path[element]);
if (entry == NULL)
{
#ifdef DEBUG
log_print_formatted(
&log_structure,
LOG_URGENCY_DEBUG,
"get_entry_by_name for '%s' returned NULL",
path[element]
);
#endif
break;
}

// log_print_formatted (&log_structure, LOG_URGENCY_DEBUG, "apa: %s",
// entry->name);
#ifdef DEBUG
log_print_formatted(
&log_structure,
LOG_URGENCY_DEBUG,
"entry->name: %s",
entry->name
);
#endif

read_clusters(fat_info, &directory_buffer,
(entry->first_cluster_number_high << 16) +
entry->first_cluster_number_low,
0, UINT32_MAX);
read_clusters(
fat_info,
&directory_buffer,
(entry->first_cluster_number_high << 16) +
entry->first_cluster_number_low,
0,
UINT32_MAX
);
}

*fat_entry = (fat_entry_type *) &directory_buffer;
// log_print (&log_structure, LOG_URGENCY_DEBUG, (*fat_entry)->name);
#ifdef DEBUG
log_print_formatted(
&log_structure,
LOG_URGENCY_DEBUG,
"fat_entry->name: %s",
(*fat_entry)->name
);
#endif

return TRUE;
}
Expand Down
16 changes: 13 additions & 3 deletions servers/file_system/fat/fat_file_get_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,29 @@ bool fat_file_get_info(fat_info_type *fat_info, file_verbose_directory_entry_typ
fat_entry_type *fat_entry;
fat_entry_type *our_file;

#ifdef DEBUG
log_print_formatted(&log_structure, LOG_URGENCY_DEBUG, "fat_file_get_info opening '%s'.", file_info->path_name);
#endif

// Read the directory where this file is located. If it is a subdirectory, we need to read every parent directory. First, we
// split the directory name into its logical components.

path_split(file_info->path_name, path, &elements);

// All file names have at least one component. Otherwise, they are wrong.
if (elements < 1)
{
log_print_formatted(&log_structure, LOG_URGENCY_ERROR,
"fat_file_get_info: '%s' does not contain any elements",
file_info->path_name);
return FALSE;
}

// Read the directory.
if (!fat_directory_read(fat_info, path, elements - 1, &fat_entry))
{
// log_print (&log_structure, LOG_URGENCY_DEBUG, "Babar krossar Ratataxes!");
log_print_formatted(&log_structure, LOG_URGENCY_ERROR,
"fat_file_get_info: Failed reading directory for '%s'",
file_info->path_name);
return FALSE;
}

Expand All @@ -37,7 +45,9 @@ bool fat_file_get_info(fat_info_type *fat_info, file_verbose_directory_entry_typ
// Did we not get a match?
if (our_file == NULL)
{
// log_print (&log_structure, LOG_URGENCY_ERROR, "Jadå, glassen e slut!");
log_print_formatted(&log_structure, LOG_URGENCY_ERROR,
"fat_file_get_info: File '%s' not found",
file_info->path_name);
return FALSE;
}

Expand Down

0 comments on commit 065c304

Please sign in to comment.