📄 fat.c
字号:
#if DOXYGEN || FAT_WRITE_SUPPORT/** * \ingroup fat_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 fat_write_dir_entry(const struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry){ if(!fs || !dir_entry) return 0; #if FAT_DATETIME_SUPPORT { uint16_t year; uint8_t month; uint8_t day; uint8_t hour; uint8_t min; uint8_t sec; fat_get_datetime(&year, &month, &day, &hour, &min, &sec); fat_set_file_modification_date(dir_entry, year, month, day); fat_set_file_modification_time(dir_entry, hour, min, sec); }#endif device_write_t device_write = fs->partition->device_write; offset_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] == FAT_DIRENTRY_DELETED) buffer[0] = 0x05; /* fill directory entry buffer */ memset(&buffer[11], 0, sizeof(buffer) - 11); buffer[0x0b] = dir_entry->attributes;#if FAT_DATETIME_SUPPORT *((uint16_t*) &buffer[0x16]) = htol16(dir_entry->modification_time); *((uint16_t*) &buffer[0x18]) = htol16(dir_entry->modification_date);#endif#if FAT_FAT32_SUPPORT *((uint16_t*) &buffer[0x14]) = htol16((uint16_t) (dir_entry->cluster >> 16));#endif *((uint16_t*) &buffer[0x1a]) = htol16(dir_entry->cluster); *((uint32_t*) &buffer[0x1c]) = htol32(dir_entry->file_size); /* write to disk */ if(!device_write(offset + (uint16_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] |= FAT_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;}#endif#if DOXYGEN || FAT_WRITE_SUPPORT/** * \ingroup fat_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 fat_delete_file */uint8_t fat_create_file(struct fat_dir_struct* parent, const char* file, struct fat_dir_entry_struct* dir_entry){ if(!parent || !file || !file[0] || !dir_entry) return 0; /* check if the file already exists */ while(1) { if(!fat_read_dir(parent, dir_entry)) break; if(strcmp(file, dir_entry->long_name) == 0) { fat_reset_dir(parent); return 0; } } struct fat_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 = fat_find_offset_for_dir_entry(fs, parent, dir_entry))) return 0; /* write directory entry to disk */ if(!fat_write_dir_entry(fs, dir_entry)) return 0; return 1;}#endif#if DOXYGEN || FAT_WRITE_SUPPORT/** * \ingroup fat_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 fat_create_file */uint8_t fat_delete_file(struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry){ if(!fs || !dir_entry) return 0; /* get offset of the file's directory entry */ offset_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] = FAT_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 || fat_free_clusters(fs, dir_entry->cluster));}#endif#if DOXYGEN || FAT_WRITE_SUPPORT/** * \ingroup fat_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 fat_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 fat_delete_dir */uint8_t fat_create_dir(struct fat_dir_struct* parent, const char* dir, struct fat_dir_entry_struct* dir_entry){ if(!parent || !dir || !dir[0] || !dir_entry) return 0; /* check if the file or directory already exists */ while(fat_read_dir(parent, dir_entry)) { if(strcmp(dir, dir_entry->long_name) == 0) { fat_reset_dir(parent); return 0; } } struct fat_fs_struct* fs = parent->fs; /* allocate cluster which will hold directory entries */ cluster_t dir_cluster = fat_append_clusters(fs, 0, 1); if(!dir_cluster) return 0; /* clear cluster to prevent bogus directory entries */ fat_clear_cluster(fs, dir_cluster); memset(dir_entry, 0, sizeof(*dir_entry)); dir_entry->attributes = FAT_ATTRIB_DIR; /* create "." directory self reference */ dir_entry->entry_offset = fs->header.cluster_zero_offset + (offset_t) (dir_cluster - 2) * fs->header.cluster_size; dir_entry->long_name[0] = '.'; dir_entry->cluster = dir_cluster; if(!fat_write_dir_entry(fs, dir_entry)) { fat_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(!fat_write_dir_entry(fs, dir_entry)) { fat_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 = fat_find_offset_for_dir_entry(fs, parent, dir_entry))) { fat_free_clusters(fs, dir_cluster); return 0; } /* write directory to disk */ if(!fat_write_dir_entry(fs, dir_entry)) { fat_free_clusters(fs, dir_cluster); return 0; } return 1;}#endif/** * \ingroup fat_dir * Deletes a directory. * * This is just a synonym for fat_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 fat_create_dir */#ifdef DOXYGENuint8_t fat_delete_dir(struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry);#endif#if DOXYGEN || FAT_DATETIME_SUPPORT/** * \ingroup fat_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 fat_get_file_modification_date(const struct fat_dir_entry_struct* dir_entry, uint16_t* year, uint8_t* month, uint8_t* day){ if(!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#if DOXYGEN || FAT_DATETIME_SUPPORT/** * \ingroup fat_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 fat_get_file_modification_time(const struct fat_dir_entry_struct* dir_e
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -