📄 fat16.c
字号:
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_SUPPORT dir_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);#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 */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_SUPPORT if(!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;#else return 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_SUPPORT if(!fs || cluster_num < 2)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -