lib/formats/fs_fat.cpp: Add write support. (#12363)

This commit is contained in:
wilbertpol 2024-05-12 21:47:07 +01:00 committed by GitHub
parent f9ef8589ef
commit 7f8793967f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,5 +1,5 @@
// license:BSD-3-Clause
// copyright-holders:Nathan Woods
// copyright-holders:Nathan Woods,Wilbert Pol
/***************************************************************************
fs_fat.cpp
@ -7,11 +7,13 @@
PC FAT disk images
Current Limitations:
- Read only
- Only supports floppy disks
- No FAT32 support
- No Long Filenames Support
Removal of files is untested; floptool does not have a command to delete
a file.
*****************************************************************************
Master boot record format:
@ -147,6 +149,7 @@
#include "strformat.h"
#include <optional>
#include <regex>
using namespace fs;
@ -163,7 +166,22 @@ namespace {
class directory_entry
{
public:
static const int SIZE = 32;
static constexpr int SIZE = 32;
static constexpr int OFFSET_FNAME = 0;
static constexpr int FNAME_LENGTH = 11;
static constexpr int OFFSET_ATTRIBUTES = 11;
static constexpr int OFFSET_CREATE_DATETIME = 14;
static constexpr int OFFSET_START_CLUSTER_HI = 20;
static constexpr int OFFSET_MODIFIED_DATETIME = 22;
static constexpr int OFFSET_START_CLUSTER = 26;
static constexpr int OFFSET_FILE_SIZE = 28;
static constexpr u8 DELETED_FILE_MARKER = 0xe5;
static constexpr u8 ATTR_READ_ONLY = 0x01;
static constexpr u8 ATTR_HIDDEN = 0x02;
static constexpr u8 ATTR_SYSTEM = 0x04;
static constexpr u8 ATTR_VOLUME_LABEL = 0x08;
static constexpr u8 ATTR_DIRECTORY = 0x10;
static constexpr u8 ATTR_ARCHIVE = 0x20;
directory_entry(const fsblk_t::block_t &block, u32 offset)
: m_block(block)
@ -171,13 +189,13 @@ public:
{
}
std::string_view raw_stem() const { return std::string_view((const char *) &m_block.rodata()[m_offset + 0], 8); }
std::string_view raw_ext() const { return std::string_view((const char *) &m_block.rodata()[m_offset + 8], 3); }
u8 attributes() const { return m_block.r8(m_offset + 11); }
u32 raw_create_datetime() const { return m_block.r32l(m_offset + 14); }
u32 raw_modified_datetime() const { return m_block.r32l(m_offset + 22); }
u32 start_cluster() const { return ((u32)m_block.r16l(m_offset + 20)) << 16 | m_block.r16l(m_offset + 26); }
u32 file_size() const { return m_block.r32l(m_offset + 28); }
std::string_view raw_stem() const { return std::string_view((const char *) &m_block.rodata()[m_offset + OFFSET_FNAME], 8); }
std::string_view raw_ext() const { return std::string_view((const char *) &m_block.rodata()[m_offset + OFFSET_FNAME + 8], 3); }
u8 attributes() const { return m_block.r8(m_offset + OFFSET_ATTRIBUTES); }
u32 raw_create_datetime() const { return m_block.r32l(m_offset + OFFSET_CREATE_DATETIME); }
u32 raw_modified_datetime() const { return m_block.r32l(m_offset + OFFSET_MODIFIED_DATETIME); }
u32 start_cluster() const { return ((u32)m_block.r16l(m_offset + OFFSET_START_CLUSTER_HI)) << 16 | m_block.r16l(m_offset + OFFSET_START_CLUSTER); }
u32 file_size() const { return m_block.r32l(m_offset + OFFSET_FILE_SIZE); }
bool is_read_only() const { return (attributes() & 0x01) != 0x00; }
bool is_hidden() const { return (attributes() & 0x02) != 0x00; }
@ -191,14 +209,15 @@ public:
std::string name() const;
meta_data metadata() const;
private:
static constexpr u8 DELETED_FILE_MARKER = 0xe5;
void set_file_size(u32 file_size) { m_block.w32l(m_offset + OFFSET_FILE_SIZE, file_size); }
void set_raw_modified_datetime(u32 datetime) { m_block.w32l(m_offset + OFFSET_MODIFIED_DATETIME, datetime); }
void mark_deleted() { m_block.w8(m_offset + OFFSET_FNAME, DELETED_FILE_MARKER); }
private:
fsblk_t::block_t m_block;
u32 m_offset;
};
// ======================> directory_span
class directory_span
@ -232,24 +251,57 @@ public:
virtual std::pair<err_t, meta_data> metadata(const std::vector<std::string> &path) override;
virtual std::pair<err_t, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
virtual std::pair<err_t, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
virtual err_t file_create(const std::vector<std::string> &path, const meta_data &meta) override;
virtual err_t file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override;
virtual err_t remove(const std::vector<std::string> &path) override;
// methods
std::vector<u32> get_sectors_from_fat(const directory_entry &dirent) const;
// Boot sector settings
static constexpr u32 OFFSET_BYTES_PER_SECTOR = 0x0b;
static constexpr u32 OFFSET_CLUSTER_SECTOR_COUNT = 0x0d;
static constexpr u32 OFFSET_RESERVED_SECTOR_COUNT = 0x0e;
static constexpr u32 OFFSET_FAT_COUNT = 0x10;
static constexpr u32 OFFSET_DIRECTORY_ENTRY_COUNT = 0x11;
static constexpr u32 OFFSET_FAT_SECTOR_COUNT = 0x16;
private:
static constexpr u32 FIRST_VALID_CLUSTER = 2;
fsblk_t::block_t m_boot_sector_block;
std::vector<u8> m_file_allocation_table;
u32 m_starting_sector;
u32 m_sector_count;
u16 m_reserved_sector_count;
u16 m_bytes_per_sector;
u16 m_root_directory_size;
u16 m_sectors_per_cluster;
u8 m_fat_count;
u16 m_fat_sector_count;
u8 m_bits_per_fat_entry;
u32 m_last_cluster_indicator;
u32 m_last_valid_cluster;
// methods
std::optional<directory_entry> find_entity(const std::vector<std::string> &path) const;
directory_span::ptr find_directory(std::vector<std::string>::const_iterator path_begin, std::vector<std::string>::const_iterator path_end) const;
std::optional<directory_entry> find_child(const directory_span &current_dir, std::string_view target) const;
void iterate_directory_entries(const directory_span &dir, const std::function<bool(const directory_entry &dirent)> &callback) const;
bool is_valid_short_filename(std::string &filename);
err_t build_direntry_filename(std::string &filename, std::string &fname);
err_t file_create_root(std::string &fname, u8 attributes = 0);
err_t file_create_directory(directory_entry &dirent, std::string &fname, u8 attributes = 0);
err_t file_create_sector(u32 sector, std::string &fname, u8 attributes);
err_t initialize_directory(u32 directory_cluster, u32 parent_cluster);
err_t initialize_directory_entry(fsblk_t::block_t &dirblk, u32 offset, const std::string_view &fname, u8 attributes, u32 start_cluster);
err_t free_clusters(u32 start_cluster);
void clear_cluster_sectors(u32 cluster, u8 fill_byte);
u32 first_cluster_sector(u32 cluster);
u32 get_next_cluster(u32 cluster);
void set_next_cluster(u32 cluster, u32 next_cluster);
u32 find_free_cluster();
};
@ -317,15 +369,27 @@ util::arbitrary_datetime decode_fat_datetime(u32 dt)
util::arbitrary_datetime result;
memset(&result, 0, sizeof(result));
result.year = ((dt >> 25) & 0x7F) + 1980;
result.month = (dt >> 21) & 0x0F;
result.day_of_month = (dt >> 16) & 0x1F;
result.hour = (dt >> 11) & 0x1F;
result.minute = (dt >> 5) & 0x3F;
result.second = ((dt >> 0) & 0x1F) * 2;
result.year = ((dt >> 25) & 0x7f) + 1980;
result.month = (dt >> 21) & 0x0f;
result.day_of_month = (dt >> 16) & 0x1f;
result.hour = (dt >> 11) & 0x1f;
result.minute = (dt >> 5) & 0x3f;
result.second = ((dt >> 0) & 0x1f) * 2;
return result;
}
u32 encode_now_fat_datetime()
{
auto now = util::arbitrary_datetime::now();
return u32((((now.year - 1980) & 0x7f) << 25) |
((now.month & 0x0f) << 21) |
((now.day_of_month & 0x1f) << 16) |
((now.hour & 0x1f) << 11) |
((now.minute & 0x3f) << 5) |
((now.second >> 1) & 0x1f));
}
}
@ -355,7 +419,7 @@ bool fs::fat_image::can_read() const
bool fs::fat_image::can_write() const
{
return false;
return true;
}
@ -429,12 +493,12 @@ std::unique_ptr<filesystem_t> fs::fat_image::mount_partition(fsblk_t &blockdev,
{
// load the boot sector block and get some basic info
fsblk_t::block_t boot_sector_block = blockdev.get(starting_sector);
u16 reserved_sector_count = boot_sector_block.r16l(14);
u16 reserved_sector_count = boot_sector_block.r16l(impl::OFFSET_RESERVED_SECTOR_COUNT);
// load all file allocation table sectors
u32 fat_count = boot_sector_block.r8(16);
u32 sectors_per_fat = boot_sector_block.r16l(22);
u16 bytes_per_sector = boot_sector_block.r16l(11);
u32 fat_count = boot_sector_block.r8(impl::OFFSET_FAT_COUNT);
u32 sectors_per_fat = boot_sector_block.r16l(impl::OFFSET_FAT_SECTOR_COUNT);
u16 bytes_per_sector = boot_sector_block.r16l(impl::OFFSET_BYTES_PER_SECTOR);
std::vector<u8> file_allocation_table;
file_allocation_table.reserve(fat_count * sectors_per_fat * bytes_per_sector);
for (auto i = 0; i < fat_count * sectors_per_fat; i++)
@ -489,8 +553,14 @@ impl::impl(fsblk_t &blockdev, fsblk_t::block_t &&boot_sector_block, std::vector<
, m_starting_sector(starting_sector)
, m_sector_count(sector_count)
, m_reserved_sector_count(reserved_sector_count)
, m_bytes_per_sector(m_boot_sector_block.r16l(11))
, m_bytes_per_sector(m_boot_sector_block.r16l(OFFSET_BYTES_PER_SECTOR))
, m_root_directory_size(m_boot_sector_block.r16l(OFFSET_DIRECTORY_ENTRY_COUNT))
, m_sectors_per_cluster(m_boot_sector_block.r8(OFFSET_CLUSTER_SECTOR_COUNT))
, m_fat_count(m_boot_sector_block.r8(OFFSET_FAT_COUNT))
, m_fat_sector_count(m_boot_sector_block.r16l(OFFSET_FAT_SECTOR_COUNT))
, m_bits_per_fat_entry(bits_per_fat_entry)
, m_last_cluster_indicator(((u64)1 << bits_per_fat_entry) - 1)
, m_last_valid_cluster(m_last_cluster_indicator - 0x10)
{
}
@ -594,6 +664,339 @@ std::pair<err_t, std::vector<u8>> impl::file_read(const std::vector<std::string>
}
bool impl::is_valid_short_filename(std::string &filename)
{
/*
Valid characters in DOS file names:
- Upper case letters A-Z
- Numbers 0-9
- Space (though there is no way to identify a trailing space)
- ! # $ % & ( ) - @ ^ _ ` { } ~
- Characters 128-255, except e5 (though the code page is indeterminate)
We currently do not check for characters 128-255.
*/
std::regex filename_regex("([A-Z0-9!#\\$%&\\(\\)\\-@^_`\\{\\}~]{0,8})(\\.([A-Z0-9!#\\$%&\\(\\)\\-@^_`\\{\\}~]{0,3}))?");
return std::regex_match(filename, filename_regex);
}
err_t impl::build_direntry_filename(std::string &filename, std::string &fname)
{
std::regex filename_regex("([A-Z0-9!#\\$%&\\(\\)\\-@^_`\\{\\}~]{0,8})(\\.([A-Z0-9!#\\$%&\\(\\)\\-@^_`\\{\\}~]{0,3}))?");
std::smatch smatch;
if (!std::regex_match(filename, smatch, filename_regex))
return ERR_INVALID;
if (smatch.size() != 4)
return ERR_INVALID;
fname.resize(directory_entry::FNAME_LENGTH, ' ');
for (int i = 0; i < 8 && i < smatch.str(1).size(); i++)
fname[i] = smatch.str(1)[i];
for (int j = 0; j < 3 && j < smatch.str(3).size(); j++)
fname[8 + j] = smatch.str(3)[j];
return ERR_OK;
}
err_t impl::file_create(const std::vector<std::string> &path, const meta_data &meta)
{
std::string filename = meta.get_string(meta_name::name, "");
std::string fname;
err_t err = build_direntry_filename(filename, fname);
if (err != ERR_OK)
return err;
if (path.empty())
{
return file_create_root(fname);
} else {
// Make sure that all parts of the path exist, creating the path parts as needed.
std::optional<directory_entry> dirent = find_entity(path);
if (!dirent)
{
std::vector<std::string> partial_path;
std::optional<directory_entry> parent_entry = { };
for (auto path_part : path)
{
partial_path.emplace_back(path_part);
std::optional<directory_entry> dir_entry = find_entity(partial_path);
if (!dir_entry)
{
if (!is_valid_short_filename(path_part))
return ERR_INVALID;
std::string part_fname;
err_t err = build_direntry_filename(path_part, part_fname);
if (err != ERR_OK)
return err;
err = !parent_entry ?
file_create_root(part_fname, directory_entry::ATTR_DIRECTORY) :
file_create_directory(parent_entry.value(), part_fname, directory_entry::ATTR_DIRECTORY);
if (err != ERR_OK)
return err;
dir_entry = find_entity(partial_path);
if (!dir_entry)
return ERR_INVALID;
err = initialize_directory(dir_entry->start_cluster(), parent_entry ? parent_entry->start_cluster() : 0);
if (err != ERR_OK)
return err;
}
else
{
if (!dir_entry->is_subdirectory())
return ERR_INVALID;
}
parent_entry = dir_entry;
}
dirent = find_entity(path);
if (!dirent)
return ERR_INVALID;
}
return file_create_directory(dirent.value(), fname);
}
}
err_t impl::initialize_directory(u32 directory_cluster, u32 parent_cluster)
{
clear_cluster_sectors(directory_cluster, 0x00);
auto dirblk = m_blockdev.get(first_cluster_sector(directory_cluster));
// Add special directory entries for . and ..
std::string dir_fname;
dir_fname.resize(directory_entry::FNAME_LENGTH, ' ');
dir_fname[0] = '.';
err_t err = initialize_directory_entry(dirblk, 0, dir_fname, directory_entry::ATTR_DIRECTORY, directory_cluster);
if (err != ERR_OK)
return err;
dir_fname[1] = '.';
err = initialize_directory_entry(dirblk, directory_entry::SIZE, dir_fname, directory_entry::ATTR_DIRECTORY, parent_cluster);
if (err != ERR_OK)
return err;
return ERR_OK;
}
err_t impl::initialize_directory_entry(fsblk_t::block_t &dirblk, u32 offset, const std::string_view &fname, u8 attributes, u32 start_cluster)
{
if (fname.size() != directory_entry::FNAME_LENGTH)
return ERR_INVALID;
for (int i = 0; i < directory_entry::SIZE; i += 4)
dirblk.w32l(offset + i, 0);
dirblk.wstr(offset + directory_entry::OFFSET_FNAME, fname);
dirblk.w8(offset + directory_entry::OFFSET_ATTRIBUTES, attributes);
dirblk.w32l(offset + directory_entry::OFFSET_CREATE_DATETIME, encode_now_fat_datetime());
dirblk.w32l(offset + directory_entry::OFFSET_MODIFIED_DATETIME, encode_now_fat_datetime());
dirblk.w16l(offset + directory_entry::OFFSET_START_CLUSTER_HI, u16(start_cluster >> 16));
dirblk.w16l(offset + directory_entry::OFFSET_START_CLUSTER, u16(start_cluster & 0xffff));
return ERR_OK;
}
err_t impl::file_create_root(std::string &fname, u8 attributes)
{
const u32 first_directory_sector = m_starting_sector + m_reserved_sector_count + ((u32)m_file_allocation_table.size() / m_bytes_per_sector);
const u32 directory_sector_count = (m_root_directory_size * directory_entry::SIZE) / m_bytes_per_sector;
for (u32 sector = first_directory_sector; sector < first_directory_sector + directory_sector_count; sector++)
{
err_t err = file_create_sector(sector, fname, attributes);
if (err != ERR_NOT_FOUND)
return err;
}
return ERR_NO_SPACE;
}
err_t impl::file_create_directory(directory_entry &dirent, std::string &fname, u8 attributes)
{
u32 current_cluster = dirent.start_cluster();
do {
const u32 first_sector = first_cluster_sector(current_cluster);
for (int i = 0; i < m_sectors_per_cluster; i++) {
err_t err = file_create_sector(first_sector + i, fname, attributes);
if (err != ERR_NOT_FOUND)
return err;
}
// File could not be created yet. Move to next cluster, allocating a new cluster when needed.
u32 next_cluster = get_next_cluster(current_cluster);
if (next_cluster >= m_last_valid_cluster) {
next_cluster = find_free_cluster();
if (next_cluster == 0)
return ERR_NO_SPACE;
set_next_cluster(current_cluster, next_cluster);
set_next_cluster(next_cluster, m_last_cluster_indicator);
clear_cluster_sectors(next_cluster, 0x00);
}
current_cluster = next_cluster;
} while (current_cluster > FIRST_VALID_CLUSTER && current_cluster < m_last_valid_cluster);
return ERR_NO_SPACE;
}
u32 impl::first_cluster_sector(u32 cluster)
{
return m_starting_sector + m_reserved_sector_count +
((u32)m_file_allocation_table.size() / m_bytes_per_sector) +
((m_root_directory_size + 1) / dirents_per_sector()) +
((cluster - FIRST_VALID_CLUSTER) * m_sectors_per_cluster);
}
void impl::clear_cluster_sectors(u32 cluster, u8 fill_byte)
{
const u32 sector = first_cluster_sector(cluster);
for (int i = 0; i < m_sectors_per_cluster; i++) {
auto dirblk = m_blockdev.get(sector + i);
for (int offset = 0; offset < m_bytes_per_sector; offset++)
dirblk.w8(offset, fill_byte);
}
}
// Returns ERR_NOT_FOUND when no room could be found to create the file in the sector.
err_t impl::file_create_sector(u32 sector, std::string &fname, u8 attributes)
{
auto dirblk = m_blockdev.get(sector);
for (u32 blkoffset = 0; blkoffset < m_bytes_per_sector; blkoffset += directory_entry::SIZE)
{
u8 first_byte = dirblk.r8(blkoffset);
if (first_byte == 0x00 || first_byte == directory_entry::DELETED_FILE_MARKER)
{
u32 start_cluster = find_free_cluster();
if (start_cluster == 0)
return ERR_NO_SPACE;
set_next_cluster(start_cluster, m_last_cluster_indicator);
err_t err = initialize_directory_entry(dirblk, blkoffset, fname, attributes, start_cluster);
if (err != ERR_OK)
return err;
return ERR_OK;
}
}
return ERR_NOT_FOUND;
}
err_t impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data)
{
std::optional<directory_entry> dirent = find_entity(path);
if (!dirent)
return ERR_NOT_FOUND;
if (dirent->is_subdirectory())
return ERR_INVALID;
u32 current_length = dirent->file_size();
const size_t data_length = data.size();
const u32 bytes_per_cluster = m_sectors_per_cluster * bytes_per_sector();
const u32 current_clusters = (current_length + bytes_per_cluster - 1) / bytes_per_cluster;
const u32 required_clusters = (data_length + bytes_per_cluster - 1) / bytes_per_cluster;
if (required_clusters > current_clusters)
{
u32 current_cluster = dirent->start_cluster();
u32 next_cluster = 0;
do {
next_cluster = get_next_cluster(current_cluster);
if (next_cluster < FIRST_VALID_CLUSTER)
return ERR_INVALID;
} while (next_cluster < m_last_valid_cluster);
for (int i = current_clusters; i < required_clusters; i++)
{
u32 free_cluster = find_free_cluster();
if (free_cluster < FIRST_VALID_CLUSTER)
return ERR_NO_SPACE;
set_next_cluster(current_cluster, free_cluster);
set_next_cluster(free_cluster, m_last_cluster_indicator);
current_cluster = free_cluster;
}
}
if (required_clusters < current_clusters)
{
u32 current_cluster = dirent->start_cluster();
for (int i = 0; i < required_clusters; i++)
{
current_cluster = get_next_cluster(current_cluster);
}
u32 next_cluster = get_next_cluster(current_cluster);
set_next_cluster(current_cluster, m_last_cluster_indicator);
err_t err = free_clusters(next_cluster);
if (err != ERR_OK)
return err;
}
auto sectors = get_sectors_from_fat(*dirent);
size_t offset = 0;
for (auto sector : sectors)
{
if (offset < data_length)
{
auto datablk = m_blockdev.get(sector);
u32 bytes = (data_length - offset > m_bytes_per_sector) ? m_bytes_per_sector : data_length - offset;
memcpy(datablk.data(), data.data() + offset, bytes);
offset += m_bytes_per_sector;
}
}
dirent->set_raw_modified_datetime(encode_now_fat_datetime());
dirent->set_file_size(data_length);
return ERR_OK;
}
err_t impl::remove(const std::vector<std::string> &path)
{
if (path.size() != 0)
return ERR_UNSUPPORTED;
std::optional<directory_entry> dirent = find_entity(path);
if (!dirent)
return ERR_OK;
// Removing directories is not supported yet
if (dirent->is_subdirectory())
return ERR_UNSUPPORTED;
dirent->mark_deleted();
return ERR_OK;
}
err_t impl::free_clusters(u32 start_cluster)
{
while (start_cluster < m_last_valid_cluster)
{
if (start_cluster < FIRST_VALID_CLUSTER)
return ERR_INVALID;
u32 next_cluster = get_next_cluster(start_cluster);
set_next_cluster(start_cluster, 0);
start_cluster = next_cluster;
}
return ERR_OK;
}
//-------------------------------------------------
// impl::get_sectors_from_fat
//-------------------------------------------------
@ -605,22 +1008,20 @@ std::vector<u32> impl::get_sectors_from_fat(const directory_entry &dirent) const
results.reserve(dirent.file_size() / bytes_per_sector());
// get critical information
u8 sectors_per_cluster = m_boot_sector_block.r8(13);
u16 root_directory_entry_count = m_boot_sector_block.r16l(17);
u16 root_directory_sector_count = (root_directory_entry_count + 1) / dirents_per_sector();
u32 fat_sector_count = (u32)(m_file_allocation_table.size() / m_bytes_per_sector);
u16 root_directory_sector_count = (m_root_directory_size + 1) / dirents_per_sector();
u32 fat_sector_count = m_fat_count * m_fat_sector_count;
u32 data_starting_sector = m_starting_sector + m_reserved_sector_count + fat_sector_count + root_directory_sector_count;
u32 data_cluster_count = (m_sector_count - data_starting_sector) / sectors_per_cluster;
u32 data_cluster_count = (m_sector_count - data_starting_sector) / m_sectors_per_cluster;
// find all clusters
u32 start_cluster_mask = ((u64)1 << m_bits_per_fat_entry) - 1;
u32 cluster = dirent.start_cluster() & start_cluster_mask;
while (cluster >= 2 && cluster < (data_cluster_count + 2))
while (cluster >= FIRST_VALID_CLUSTER && cluster < (data_cluster_count + 2))
{
// add the sectors for this cluster
for (auto i = 0; i < sectors_per_cluster; i++)
results.push_back(data_starting_sector + (cluster - 2) * sectors_per_cluster + i);
for (auto i = 0; i < m_sectors_per_cluster; i++)
results.push_back(data_starting_sector + (cluster - FIRST_VALID_CLUSTER) * m_sectors_per_cluster + i);
// determine the bit position of this entry
u32 entry_bit_position = cluster * m_bits_per_fat_entry;
@ -644,8 +1045,8 @@ std::vector<u32> impl::get_sectors_from_fat(const directory_entry &dirent) const
}
// normalize special cluster IDs
if (new_cluster > ((u32)1 << m_bits_per_fat_entry) - 0x10)
new_cluster |= ~(((u32)1 << m_bits_per_fat_entry) - 1);
if (new_cluster > m_last_valid_cluster)
new_cluster |= ~m_last_cluster_indicator;
}
cluster = new_cluster;
}
@ -654,6 +1055,89 @@ std::vector<u32> impl::get_sectors_from_fat(const directory_entry &dirent) const
}
u32 impl::get_next_cluster(u32 cluster)
{
u32 entry_bit_position = cluster * m_bits_per_fat_entry;
u32 new_cluster = 0;
if (entry_bit_position + m_bits_per_fat_entry <= m_file_allocation_table.size() * 8)
{
u32 current_bit = 0;
while (current_bit < m_bits_per_fat_entry)
{
u32 pos = entry_bit_position + current_bit;
u32 shift = pos % 8;
u32 bit_count = std::min(8 - shift, m_bits_per_fat_entry - current_bit);
u32 bits = (m_file_allocation_table[pos / 8] >> shift) & ((1 << bit_count) - 1);
new_cluster |= (bits << current_bit);
current_bit += bit_count;
}
}
return new_cluster;
}
void impl::set_next_cluster(u32 cluster, u32 next_cluster)
{
const u32 m_fat_start_sector = m_starting_sector + m_reserved_sector_count;
const u32 entry_bit_position = cluster * m_bits_per_fat_entry;
if (entry_bit_position + m_bits_per_fat_entry <= m_file_allocation_table.size() * 8)
{
u32 current_bit = 0;
while (current_bit < m_bits_per_fat_entry)
{
u32 pos = entry_bit_position + current_bit;
u32 shift = pos % 8;
u32 bit_count = std::min(8 - shift, m_bits_per_fat_entry - current_bit);
u32 byte_pos = pos / 8;
u32 mask = ((1 << bit_count) - 1);
m_file_allocation_table[byte_pos] = (m_file_allocation_table[byte_pos] & ~(mask << shift)) | ((next_cluster & mask) << shift);
next_cluster = next_cluster >> bit_count;
current_bit += bit_count;
// Write back to backing blocks
for (int i = 0; i < m_fat_count; i++) {
u32 fat_sector = m_fat_start_sector + (i * m_fat_sector_count) + (byte_pos / m_bytes_per_sector);
auto fatblk = m_blockdev.get(fat_sector);
fatblk.w8(byte_pos % m_bytes_per_sector, m_file_allocation_table[byte_pos]);
}
}
}
}
// Returns 0 if no free cluster could be found
u32 impl::find_free_cluster()
{
u16 root_directory_sector_count = (m_root_directory_size + 1) / dirents_per_sector();
u32 fat_sector_count = m_fat_count * m_fat_sector_count;
u32 data_starting_sector = m_starting_sector + m_reserved_sector_count + fat_sector_count + root_directory_sector_count;
u32 data_cluster_count = (m_sector_count - data_starting_sector) / m_sectors_per_cluster;
for (u32 cluster = FIRST_VALID_CLUSTER; cluster < (data_cluster_count + 2); cluster++) {
u32 entry_bit_position = cluster * m_bits_per_fat_entry;
if (entry_bit_position + m_bits_per_fat_entry <= m_file_allocation_table.size() * 8)
{
u32 new_cluster = 0;
u32 current_bit = 0;
while (current_bit < m_bits_per_fat_entry)
{
u32 pos = entry_bit_position + current_bit;
u32 shift = pos % 8;
u32 bit_count = std::min(8 - shift, m_bits_per_fat_entry - current_bit);
u32 bits = (m_file_allocation_table[pos / 8] >> shift) & ((1 << bit_count) - 1);
new_cluster |= (bits << current_bit);
current_bit += bit_count;
}
if (new_cluster == 0)
return cluster;
}
}
return 0;
}
//-------------------------------------------------
// impl::find_entity
//-------------------------------------------------
@ -682,8 +1166,7 @@ directory_span::ptr impl::find_directory(std::vector<std::string>::const_iterato
{
// the root directory is treated differently
u32 first_sector = m_starting_sector + m_reserved_sector_count + (u32)m_file_allocation_table.size() / m_bytes_per_sector;
u16 directory_entry_count = m_boot_sector_block.r16l(17);
directory_span::ptr current_dir = std::make_unique<root_directory_span>(*this, first_sector, directory_entry_count);
directory_span::ptr current_dir = std::make_unique<root_directory_span>(*this, first_sector, m_root_directory_size);
// traverse the directory
for (auto iter = path_begin; iter != path_end; iter++)