📄 fat16.c
字号:
* 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.
*/
u8 fat16_write_dir_entry(const struct fat16_fs_struct* fs, struct fat16_dir_entry_struct* dir_entry)
{
#if FAT16_WRITE_SUPPORT
if(!fs || !dir_entry)
return 0;
#if FAT16_DATETIME_SUPPORT
{
u16 year;
u8 month;
u8 day;
u8 hour;
u8 min;
u8 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);
}
#endif
device_write_t device_write = fs->partition->device_write;
u32 offset = dir_entry->entry_offset;
const char* name = dir_entry->long_name;
u8 name_len = strlen(name);
u8 lfn_entry_count = (name_len + 12) / 13;
u8 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)
{
u8 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.
*/
u8 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_SUPPORT
buffer[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;
#endif
buffer[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 + (u32) lfn_entry_count * 32, buffer, sizeof(buffer)))
return 0;
/* calculate checksum of 8.3 name */
u8 checksum = buffer[0];
for(u8 i = 1; i < 11; ++i)
checksum = ((checksum >> 1) | (checksum << 7)) + buffer[i];
/* write lfn entries */
for(u8 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;
u8 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;
#else
return 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
*/
u8 fat16_create_file(struct fat16_dir_struct* parent, const char* file, struct fat16_dir_entry_struct* dir_entry)
{
#if FAT16_WRITE_SUPPORT
if(!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;
#else
return 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
*/
u8 fat16_delete_file(struct fat16_fs_struct* fs, struct fat16_dir_entry_struct* dir_entry)
{
#if FAT16_WRITE_SUPPORT
if(!fs || !dir_entry)
return 0;
/* get offset of the file's directory entry */
u32 dir_entry_offset = dir_entry->entry_offset;
if(!dir_entry_offset)
return 0;
u8 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));
#else
return 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
*/
u8 fat16_create_dir(struct fat16_dir_struct* parent, const char* dir, struct fat16_dir_entry_struct* dir_entry)
{
#if FAT16_WRITE_SUPPORT
if(!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 */
u16 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 +
(u32) (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;
#else
return 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 DOXYGEN
u8 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, u16* year, u8* month, u8* day)
{
#if FAT16_
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -