📄 fat16.c
字号:
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)
{
u32 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;
#else
return -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.
*/
u8 fat16_seek_file(struct fat16_file_struct* fd, s32* offset, u8 whence)
{
if(!fd || !offset)
return 0;
u32 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.
*/
u8 fat16_resize_file(struct fat16_file_struct* fd, u32 size)
{
#if FAT16_WRITE_SUPPORT
if(!fd)
return 0;
u16 cluster_num = fd->dir_entry.cluster;
u16 cluster_size = fd->fs->header.cluster_size;
u32 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 */
u16 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.
*/
u16 cluster_count = size_new / cluster_size;
if((u32) cluster_count * cluster_size < size_new)
++cluster_count;
u16 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;
#else
return 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_MEMORY
struct fat16_dir_struct* dd = malloc(sizeof(*dd));
if(!dd)
return 0;
#else
struct fat16_dir_struct* dd = fat16_dir_handlers;
u8 i;
for(i = 0; i < FAT16_DIR_COUNT; ++i)
{
if(!dd->fs)
break;
++dd;
}
if(i >= FAT16_DIR_COUNT)
return 0;
#endif
memcpy(&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_MEMORY
free(dd);
#else
dd->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
*/
u8 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
*/
u8 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.
*/
u32 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_SUPPORT
if(!fs || !dir_entry)
return 0;
/* search for a place where to write the directory entry to disk */
u8 free_dir_entries_needed = (strlen(dir_entry->long_name) + 12) / 13 + 1;
u8 free_dir_entries_found = 0;
u16 cluster_num = parent->dir_entry.cluster;
u32 dir_entry_offset = 0;
u32 offset = 0;
u32 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.
*/
u16 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 +
(u32) (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 +
(u32) (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 */
u8 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;
#else
return 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -