📄 fat16.c
字号:
/* 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
*/
u8 fat16_read_sub_dir_entry(const struct fat16_fs_struct* fs, u16 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 */
u8 buffer[32];
u32 cluster_offset;
u16 cluster_size = fs->header.cluster_size;
u16 cluster_num = parent->cluster;
struct fat16_read_callback_arg arg;
while(1)
{
/* calculate new cluster offset */
cluster_offset = fs->header.cluster_zero_offset + (u32) (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.
*/
u8 fat16_dir_entry_seek_callback(u8* buffer, u32 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.
*/
u8 fat16_dir_entry_read_callback(u8* buffer, u32 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.
*/
u8 fat16_interpret_dir_entry(struct fat16_dir_entry_struct* dir_entry, const u8* raw_entry)
{
if(!dir_entry || !raw_entry || !raw_entry[0])
return 0;
char* long_name = dir_entry->long_name;
if(raw_entry[11] == 0x0f)
{
u16 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')
{
u8 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++] = '.';
u8 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 = ((u16) raw_entry[26]) |
((u16) raw_entry[27] << 8);
dir_entry->file_size = ((u32) raw_entry[28]) |
((u32) raw_entry[29] << 8) |
((u32) raw_entry[30] << 16) |
((u32) raw_entry[31] << 24);
#if FAT16_DATETIME_SUPPORT
dir_entry->modification_time = ((u16) raw_entry[22]) |
((u16) raw_entry[23] << 8);
dir_entry->modification_date = ((u16) raw_entry[24]) |
((u16) raw_entry[25] << 8);
#endif
return 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
*/
u8 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);
u8 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.
*/
u16 fat16_get_next_cluster(const struct fat16_fs_struct* fs, u16 cluster_num)
{
if(!fs || cluster_num < 2)
return 0;
/* read appropriate fat entry */
u8 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 = ((u16) fat_entry[0]) |
((u16) 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.
*/
u16 fat16_append_clusters(const struct fat16_fs_struct* fs, u16 cluster_num, u16 count)
{
#if FAT16_WRITE_SUPPORT
if(!fs)
return 0;
device_read_t device_read = fs->partition->device_read;
device_write_t device_write = fs->partition->device_write;
u32 fat_offset = fs->header.fat_offset;
u16 cluster_max = fs->header.fat_size / 2;
u16 cluster_next = 0;
u16 count_left = count;
u8 buffer[2];
for(u16 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -