/** Copyright (c) 2006-2007 by Roland Riegel <feedback@roland-riegel.de>** This file is free software; you can redistribute it and/or modify* it under the terms of either the GNU General Public License version 2* or the GNU Lesser General Public License version 2.1, both as* published by the Free Software Foundation.*/#include "partition.h"#include "fat16.h"#include "fat16_config.h"#include "sd-reader_config.h"#include <string.h>#if USE_DYNAMIC_MEMORY#include <stdlib.h>#endif/*** \addtogroup fat16 FAT16 support** This module implements FAT16 read and write access.** The following features are supported:* - File names up to 31 characters long.* - Unlimited depth of subdirectories.* - Short 8.3 and long filenames.* - Creating and deleting files.* - Reading and writing from and to files.* - File resizing.* - File sizes of up to 4 gigabytes.** @{*//*** \file* FAT16 implementation (license: GPLv2 or LGPLv2.1)** \author Roland Riegel*//*** \addtogroup fat16_config FAT16 configuration* Preprocessor defines to configure the FAT16 implementation.*//*** \addtogroup fat16_fs FAT16 access* Basic functions for handling a FAT16 filesystem.*//*** \addtogroup fat16_file FAT16 file functions* Functions for managing files.*//*** \addtogroup fat16_dir FAT16 directory functions* Functions for managing directories.*//*** @}*/#define FAT16_CLUSTER_FREE 0x0000#define FAT16_CLUSTER_RESERVED_MIN 0xfff0#define FAT16_CLUSTER_RESERVED_MAX 0xfff6#define FAT16_CLUSTER_BAD 0xfff7#define FAT16_CLUSTER_LAST_MIN 0xfff8#define FAT16_CLUSTER_LAST_MAX 0xffff#define FAT16_DIRENTRY_DELETED 0xe5#define FAT16_DIRENTRY_LFNLAST (1 << 6)#define FAT16_DIRENTRY_LFNSEQMASK ((1 << 6) - 1)/* Each entry within the directory table has a size of 32 bytes* and either contains a 8.3 DOS-style file name or a part of a* long file name, which may consist of several directory table* entries at once.** multi-byte integer values are stored little-endian!** 8.3 file name entry:* ====================* offset length description* 0 8 name (space padded)* 8 3 extension (space padded)* 11 1 attributes (FAT16_ATTRIB_*)** long file name (lfn) entry ordering for a single file name:* ===========================================================* LFN entry n* ...* LFN entry 2* LFN entry 1* 8.3 entry (see above)** lfn entry:* ==========* offset length description* 0 1 ordinal field* 1 2 unicode character 1* 3 3 unicode character 2* 5 3 unicode character 3* 7 3 unicode character 4* 9 3 unicode character 5* 11 1 attribute (always 0x0f)* 12 1 type (reserved, always 0)* 13 1 checksum* 14 2 unicode character 6* 16 2 unicode character 7* 18 2 unicode character 8* 20 2 unicode character 9* 22 2 unicode character 10* 24 2 unicode character 11* 26 2 cluster (unused, always 0)* 28 2 unicode character 12* 30 2 unicode character 13** The ordinal field contains a descending number, from n to 1.* For the n'th lfn entry the ordinal field is or'ed with 0x40.* For deleted lfn entries, the ordinal field is set to 0xe5.*/struct fat16_header_struct{uint32_t size;uint32_t fat_offset;uint32_t fat_size;uint16_t sector_size;uint16_t cluster_size;uint32_t root_dir_offset;uint32_t cluster_zero_offset;};struct fat16_fs_struct{struct partition_struct* partition;struct fat16_header_struct header;};struct fat16_file_struct{struct fat16_fs_struct* fs;struct fat16_dir_entry_struct dir_entry;uint32_t pos;uint16_t pos_cluster;};struct fat16_dir_struct{struct fat16_fs_struct* fs;struct fat16_dir_entry_struct dir_entry;uint16_t entry_next;};struct fat16_read_callback_arg{uint16_t entry_cur;uint16_t entry_num;uint32_t entry_offset;uint8_t byte_count;};struct fat16_usage_count_callback_arg{uint16_t cluster_count;uint8_t buffer_size;};#if !USE_DYNAMIC_MEMORYstatic struct fat16_fs_struct fat16_fs_handlers[FAT16_FS_COUNT];static struct fat16_file_struct fat16_file_handlers[FAT16_FILE_COUNT];static struct fat16_dir_struct fat16_dir_handlers[FAT16_DIR_COUNT];#endifstatic uint8_t fat16_read_header(struct fat16_fs_struct* fs);static uint8_t fat16_read_root_dir_entry(const struct fat16_fs_struct* fs, uint16_t entry_num, struct fat16_dir_entry_struct* dir_entry);static uint8_t fat16_read_sub_dir_entry(const struct fat16_fs_struct* fs, uint16_t entry_num, const struct fat16_dir_entry_struct* parent, struct fat16_dir_entry_struct* dir_entry);static uint8_t fat16_dir_entry_seek_callback(uint8_t* buffer, uint32_t offset, void* p);static uint8_t fat16_dir_entry_read_callback(uint8_t* buffer, uint32_t offset, void* p);static uint8_t fat16_interpret_dir_entry(struct fat16_dir_entry_struct* dir_entry, const uint8_t* raw_entry);static uint16_t fat16_get_next_cluster(const struct fat16_fs_struct* fs, uint16_t cluster_num);static uint16_t fat16_append_clusters(const struct fat16_fs_struct* fs, uint16_t cluster_num, uint16_t count);static uint8_t fat16_free_clusters(const struct fat16_fs_struct* fs, uint16_t cluster_num);static uint8_t fat16_terminate_clusters(const struct fat16_fs_struct* fs, uint16_t cluster_num);static uint8_t fat16_clear_cluster(const struct fat16_fs_struct* fs, uint16_t cluster_num);static uint16_t fat16_clear_cluster_callback(uint8_t* buffer, uint32_t offset, void* p);static uint32_t fat16_find_offset_for_dir_entry(const struct fat16_fs_struct* fs, const struct fat16_dir_struct* parent, const struct fat16_dir_entry_struct* dir_entry);static uint8_t fat16_write_dir_entry(const struct fat16_fs_struct* fs, struct fat16_dir_entry_struct* dir_entry);static uint8_t fat16_get_fs_free_callback(uint8_t* buffer, uint32_t offset, void* p);static void fat16_set_file_modification_date(struct fat16_dir_entry_struct* dir_entry, uint16_t year, uint8_t month, uint8_t day);static void fat16_set_file_modification_time(struct fat16_dir_entry_struct* dir_entry, uint8_t hour, uint8_t min, uint8_t sec);/*** \ingroup fat16_fs* Opens a FAT16 filesystem.** \param[in] partition Discriptor of partition on which the filesystem resides.* \returns 0 on error, a FAT16 filesystem descriptor on success.* \see fat16_open*/struct fat16_fs_struct* fat16_open(struct partition_struct* partition){if(!partition ||#if FAT16_WRITE_SUPPORT!partition->device_write ||!partition->device_write_interval#else0#endif)return 0;#if USE_DYNAMIC_MEMORYstruct fat16_fs_struct* fs = malloc(sizeof(*fs));if(!fs)return 0;#elsestruct fat16_fs_struct* fs = fat16_fs_handlers;uint8_t i;for(i = 0; i < FAT16_FS_COUNT; ++i){if(!fs->partition)break;++fs;}if(i >= FAT16_FS_COUNT)return 0;#endifmemset(fs, 0, sizeof(*fs));fs->partition = partition;if(!fat16_read_header(fs)){#if USE_DYNAMIC_MEMORYfree(fs);#elsefs->partition = 0;#endifreturn 0;}return fs;}/*** \ingroup fat16_fs* Closes a FAT16 filesystem.** When this function returns, the given filesystem descriptor* will be invalid.** \param[in] fs The filesystem to close.* \see fat16_open*/void fat16_close(struct fat16_fs_struct* fs){if(!fs)return;#if USE_DYNAMIC_MEMORYfree(fs);#elsefs->partition = 0;#endif}/*** \ingroup fat16_fs* Reads and parses the header of a FAT16 filesystem.** \param[inout] fs The filesystem for which to parse the header.* \returns 0 on failure, 1 on success.*/uint8_t fat16_read_header(struct fat16_fs_struct* fs){if(!fs)return 0;struct partition_struct* partition = fs->partition;if(!partition)return 0;/* read fat parameters */uint8_t buffer[25];uint32_t partition_offset = partition->offset * 512;if(!partition->device_read(partition_offset + 0x0b, buffer, sizeof(buffer)))return 0;uint16_t bytes_per_sector = ((uint16_t) buffer[0x00]) |((uint16_t) buffer[0x01] << 8);uint8_t sectors_per_cluster = buffer[0x02];uint16_t reserved_sectors = ((uint16_t) buffer[0x03]) |((uint16_t) buffer[0x04] << 8);uint8_t fat_copies = buffer[0x05];uint16_t max_root_entries = ((uint16_t) buffer[0x06]) |((uint16_t) buffer[0x07] << 8);uint16_t sector_count_16 = ((uint16_t) buffer[0x08]) |((uint16_t) buffer[0x09] << 8);uint16_t sectors_per_fat = ((uint16_t) buffer[0x0b]) |((uint16_t) buffer[0x0c] << 8);uint32_t sector_count = ((uint32_t) buffer[0x15]) |((uint32_t) buffer[0x16] << 8) |((uint32_t) buffer[0x17] << 16) |((uint32_t) buffer[0x18] << 24);if(sectors_per_fat == 0)/* this is not a FAT16 */return 0;if(sector_count == 0){if(sector_count_16 == 0)/* illegal volume size */return 0;elsesector_count = sector_count_16;}/* ensure we really have a FAT16 fs here */uint32_t data_sector_count = sector_count- reserved_sectors- (uint32_t) sectors_per_fat * fat_copies- ((max_root_entries * 32 + bytes_per_sector - 1) / bytes_per_sector);uint32_t data_cluster_count = data_sector_count / sectors_per_cluster;if(data_cluster_count < 4085 || data_cluster_count >= 65525)/* this is not a FAT16 */return 0;partition->type = PARTITION_TYPE_FAT16;/* fill header information */struct fat16_header_struct* header = &fs->header;memset(header, 0, sizeof(*header));header->size = sector_count * bytes_per_sector;header->fat_offset = /* jump to partition */partition_offset +/* jump to fat */(uint32_t) reserved_sectors * bytes_per_sector;header->fat_size = (data_cluster_count + 2) * 2;header->sector_size = bytes_per_sector;header->cluster_size = (uint32_t) bytes_per_sector * sectors_per_cluster;header->root_dir_offset = /* jump to fats */header->fat_offset +/* jump to root directory entries */(uint32_t) fat_copies * sectors_per_fat * bytes_per_sector;header->cluster_zero_offset = /* jump to root directory entries */header->root_dir_offset +/* skip root directory entries */(uint32_t) max_root_entries * 32;return 1;}/*** \ingroup fat16_fs* Reads a directory entry of the root directory.** \param[in] fs Descriptor of file system to use.* \param[in] entry_num The index of the directory entry to read.* \param[out] dir_entry Directory entry descriptor which will get filled.* \returns 0 on failure, 1 on success* \see fat16_read_sub_dir_entry, fat16_read_dir_entry_by_path*/uint8_t fat16_read_root_dir_entry(const struct fat16_fs_struct* fs, uint16_t entry_num, struct fat16_dir_entry_struct* dir_entry){if(!fs || !dir_entry)return 0;/* we read from the root directory entry */const struct fat16_header_struct* header = &fs->header;device_read_interval_t device_read_interval = fs->partition->device_read_interval;uint8_t buffer[32];/* seek to the n-th entry */struct fat16_read_callback_arg arg;memset(&arg, 0, sizeof(arg));arg.entry_num = entry_num;if(!device_read_interval(header->root_dir_offset,buffer,sizeof(buffer),header->cluster_zero_offset - header->root_dir_offset,fat16_dir_entry_seek_callback,&arg) ||arg.entry_offset == 0)return 0;/* read entry */memset(dir_entry, 0, sizeof(*dir_entry));if(!device_read_interval(arg.entry_offset,buffer,sizeof(buffer),arg.byte_count,fat16_dir_entry_read_callback,dir_entry))return 0;return dir_entry->long_name[0] != '\0' ? 1 : 0;}/*** \ingroup fat16_fs* Reads a directory entry of a given parent directory.** \param[in] fs Descriptor of file system to use.* \param[in] entry_num The index of the directory entry to read.* \param[in] parent Directory entry descriptor in which to read directory entry.* \param[out] dir_entry Directory entry descriptor which will get filled.* \returns 0 on failure, 1 on success* \see fat16_read_root_dir_entry, fat16_read_dir_entry_by_path*/uint8_t fat16_read_sub_dir_entry(const struct fat16_fs_struct* fs, uint16_t entry_num, const struct fat16_dir_entry_struct* parent, struct fat16_dir_entry_struct* dir_entry){if(!fs || !parent || !dir_entry)return 0;/* we are in a parent directory and want to search within its directory entry table */if(!(parent->attributes & FAT16_ATTRIB_DIR))return 0;/* loop through all clusters of the directory */uint8_t buffer[32];uint32_t cluster_offset;uint16_t cluster_size = fs->header.cluster_size;uint16_t cluster_num = parent->cluster;struct fat16_read_callback_arg arg;while(1){/* calculate new cluster offset */cluster_offset = fs->header.cluster_zero_offset + (uint32_t) (cluster_num - 2) * cluster_size;/* seek to the n-th entry */memset(&arg, 0, sizeof(arg));arg.entry_num = entry_num;if(!fs->partition->device_read_interval(cluster_offset,buffer,sizeof(buffer),cluster_size,fat16_dir_entry_seek_callback,&arg))return 0;/* check if we found the entry */if(arg.entry_offset)break;/* get number of next cluster */if(!(cluster_num = fat16_get_next_cluster(fs, cluster_num)))return 0; /* directory entry not found */}memset(dir_entry, 0, sizeof(*dir_entry));/* read entry */if(!fs->partition->device_read_interval(arg.entry_offset,buffer,sizeof(buffer),arg.byte_count,fat16_dir_entry_read_callback,dir_entry))return 0;return dir_entry->long_name[0] != '\0' ? 1 : 0;}/*** \ingroup fat16_fs* Callback function for seeking through subdirectory entries.*/uint8_t fat16_dir_entry_seek_callback(uint8_t* buffer, uint32_t offset, void* p){struct fat16_read_callback_arg* arg = p;/* skip deleted or empty entries */if(buffer[0] == FAT16_DIRENTRY_DELETED || !buffer[0])return 1;if(arg->entry_cur == arg->entry_num){arg->entry_offset = offset;arg->byte_count = buffer[11] == 0x0f ?((buffer[0] & FAT16_DIRENTRY_LFNSEQMASK) + 1) * 32 :32;return 0;}/* if we read a 8.3 entry, we reached a new directory entry */if(buffer[11] != 0x0f)++arg->entry_cur;return 1;}/*** \ingroup fat16_fs* Callback function for reading a directory entry.*/uint8_t fat16_dir_entry_read_callback(uint8_t* buffer, uint32_t offset, void* p){struct fat16_dir_entry_struct* dir_entry = p;/* there should not be any deleted or empty entries */if(buffer[0] == FAT16_DIRENTRY_DELETED || !buffer[0])return 0;if(!dir_entry->entry_offset)dir_entry->entry_offset = offset;switch(fat16_interpret_dir_entry(dir_entry, buffer)){case 0: /* failure */return 0;case 1: /* buffer successfully parsed, continue */return 1;case 2: /* directory entry complete, finish */return 0;}return 0;}/*** \ingroup fat16_fs* Interprets a raw directory entry and puts the contained* information into the directory entry.** For a single file there may exist multiple directory* entries. All except the last one are lfn entries, which* contain parts of the long filename. The last directory* entry is a traditional 8.3 style one. It contains all* other information like size, cluster, date and time.** \param[in,out] dir_entry The directory entry to fill.* \param[in] raw_entry A pointer to 32 bytes of raw data.* \returns 0 on failure, 1 on success and 2 if the* directory entry is complete.*/uint8_t fat16_interpret_dir_entry(struct fat16_dir_entry_struct* dir_entry, const uint8_t* raw_entry){if(!dir_entry || !raw_entry || !raw_entry[0])return 0;char* long_name = dir_entry->long_name;if(raw_entry[11] == 0x0f){uint16_t char_offset = ((raw_entry[0] & 0x3f) - 1) * 13;if(char_offset + 12 < sizeof(dir_entry->long_name)){/* Lfn supports unicode, but we do not, for now.* So we assume pure ascii and read only every* second byte.*/long_name[char_offset + 0] = raw_entry[1];long_name[char_offset + 1] = raw_entry[3];long_name[char_offset + 2] = raw_entry[5];long_name[char_offset + 3] = raw_entry[7];long_name[char_offset + 4] = raw_entry[9];long_name[char_offset + 5] = raw_entry[14];long_name[char_offset + 6] = raw_entry[16];long_name[char_offset + 7] = raw_entry[18];long_name[char_offset + 8] = raw_entry[20];long_name[char_offset + 9] = raw_entry[22];long_name[char_offset + 10] = raw_entry[24];long_name[char_offset + 11] = raw_entry[28];long_name[char_offset + 12] = raw_entry[30];}return 1;}else{/* if we do not have a long name, take the short one */if(long_name[0] == '\0'){uint8_t i;for(i = 0; i < 8; ++i){if(raw_entry[i] == ' ')break;long_name[i] = raw_entry[i];}if(long_name[0] == 0x05)long_name[0] = (char) FAT16_DIRENTRY_DELETED;if(raw_entry[8] != ' '){long_name[i++] = '.';uint8_t j = 8;for(; j < 11; ++j){if(raw_entry[j] != ' '){long_name[i++] = raw_entry[j];}else{break;}}}long_name[i] = '\0';}/* extract properties of file and store them within the structure */dir_entry->attributes = raw_entry[11];dir_entry->cluster = ((uint16_t) raw_entry[26]) |((uint16_t) raw_entry[27] << 8);dir_entry->file_size = ((uint32_t) raw_entry[28]) |((uint32_t) raw_entry[29] << 8) |((uint32_t) raw_entry[30] << 16) |((uint32_t) raw_entry[31] << 24);#if FAT16_DATETIME_SUPPORTdir_entry->modification_time = ((uint16_t) raw_entry[22]) |((uint16_t) raw_entry[23] << 8);dir_entry->modification_date = ((uint16_t) raw_entry[24]) |((uint16_t) raw_entry[25] << 8);#endifreturn 2;}}/*** \ingroup fat16_file* Retrieves the directory entry of a path.** The given path may both describe a file or a directory.** \param[in] fs The FAT16 filesystem on which to search.* \param[in] path The path of which to read the directory entry.* \param[out] dir_entry The directory entry to fill.* \returns 0 on failure, 1 on success.* \see fat16_read_dir*/uint8_t fat16_get_dir_entry_of_path(struct fat16_fs_struct* fs, const char* path, struct fat16_dir_entry_struct* dir_entry){if(!fs || !path || path[0] == '\0' || !dir_entry)return 0;if(path[0] == '/')++path;/* begin with the root directory */memset(dir_entry, 0, sizeof(*dir_entry));dir_entry->attributes = FAT16_ATTRIB_DIR;if(path[0] == '\0')return 1;while(1){struct fat16_dir_struct* dd = fat16_open_dir(fs, dir_entry);if(!dd)break;/* extract the next hierarchy we will search for */const char* sep_pos = strchr(path, '/');if(!sep_pos)sep_pos = path + strlen(path);uint8_t length_to_sep = sep_pos - path;/* read directory entries */while(fat16_read_dir(dd, dir_entry)){/* check if we have found the next hierarchy */if((strlen(dir_entry->long_name) != length_to_sep ||strncmp(path, dir_entry->long_name, length_to_sep) != 0))continue;fat16_close_dir(dd);dd = 0;if(path[length_to_sep] == '\0')/* we iterated through the whole path and have found the file */return 1;if(dir_entry->attributes & FAT16_ATTRIB_DIR){/* we found a parent directory of the file we are searching for */path = sep_pos + 1;break;}/* a parent of the file exists, but not the file itself */return 0;}fat16_close_dir(dd);}return 0;}/*** \ingroup fat16_fs* Retrieves the next following cluster of a given cluster.** Using the filesystem file allocation table, this function returns* the number of the cluster containing the data directly following* the data within the cluster with the given number.** \param[in] fs The filesystem for which to determine the next cluster.* \param[in] cluster_num The number of the cluster for which to determine its successor.* \returns The wanted cluster number, or 0 on error.*/uint16_t fat16_get_next_cluster(const struct fat16_fs_struct* fs, uint16_t cluster_num){if(!fs || cluster_num < 2)return 0;/* read appropriate fat entry */uint8_t fat_entry[2];if(!fs->partition->device_read(fs->header.fat_offset + 2 * cluster_num, fat_entry, 2))return 0;/* determine next cluster from fat */cluster_num = ((uint16_t) fat_entry[0]) |((uint16_t) fat_entry[1] << 8);if(cluster_num == FAT16_CLUSTER_FREE ||cluster_num == FAT16_CLUSTER_BAD ||(cluster_num >= FAT16_CLUSTER_RESERVED_MIN && cluster_num <= FAT16_CLUSTER_RESERVED_MAX) ||(cluster_num >= FAT16_CLUSTER_LAST_MIN && cluster_num <= FAT16_CLUSTER_LAST_MAX))return 0;return cluster_num;}/*** \ingroup fat16_fs* Appends a new cluster chain to an existing one.** Set cluster_num to zero to create a completely new one.** \param[in] fs The file system on which to operate.* \param[in] cluster_num The cluster to which to append the new chain.* \param[in] count The number of clusters to allocate.* \returns 0 on failure, the number of the first new cluster on success.*/uint16_t fat16_append_clusters(const struct fat16_fs_struct* fs, uint16_t cluster_num, uint16_t count){#if FAT16_WRITE_SUPPORTif(!fs)return 0;device_read_t device_read = fs->partition->device_read;device_write_t device_write = fs->partition->device_write;uint32_t fat_offset = fs->header.fat_offset;uint16_t cluster_max = fs->header.fat_size / 2;uint16_t cluster_next = 0;uint16_t count_left = count;uint8_t buffer[2];for(uint16_t cluster_new = 0; cluster_new < cluster_max; ++cluster_new){if(!device_read(fat_offset + 2 * cluster_new, buffer, sizeof(buffer)))return 0;/* check if this is a free cluster */if(buffer[0] == (FAT16_CLUSTER_FREE & 0xff) &&buffer[1] == ((FAT16_CLUSTER_FREE >> 8) & 0xff)){/* allocate cluster */if(count_left == count){buffer[0] = FAT16_CLUSTER_LAST_MAX & 0xff;buffer[1] = (FAT16_CLUSTER_LAST_MAX >> 8) & 0xff;}else{buffer[0] = cluster_next & 0xff;buffer[1] = (cluster_next >> 8) & 0xff;}if(!device_write(fat_offset + 2 * cluster_new, buffer, sizeof(buffer)))break;cluster_next = cluster_new;if(--count_left == 0)break;}}do{if(count_left > 0)break;/* We allocated a new cluster chain. Now join* it with the existing one.*/if(cluster_num >= 2){buffer[0] = cluster_next & 0xff;buffer[1] = (cluster_next >> 8) & 0xff;if(!device_write(fat_offset + 2 * cluster_num, buffer, sizeof(buffer)))break;}return cluster_next;} while(0);/* No space left on device or writing error.* Free up all clusters already allocated.*/fat16_free_clusters(fs, cluster_next);return 0;#elsereturn 0;#endif}/*** \ingroup fat16_fs* Frees a cluster chain, or a part thereof.** Marks the specified cluster and all clusters which are sequentially* referenced by it as free. They may then be used again for future* file allocations.** \note If this function is used for freeing just a part of a cluster* chain, the new end of the chain is not correctly terminated* within the FAT. Use fat16_terminate_clusters() instead.** \param[in] fs The filesystem on which to operate.* \param[in] cluster_num The starting cluster of the chain which to free.* \returns 0 on failure, 1 on success.* \see fat16_terminate_clusters*/uint8_t fat16_free_clusters(const struct fat16_fs_struct* fs, uint16_t cluster_num){#if FAT16_WRITE_SUPPORTif(!fs || cluster_num < 2)return 0;uint32_t fat_offset = fs->header.fat_offset;uint8_t buffer[2];while(cluster_num){if(!fs->partition->device_read(fat_offset + 2 * cluster_num, buffer, 2))return 0;/* get next cluster of current cluster before freeing current cluster */uint16_t cluster_num_next = ((uint16_t) buffer[0]) |((uint16_t) buffer[1] << 8);if(cluster_num_next == FAT16_CLUSTER_FREE)return 1;if(cluster_num_next == FAT16_CLUSTER_BAD ||(cluster_num_next >= FAT16_CLUSTER_RESERVED_MIN &&cluster_num_next <= FAT16_CLUSTER_RESERVED_MAX))return 0;if(cluster_num_next >= FAT16_CLUSTER_LAST_MIN && cluster_num_next <= FAT16_CLUSTER_LAST_MAX)cluster_num_next = 0;/* free cluster */buffer[0] = FAT16_CLUSTER_FREE & 0xff;buffer[1] = (FAT16_CLUSTER_FREE >> 8) & 0xff;fs->partition->device_write(fat_offset + 2 * cluster_num, buffer, 2);/* We continue in any case here, even if freeing the cluster failed.* The cluster is lost, but maybe we can still free up some later ones.*/cluster_num = cluster_num_next;}return 1;#elsereturn 0;#endif}/*** \ingroup fat16_fs* Frees a part of a cluster chain and correctly terminates the rest.** Marks the specified cluster as the new end of a cluster chain and* frees all following clusters.** \param[in] fs The filesystem on which to operate.* \param[in] cluster_num The new end of the cluster chain.* \returns 0 on failure, 1 on success.* \see fat16_free_clusters*/uint8_t fat16_terminate_clusters(const struct fat16_fs_struct* fs, uint16_t cluster_num){#if FAT16_WRITE_SUPPORTif(!fs || cluster_num < 2)return 0;/* fetch next cluster before overwriting the cluster entry */uint16_t cluster_num_next = fat16_get_next_cluster(fs, cluster_num);/* mark cluster as the last one */uint8_t buffer[2];buffer[0] = FAT16_CLUSTER_LAST_MAX & 0xff;buffer[1] = (FAT16_CLUSTER_LAST_MAX >> 8) & 0xff;if(!fs->partition->device_write(fs->header.fat_offset + 2 * cluster_num, buffer, 2))return 0;/* free remaining clusters */if(cluster_num_next)return fat16_free_clusters(fs, cluster_num_next);elsereturn 1;#elsereturn 0;#endif}/*** \ingroup fat16_fs* Clears a single cluster.** The complete cluster is filled with zeros.** \param[in] fs The filesystem on which to operate.* \param[in] cluster_num The cluster to clear.* \returns 0 on failure, 1 on success.*/uint8_t fat16_clear_cluster(const struct fat16_fs_struct* fs, uint16_t cluster_num){#if FAT16_WRITE_SUPPORTif(cluster_num < 2)return 0;uint32_t cluster_offset = fs->header.cluster_zero_offset +(uint32_t) (cluster_num - 2) * fs->header.cluster_size;uint8_t zero[16];return fs->partition->device_write_interval(cluster_offset,zero,fs->header.cluster_size,fat16_clear_cluster_callback,0);#elsereturn 0;#endif}/*** \ingroup fat16_fs* Callback function for clearing a cluster.*/uint16_t fat16_clear_cluster_callback(uint8_t* buffer, uint32_t offset, void* p){#if FAT16_WRITE_SUPPORTmemset(buffer, 0, 16);return 16;#elsereturn 0;#endif}/*** \ingroup fat16_file* Opens a file on a FAT16 filesystem.** \param[in] fs The filesystem on which the file to open lies.* \param[in] dir_entry The directory entry of the file to open.* \returns The file handle, or 0 on failure.* \see fat16_close_file*/struct fat16_file_struct* fat16_open_file(struct fat16_fs_struct* fs, const struct fat16_dir_entry_struct* dir_entry){if(!fs || !dir_entry || (dir_entry->attributes & FAT16_ATTRIB_DIR))return 0;#if USE_DYNAMIC_MEMORYstruct fat16_file_struct* fd = malloc(sizeof(*fd));if(!fd)return 0;#elsestruct fat16_file_struct* fd = fat16_file_handlers;uint8_t i;for(i = 0; i < FAT16_FILE_COUNT; ++i){if(!fd->fs)break;++fd;}if(i >= FAT16_FILE_COUNT)return 0;#endifmemcpy(&fd->dir_entry, dir_entry, sizeof(*dir_entry));fd->fs = fs;fd->pos = 0;fd->pos_cluster = dir_entry->cluster;return fd;}/*** \ingroup fat16_file* Closes a file.** \param[in] fd The file handle of the file to close.* \see fat16_open_file*/void fat16_close_file(struct fat16_file_struct* fd){if(fd)#if USE_DYNAMIC_MEMORYfree(fd);#elsefd->fs = 0;#endif}/*** \ingroup fat16_file* Reads data from a file.** The data requested is read from the current file location.** \param[in] fd The file handle of the file from which to read.* \param[out] buffer The buffer into which to write.* \param[in] buffer_len The amount of data to read.* \returns The number of bytes read, 0 on end of file, or -1 on failure.* \see fat16_write_file*/int16_t fat16_read_file(struct fat16_file_struct* fd, uint8_t* buffer, uint16_t buffer_len){/* check arguments */if(!fd || !buffer || buffer_len < 1)return -1;/* determine number of bytes to read */if(fd->pos + buffer_len > fd->dir_entry.file_size)buffer_len = fd->dir_entry.file_size - fd->pos;if(buffer_len == 0)return 0;uint16_t cluster_size = fd->fs->header.cluster_size;uint16_t cluster_num = fd->pos_cluster;uint16_t buffer_left = buffer_len;uint16_t first_cluster_offset = fd->pos % cluster_size;/* find cluster in which to start reading */if(!cluster_num){cluster_num = fd->dir_entry.cluster;if(!cluster_num){if(!fd->pos)return 0;elsereturn -1;}if(fd->pos){uint32_t pos = fd->pos;while(pos >= cluster_size){pos -= cluster_size;cluster_num = fat16_get_next_cluster(fd->fs, cluster_num);if(!cluster_num)return -1;}}}/* read data */do{/* calculate data size to copy from cluster */uint32_t cluster_offset = fd->fs->header.cluster_zero_offset +(uint32_t) (cluster_num - 2) * cluster_size + first_cluster_offset;uint16_t copy_length = cluster_size - first_cluster_offset;if(copy_length > buffer_left)copy_length = buffer_left;/* read data */if(!fd->fs->partition->device_read(cluster_offset, buffer, copy_length))return buffer_len - buffer_left;/* calculate new file position */buffer += copy_length;buffer_left -= copy_length;fd->pos += copy_length;if(first_cluster_offset + copy_length >= cluster_size){/* we are on a cluster boundary, so get the next cluster */if((cluster_num = fat16_get_next_cluster(fd->fs, cluster_num))){first_cluster_offset = 0;}else{fd->pos_cluster = 0;return buffer_len - buffer_left;}}fd->pos_cluster = cluster_num;} while(buffer_left > 0); /* check if we are done */return buffer_len;}/*** \ingroup fat16_file* Writes data to a file.** The data is written to the current file location.** \param[in] fd The file handle of the file to which to write.* \param[in] buffer The buffer from which to read the data to be written.* \param[in] buffer_len The amount of data to write.* \returns The number of bytes written, 0 on disk full, or -1 on failure.* \see fat16_read_file*/int16_t fat16_write_file(struct fat16_file_struct* fd, const uint8_t* buffer, uint16_t buffer_len){#if FAT16_WRITE_SUPPORT/* check arguments */if(!fd || !buffer || buffer_len < 1)return -1;if(fd->pos > fd->dir_entry.file_size)return -1;uint16_t cluster_size = fd->fs->header.cluster_size;uint16_t cluster_num = fd->pos_cluster;uint16_t buffer_left = buffer_len;uint16_t first_cluster_offset = fd->pos % cluster_size;/* find cluster in which to start writing */if(!cluster_num){cluster_num = fd->dir_entry.cluster;if(!cluster_num){if(!fd->pos){/* empty file */fd->dir_entry.cluster = cluster_num = fat16_append_clusters(fd->fs, 0, 1);if(!cluster_num)return -1;}else{return -1;}}if(fd->pos){uint32_t pos = fd->pos;uint16_t cluster_num_next;while(pos >= cluster_size){pos -= cluster_size;cluster_num_next = fat16_get_next_cluster(fd->fs, cluster_num);if(!cluster_num_next && pos == 0)/* the file exactly ends on a cluster boundary, and we append to it */cluster_num_next = fat16_append_clusters(fd->fs, cluster_num, 1);if(!cluster_num_next)return -1;cluster_num = cluster_num_next;}}}/* write data */do{/* calculate data size to write to cluster */uint32_t cluster_offset = fd->fs->header.cluster_zero_offset +(uint32_t) (cluster_num - 2) * cluster_size + first_cluster_offset;uint16_t write_length = cluster_size - first_cluster_offset;if(write_length > buffer_left)write_length = buffer_left;/* write data which fits into the current cluster */if(!fd->fs->partition->device_write(cluster_offset, buffer, write_length))break;/* calculate new file position */buffer += write_length;buffer_left -= write_length;fd->pos += write_length;if(first_cluster_offset + write_length >= cluster_size){/* we are on a cluster boundary, so get the next cluster */uint16_t cluster_num_next = fat16_get_next_cluster(fd->fs, cluster_num);if(!cluster_num_next && buffer_left > 0)/* we reached the last cluster, append a new one */cluster_num_next = fat16_append_clusters(fd->fs, cluster_num, 1);if(!cluster_num_next){fd->pos_cluster = 0;break;}cluster_num = cluster_num_next;first_cluster_offset = 0;}fd->pos_cluster = cluster_num;} while(buffer_left > 0); /* check if we are done *//* update directory entry */if(fd->pos > fd->dir_entry.file_size){uint32_t size_old = fd->dir_entry.file_size;/* update file size */fd->dir_entry.file_size = fd->pos;/* write directory entry */if(!fat16_write_dir_entry(fd->fs, &fd->dir_entry)){/* We do not return an error here since we actually wrote* some data to disk. So we calculate the amount of data* we wrote to disk and which lies within the old file size.*/buffer_left = fd->pos - size_old;fd->pos = size_old;}}return buffer_len - buffer_left;#elsereturn -1;#endif}/*** \ingroup fat16_file* Repositions the read/write file offset.** Changes the file offset where the next call to fat16_read_file()* or fat16_write_file() starts reading/writing.** If the new offset is beyond the end of the file, fat16_resize_file()* is implicitly called, i.e. the file is expanded.** The new offset can be given in different ways determined by* the \c whence parameter:* - \b FAT16_SEEK_SET: \c *offset is relative to the beginning of the file.* - \b FAT16_SEEK_CUR: \c *offset is relative to the current file position.* - \b FAT16_SEEK_END: \c *offset is relative to the end of the file.** The resulting absolute offset is written to the location the \c offset* parameter points to.** \param[in] fd The file decriptor of the file on which to seek.* \param[in,out] offset A pointer to the new offset, as affected by the \c whence* parameter. The function writes the new absolute offset* to this location before it returns.* \param[in] whence Affects the way \c offset is interpreted, see above.* \returns 0 on failure, 1 on success.*/uint8_t fat16_seek_file(struct fat16_file_struct* fd, int32_t* offset, uint8_t whence){if(!fd || !offset)return 0;uint32_t new_pos = fd->pos;switch(whence){case FAT16_SEEK_SET:new_pos = *offset;break;case FAT16_SEEK_CUR:new_pos += *offset;break;case FAT16_SEEK_END:new_pos = fd->dir_entry.file_size + *offset;break;default:return 0;}if(new_pos > fd->dir_entry.file_size && !fat16_resize_file(fd, new_pos))return 0;fd->pos = new_pos;fd->pos_cluster = 0;*offset = new_pos;return 1;}/*** \ingroup fat16_file* Resizes a file to have a specific size.** Enlarges or shrinks the file pointed to by the file descriptor to have* exactly the specified size.** If the file is truncated, all bytes having an equal or larger offset* than the given size are lost. If the file is expanded, the additional* bytes are allocated.** \note Please be aware that this function just allocates or deallocates disk* space, it does not explicitely clear it. To avoid data leakage, this* must be done manually.** \param[in] fd The file decriptor of the file which to resize.* \param[in] size The new size of the file.* \returns 0 on failure, 1 on success.*/uint8_t fat16_resize_file(struct fat16_file_struct* fd, uint32_t size){#if FAT16_WRITE_SUPPORTif(!fd)return 0;uint16_t cluster_num = fd->dir_entry.cluster;uint16_t cluster_size = fd->fs->header.cluster_size;uint32_t size_new = size;do{if(cluster_num == 0 && size_new == 0)/* the file stays empty */break;/* seek to the next cluster as long as we need the space */while(size_new > cluster_size){/* get next cluster of file */uint16_t cluster_num_next = fat16_get_next_cluster(fd->fs, cluster_num);if(cluster_num_next){cluster_num = cluster_num_next;size_new -= cluster_size;}else{break;}}if(size_new > cluster_size || cluster_num == 0){/* Allocate new cluster chain and append* it to the existing one, if available.*/uint16_t cluster_count = size_new / cluster_size;if((uint32_t) cluster_count * cluster_size < size_new)++cluster_count;uint16_t cluster_new_chain = fat16_append_clusters(fd->fs, cluster_num, cluster_count);if(!cluster_new_chain)return 0;if(!cluster_num){cluster_num = cluster_new_chain;fd->dir_entry.cluster = cluster_num;}}/* write new directory entry */fd->dir_entry.file_size = size;if(size == 0)fd->dir_entry.cluster = 0;if(!fat16_write_dir_entry(fd->fs, &fd->dir_entry))return 0;if(size == 0){/* free all clusters of file */fat16_free_clusters(fd->fs, cluster_num);}else if(size_new <= cluster_size){/* free all clusters no longer needed */fat16_terminate_clusters(fd->fs, cluster_num);}} while(0);/* correct file position */if(size < fd->pos){fd->pos = size;fd->pos_cluster = 0;}return 1;#elsereturn 0;#endif}/*** \ingroup fat16_dir* Opens a directory.** \param[in] fs The filesystem on which the directory to open resides.* \param[in] dir_entry The directory entry which stands for the directory to open.* \returns An opaque directory descriptor on success, 0 on failure.* \see fat16_close_dir*/struct fat16_dir_struct* fat16_open_dir(struct fat16_fs_struct* fs, const struct fat16_dir_entry_struct* dir_entry){if(!fs || !dir_entry || !(dir_entry->attributes & FAT16_ATTRIB_DIR))return 0;#if USE_DYNAMIC_MEMORYstruct fat16_dir_struct* dd = malloc(sizeof(*dd));if(!dd)return 0;#elsestruct fat16_dir_struct* dd = fat16_dir_handlers;uint8_t i;for(i = 0; i < FAT16_DIR_COUNT; ++i){if(!dd->fs)break;++dd;}if(i >= FAT16_DIR_COUNT)return 0;#endifmemcpy(&dd->dir_entry, dir_entry, sizeof(*dir_entry));dd->fs = fs;dd->entry_next = 0;return dd;}/*** \ingroup fat16_dir* Closes a directory descriptor.** This function destroys a directory descriptor which was* previously obtained by calling fat16_open_dir(). When this* function returns, the given descriptor will be invalid.** \param[in] dd The directory descriptor to close.* \see fat16_open_dir*/void fat16_close_dir(struct fat16_dir_struct* dd){if(dd)#if USE_DYNAMIC_MEMORYfree(dd);#elsedd->fs = 0;#endif}/*** \ingroup fat16_dir* Reads the next directory entry contained within a parent directory.** \param[in] dd The descriptor of the parent directory from which to read the entry.* \param[out] dir_entry Pointer to a buffer into which to write the directory entry information.* \returns 0 on failure, 1 on success.* \see fat16_reset_dir*/uint8_t fat16_read_dir(struct fat16_dir_struct* dd, struct fat16_dir_entry_struct* dir_entry){if(!dd || !dir_entry)return 0;if(dd->dir_entry.cluster == 0){/* read entry from root directory */if(fat16_read_root_dir_entry(dd->fs, dd->entry_next, dir_entry)){++dd->entry_next;return 1;}}else{/* read entry from a subdirectory */if(fat16_read_sub_dir_entry(dd->fs, dd->entry_next, &dd->dir_entry, dir_entry)){++dd->entry_next;return 1;}}/* restart reading */dd->entry_next = 0;return 0;}/*** \ingroup fat16_dir* Resets a directory handle.** Resets the directory handle such that reading restarts* with the first directory entry.** \param[in] dd The directory handle to reset.* \returns 0 on failure, 1 on success.* \see fat16_read_dir*/uint8_t fat16_reset_dir(struct fat16_dir_struct* dd){if(!dd)return 0;dd->entry_next = 0;return 1;}/*** \ingroup fat16_fs* Searches for space where to store a directory entry.** \param[in] fs The filesystem on which to operate.* \param[in] dir_entry The directory entry for which to search space.* \returns 0 on failure, a device offset on success.*/uint32_t fat16_find_offset_for_dir_entry(const struct fat16_fs_struct* fs, const struct fat16_dir_struct* parent, const struct fat16_dir_entry_struct* dir_entry){#if FAT16_WRITE_SUPPORTif(!fs || !dir_entry)return 0;/* search for a place where to write the directory entry to disk */uint8_t free_dir_entries_needed = (strlen(dir_entry->long_name) + 12) / 13 + 1;uint8_t free_dir_entries_found = 0;uint16_t cluster_num = parent->dir_entry.cluster;uint32_t dir_entry_offset = 0;uint32_t offset = 0;uint32_t offset_to = 0;if(cluster_num == 0){/* we read/write from the root directory entry */offset = fs->header.root_dir_offset;offset_to = fs->header.cluster_zero_offset;dir_entry_offset = offset;}while(1){if(offset == offset_to){if(cluster_num == 0)/* We iterated through the whole root directory entry* and could not find enough space for the directory entry.*/return 0;if(offset){/* We reached a cluster boundary and have to* switch to the next cluster.*/uint16_t cluster_next = fat16_get_next_cluster(fs, cluster_num);if(!cluster_next){cluster_next = fat16_append_clusters(fs, cluster_num, 1);if(!cluster_next)return 0;/* we appended a new cluster and know it is free */dir_entry_offset = fs->header.cluster_zero_offset +(uint32_t) (cluster_next - 2) * fs->header.cluster_size;/* clear cluster to avoid garbage directory entries */fat16_clear_cluster(fs, cluster_next);break;}cluster_num = cluster_next;}offset = fs->header.cluster_zero_offset +(uint32_t) (cluster_num - 2) * fs->header.cluster_size;offset_to = offset + fs->header.cluster_size;dir_entry_offset = offset;free_dir_entries_found = 0;}/* read next lfn or 8.3 entry */uint8_t first_char;if(!fs->partition->device_read(offset, &first_char, sizeof(first_char)))return 0;/* check if we found a free directory entry */if(first_char == FAT16_DIRENTRY_DELETED || !first_char){/* check if we have the needed number of available entries */++free_dir_entries_found;if(free_dir_entries_found >= free_dir_entries_needed)break;offset += 32;}else{offset += 32;dir_entry_offset = offset;free_dir_entries_found = 0;}}return dir_entry_offset;#elsereturn 0;#endif}/*** \ingroup fat16_fs* Writes a directory entry to disk.** \note The file name is not checked for invalid characters.** \note The generation of the short 8.3 file name is quite* simple. The first eight characters are used for the filename.* The extension, if any, is made up of the first three characters* following the last dot within the long filename. If the* filename (without the extension) is longer than eight characters,* the lower byte of the cluster number replaces the last two* characters to avoid name clashes. In any other case, it is your* responsibility to avoid name clashes.** \param[in] fs The filesystem on which to operate.* \param[in] dir_entry The directory entry to write.* \returns 0 on failure, 1 on success.*/uint8_t fat16_write_dir_entry(const struct fat16_fs_struct* fs, struct fat16_dir_entry_struct* dir_entry){#if FAT16_WRITE_SUPPORTif(!fs || !dir_entry)return 0;#if FAT16_DATETIME_SUPPORT{uint16_t year;uint8_t month;uint8_t day;uint8_t hour;uint8_t min;uint8_t sec;fat16_get_datetime(&year, &month, &day, &hour, &min, &sec);fat16_set_file_modification_date(dir_entry, year, month, day);fat16_set_file_modification_time(dir_entry, hour, min, sec);}#endifdevice_write_t device_write = fs->partition->device_write;uint32_t offset = dir_entry->entry_offset;const char* name = dir_entry->long_name;uint8_t name_len = strlen(name);uint8_t lfn_entry_count = (name_len + 12) / 13;uint8_t buffer[32];/* write 8.3 entry *//* generate 8.3 file name */memset(&buffer[0], ' ', 11);char* name_ext = strrchr(name, '.');if(name_ext && *++name_ext){uint8_t name_ext_len = strlen(name_ext);name_len -= name_ext_len + 1;if(name_ext_len > 3)name_ext_len = 3;memcpy(&buffer[8], name_ext, name_ext_len);}if(name_len <= 8){memcpy(buffer, name, name_len);/* For now, we create lfn entries for all files,* except the "." and ".." directory references.* This is to avoid difficulties with capitalization,* as 8.3 filenames allow uppercase letters only.** Theoretically it would be possible to leave* the 8.3 entry alone if the basename and the* extension have no mixed capitalization.*/if(name[0] == '.' &&((name[1] == '.' && name[2] == '\0') ||name[1] == '\0'))lfn_entry_count = 0;}else{memcpy(buffer, name, 8);/* Minimize 8.3 name clashes by appending* the lower byte of the cluster number.*/uint8_t num = dir_entry->cluster & 0xff;buffer[6] = (num < 0xa0) ? ('0' + (num >> 4)) : ('a' + (num >> 4));num &= 0x0f;buffer[7] = (num < 0x0a) ? ('0' + num) : ('a' + num);}if(buffer[0] == FAT16_DIRENTRY_DELETED)buffer[0] = 0x05;/* fill directory entry buffer */memset(&buffer[11], 0, sizeof(buffer) - 11);buffer[0x0b] = dir_entry->attributes;#if FAT16_DATETIME_SUPPORTbuffer[0x16] = (dir_entry->modification_time >> 0) & 0xff;buffer[0x17] = (dir_entry->modification_time >> 8) & 0xff;buffer[0x18] = (dir_entry->modification_date >> 0) & 0xff;buffer[0x19] = (dir_entry->modification_date >> 8) & 0xff;#endifbuffer[0x1a] = (dir_entry->cluster >> 0) & 0xff;buffer[0x1b] = (dir_entry->cluster >> 8) & 0xff;buffer[0x1c] = (dir_entry->file_size >> 0) & 0xff;buffer[0x1d] = (dir_entry->file_size >> 8) & 0xff;buffer[0x1e] = (dir_entry->file_size >> 16) & 0xff;buffer[0x1f] = (dir_entry->file_size >> 24) & 0xff;/* write to disk */if(!device_write(offset + (uint32_t) lfn_entry_count * 32, buffer, sizeof(buffer)))return 0;/* calculate checksum of 8.3 name */uint8_t checksum = buffer[0];for(uint8_t i = 1; i < 11; ++i)checksum = ((checksum >> 1) | (checksum << 7)) + buffer[i];/* write lfn entries */for(uint8_t lfn_entry = lfn_entry_count; lfn_entry > 0; --lfn_entry){memset(buffer, 0xff, sizeof(buffer));/* set file name */const char* long_name_curr = name + (lfn_entry - 1) * 13;uint8_t i = 1;while(i < 0x1f){buffer[i++] = *long_name_curr;buffer[i++] = 0;switch(i){case 0x0b:i = 0x0e;break;case 0x1a:i = 0x1c;break;}if(!*long_name_curr++)break;}/* set index of lfn entry */buffer[0x00] = lfn_entry;if(lfn_entry == lfn_entry_count)buffer[0x00] |= FAT16_DIRENTRY_LFNLAST;/* mark as lfn entry */buffer[0x0b] = 0x0f;/* set 8.3 checksum */buffer[0x0d] = checksum;/* clear reserved bytes */buffer[0x0c] = 0;buffer[0x1a] = 0;buffer[0x1b] = 0;/* write entry */device_write(offset, buffer, sizeof(buffer));offset += sizeof(buffer);}return 1;#elsereturn 0;#endif}/*** \ingroup fat16_file* Creates a file.** Creates a file and obtains the directory entry of the* new file. If the file to create already exists, the* directory entry of the existing file will be returned* within the dir_entry parameter.** \note The file name is not checked for invalid characters.** \note The generation of the short 8.3 file name is quite* simple. The first eight characters are used for the filename.* The extension, if any, is made up of the first three characters* following the last dot within the long filename. If the* filename (without the extension) is longer than eight characters,* the lower byte of the cluster number replaces the last two* characters to avoid name clashes. In any other case, it is your* responsibility to avoid name clashes.** \param[in] parent The handle of the directory in which to create the file.* \param[in] file The name of the file to create.* \param[out] dir_entry The directory entry to fill for the new file.* \returns 0 on failure, 1 on success.* \see fat16_delete_file*/uint8_t fat16_create_file(struct fat16_dir_struct* parent, const char* file, struct fat16_dir_entry_struct* dir_entry){#if FAT16_WRITE_SUPPORTif(!parent || !file || !file[0] || !dir_entry)return 0;/* check if the file already exists */while(1){if(!fat16_read_dir(parent, dir_entry))break;if(strcmp(file, dir_entry->long_name) == 0){fat16_reset_dir(parent);return 0;}}struct fat16_fs_struct* fs = parent->fs;/* prepare directory entry with values already known */memset(dir_entry, 0, sizeof(*dir_entry));strncpy(dir_entry->long_name, file, sizeof(dir_entry->long_name) - 1);/* find place where to store directory entry */if(!(dir_entry->entry_offset = fat16_find_offset_for_dir_entry(fs, parent, dir_entry)))return 0;/* write directory entry to disk */if(!fat16_write_dir_entry(fs, dir_entry))return 0;return 1;#elsereturn 0;#endif}/*** \ingroup fat16_file* Deletes a file or directory.** If a directory is deleted without first deleting its* subdirectories and files, disk space occupied by these* files will get wasted as there is no chance to release* it and mark it as free.** \param[in] fs The filesystem on which to operate.* \param[in] dir_entry The directory entry of the file to delete.* \returns 0 on failure, 1 on success.* \see fat16_create_file*/uint8_t fat16_delete_file(struct fat16_fs_struct* fs, struct fat16_dir_entry_struct* dir_entry){#if FAT16_WRITE_SUPPORTif(!fs || !dir_entry)return 0;/* get offset of the file's directory entry */uint32_t dir_entry_offset = dir_entry->entry_offset;if(!dir_entry_offset)return 0;uint8_t buffer[12];while(1){/* read directory entry */if(!fs->partition->device_read(dir_entry_offset, buffer, sizeof(buffer)))return 0;/* mark the directory entry as deleted */buffer[0] = FAT16_DIRENTRY_DELETED;/* write back entry */if(!fs->partition->device_write(dir_entry_offset, buffer, sizeof(buffer)))return 0;/* check if we deleted the whole entry */if(buffer[11] != 0x0f)break;dir_entry_offset += 32;}/* We deleted the directory entry. The next thing to do is* marking all occupied clusters as free.*/return (dir_entry->cluster == 0 || fat16_free_clusters(fs, dir_entry->cluster));#elsereturn 0;#endif}/*** \ingroup fat16_dir* Creates a directory.** Creates a directory and obtains its directory entry.* If the directory to create already exists, its* directory entry will be returned within the dir_entry* parameter.** \note The notes which apply to fat16_create_file also* apply to this function.** \param[in] parent The handle of the parent directory of the new directory.* \param[in] dir The name of the directory to create.* \param[out] dir_entry The directory entry to fill for the new directory.* \returns 0 on failure, 1 on success.* \see fat16_delete_dir*/uint8_t fat16_create_dir(struct fat16_dir_struct* parent, const char* dir, struct fat16_dir_entry_struct* dir_entry){#if FAT16_WRITE_SUPPORTif(!parent || !dir || !dir[0] || !dir_entry)return 0;/* check if the file or directory already exists */while(1){if(!fat16_read_dir(parent, dir_entry))break;if(strcmp(dir, dir_entry->long_name) == 0){fat16_reset_dir(parent);return 0;}}struct fat16_fs_struct* fs = parent->fs;/* allocate cluster which will hold directory entries */uint16_t dir_cluster = fat16_append_clusters(fs, 0, 1);if(!dir_cluster)return 0;/* clear cluster to prevent bogus directory entries */fat16_clear_cluster(fs, dir_cluster);memset(dir_entry, 0, sizeof(*dir_entry));dir_entry->attributes = FAT16_ATTRIB_DIR;/* create "." directory self reference */dir_entry->entry_offset = fs->header.cluster_zero_offset +(uint32_t) (dir_cluster - 2) * fs->header.cluster_size;dir_entry->long_name[0] = '.';dir_entry->cluster = dir_cluster;if(!fat16_write_dir_entry(fs, dir_entry)){fat16_free_clusters(fs, dir_cluster);return 0;}/* create ".." parent directory reference */dir_entry->entry_offset += 32;dir_entry->long_name[1] = '.';dir_entry->cluster = parent->dir_entry.cluster;if(!fat16_write_dir_entry(fs, dir_entry)){fat16_free_clusters(fs, dir_cluster);return 0;}/* fill directory entry */strncpy(dir_entry->long_name, dir, sizeof(dir_entry->long_name) - 1);dir_entry->cluster = dir_cluster;/* find place where to store directory entry */if(!(dir_entry->entry_offset = fat16_find_offset_for_dir_entry(fs, parent, dir_entry))){fat16_free_clusters(fs, dir_cluster);return 0;}/* write directory to disk */if(!fat16_write_dir_entry(fs, dir_entry)){fat16_free_clusters(fs, dir_cluster);return 0;}return 1;#elsereturn 0;#endif}/*** \ingroup fat16_dir* Deletes a directory.** This is just a synonym for fat16_delete_file().* If a directory is deleted without first deleting its* subdirectories and files, disk space occupied by these* files will get wasted as there is no chance to release* it and mark it as free.** \param[in] fs The filesystem on which to operate.* \param[in] dir_entry The directory entry of the directory to delete.* \returns 0 on failure, 1 on success.* \see fat16_create_dir*/#ifdef DOXYGENuint8_t fat16_delete_dir(struct fat16_fs_struct* fs, struct fat16_dir_entry_struct* dir_entry);#endif/*** \ingroup fat16_file* Returns the modification date of a file.** \param[in] dir_entry The directory entry of which to return the modification date.* \param[out] year The year the file was last modified.* \param[out] month The month the file was last modified.* \param[out] day The day the file was last modified.*/void fat16_get_file_modification_date(const struct fat16_dir_entry_struct* dir_entry, uint16_t* year, uint8_t* month, uint8_t* day){#if FAT16_DATETIME_SUPPORTif(!dir_entry)return;*year = 1980 + ((dir_entry->modification_date >> 9) & 0x7f);*month = (dir_entry->modification_date >> 5) & 0x0f;*day = (dir_entry->modification_date >> 0) & 0x1f;#endif}/*** \ingroup fat16_file* Returns the modification time of a file.** \param[in] dir_entry The directory entry of which to return the modification time.* \param[out] hour The hour the file was last modified.* \param[out] min The min the file was last modified.* \param[out] sec The sec the file was last modified.*/void fat16_get_file_modification_time(const struct fat16_dir_entry_struct* dir_entry, uint8_t* hour, uint8_t* min, uint8_t* sec){#if FAT16_DATETIME_SUPPORTif(!dir_entry)return;*hour = (dir_entry->modification_time >> 11) & 0x1f;*min = (dir_entry->modification_time >> 5) & 0x3f;*sec = ((dir_entry->modification_time >> 0) & 0x1f) * 2;#endif}/*** \ingroup fat16_file* Sets the modification time of a date.** \param[in] dir_entry The directory entry for which to set the modification date.* \param[in] year The year the file was last modified.* \param[in] month The month the file was last modified.* \param[in] day The day the file was last modified.*/void fat16_set_file_modification_date(struct fat16_dir_entry_struct* dir_entry, uint16_t year, uint8_t month, uint8_t day){#if FAT16_WRITE_SUPPORT#if FAT16_DATETIME_SUPPORTif(!dir_entry)return;dir_entry->modification_date =((year - 1980) << 9) |((uint16_t) month << 5) |((uint16_t) day << 0);#endif#endif}/*** \ingroup fat16_file* Sets the modification time of a file.** \param[in] dir_entry The directory entry for which to set the modification time.* \param[in] hour The year the file was last modified.* \param[in] min The month the file was last modified.* \param[in] sec The day the file was last modified.*/void fat16_set_file_modification_time(struct fat16_dir_entry_struct* dir_entry, uint8_t hour, uint8_t min, uint8_t sec){#if FAT16_WRITE_SUPPORT#if FAT16_DATETIME_SUPPORTif(!dir_entry)return;dir_entry->modification_time =((uint16_t) hour << 11) |((uint16_t) min << 5) |((uint16_t) sec >> 1) ;#endif#endif}/*** \ingroup fat16_fs* Returns the amount of total storage capacity of the filesystem in bytes.** \param[in] fs The filesystem on which to operate.* \returns 0 on failure, the filesystem size in bytes otherwise.*/uint32_t fat16_get_fs_size(const struct fat16_fs_struct* fs){if(!fs)return 0;return (fs->header.fat_size / 2 - 2) * fs->header.cluster_size;}/*** \ingroup fat16_fs* Returns the amount of free storage capacity on the filesystem in bytes.** \note As the FAT16 filesystem is cluster based, this function does not* return continuous values but multiples of the cluster size.** \param[in] fs The filesystem on which to operate.* \returns 0 on failure, the free filesystem space in bytes otherwise.*/uint32_t fat16_get_fs_free(const struct fat16_fs_struct* fs){if(!fs)return 0;uint8_t fat[32];struct fat16_usage_count_callback_arg count_arg;count_arg.cluster_count = 0;count_arg.buffer_size = sizeof(fat);uint32_t fat_offset = fs->header.fat_offset;uint32_t fat_size = fs->header.fat_size;while(fat_size > 0){uint16_t length = UINT16_MAX - 1;if(fat_size < length)length = fat_size;if(!fs->partition->device_read_interval(fat_offset,fat,sizeof(fat),length,fat16_get_fs_free_callback,&count_arg))return 0;fat_offset += length;fat_size -= length;}return (uint32_t) count_arg.cluster_count * fs->header.cluster_size;}/*** \ingroup fat16_fs* Callback function used for counting free clusters.*/uint8_t fat16_get_fs_free_callback(uint8_t* buffer, uint32_t offset, void* p){struct fat16_usage_count_callback_arg* count_arg = (struct fat16_usage_count_callback_arg*) p;uint8_t buffer_size = count_arg->buffer_size;for(uint8_t i = 0; i < buffer_size; i += 2){if((((uint16_t) buffer[1] << 8) | ((uint16_t) buffer[0] << 0)) == FAT16_CLUSTER_FREE)++(count_arg->cluster_count);buffer += 2;}return 1;}