📄 fat.c
字号:
{ /* free all clusters no longer needed */ fat_terminate_clusters(fd->fs, cluster_num); } } while(0); /* correct file position */ if(size < fd->pos) { fd->pos = size; fd->pos_cluster = 0; } return 1;}#endif/** * \ingroup fat_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 fat_close_dir */struct fat_dir_struct* fat_open_dir(struct fat_fs_struct* fs, const struct fat_dir_entry_struct* dir_entry){ if(!fs || !dir_entry || !(dir_entry->attributes & FAT_ATTRIB_DIR)) return 0;#if USE_DYNAMIC_MEMORY struct fat_dir_struct* dd = malloc(sizeof(*dd)); if(!dd) return 0;#else struct fat_dir_struct* dd = fat_dir_handles; uint8_t i; for(i = 0; i < FAT_DIR_COUNT; ++i) { if(!dd->fs) break; ++dd; } if(i >= FAT_DIR_COUNT) return 0;#endif memcpy(&dd->dir_entry, dir_entry, sizeof(*dir_entry)); dd->fs = fs; dd->entry_cluster = dir_entry->cluster; dd->entry_offset = 0; return dd;}/** * \ingroup fat_dir * Closes a directory descriptor. * * This function destroys a directory descriptor which was * previously obtained by calling fat_open_dir(). When this * function returns, the given descriptor will be invalid. * * \param[in] dd The directory descriptor to close. * \see fat_open_dir */void fat_close_dir(struct fat_dir_struct* dd){ if(dd)#if USE_DYNAMIC_MEMORY free(dd);#else dd->fs = 0;#endif}/** * \ingroup fat_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 fat_reset_dir */uint8_t fat_read_dir(struct fat_dir_struct* dd, struct fat_dir_entry_struct* dir_entry){ if(!dd || !dir_entry) return 0; /* get current position of directory handle */ struct fat_fs_struct* fs = dd->fs; const struct fat_header_struct* header = &fs->header; uint16_t cluster_size = header->cluster_size; cluster_t cluster_num = dd->entry_cluster; uint16_t cluster_offset = dd->entry_offset; struct fat_read_dir_callback_arg arg; /* reset directory entry */ memset(dir_entry, 0, sizeof(*dir_entry)); /* reset callback arguments */ memset(&arg, 0, sizeof(arg)); arg.dir_entry = dir_entry; /* check if we read from the root directory */ if(cluster_num == 0) {#if FAT_FAT32_SUPPORT if(fs->partition->type == PARTITION_TYPE_FAT32) cluster_num = header->root_dir_cluster; else#endif cluster_size = header->cluster_zero_offset - header->root_dir_offset; } /* read entries */ uint8_t buffer[32]; while(!arg.finished) { /* read directory entries up to the cluster border */ uint16_t cluster_left = cluster_size - cluster_offset; uint32_t pos = cluster_offset; if(cluster_num == 0) pos += header->root_dir_offset; else pos += fat_cluster_offset(fs, cluster_num); arg.bytes_read = 0; if(!fs->partition->device_read_interval(pos, buffer, sizeof(buffer), cluster_left, fat_dir_entry_read_callback, &arg) ) return 0; cluster_offset += arg.bytes_read; if(cluster_offset >= cluster_size) { /* we reached the cluster border and switch to the next cluster */ cluster_offset = 0; /* get number of next cluster */ if(!(cluster_num = fat_get_next_cluster(fs, cluster_num))) { /* directory entry not found, reset directory handle */ cluster_num = dd->dir_entry.cluster; break; } } } dd->entry_cluster = cluster_num; dd->entry_offset = cluster_offset; return dir_entry->long_name[0] != '\0' ? 1 : 0;}/** * \ingroup fat_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 fat_read_dir */uint8_t fat_reset_dir(struct fat_dir_struct* dd){ if(!dd) return 0; dd->entry_cluster = dd->dir_entry.cluster; dd->entry_offset = 0; return 1;}/** * \ingroup fat_fs * Callback function for reading a directory entry. */uint8_t fat_dir_entry_read_callback(uint8_t* buffer, offset_t offset, void* p){ struct fat_read_dir_callback_arg* arg = p; struct fat_dir_entry_struct* dir_entry = arg->dir_entry; arg->bytes_read += 32; /* skip deleted or empty entries */ if(buffer[0] == FAT_DIRENTRY_DELETED || !buffer[0]) return 1; if(!dir_entry->entry_offset) dir_entry->entry_offset = offset; switch(fat_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 */ { arg->finished = 1; return 0; } } return 0;}/** * \ingroup fat_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 fat_interpret_dir_entry(struct fat_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) { /* Lfn supports unicode, but we do not, for now. * So we assume pure ascii and read only every * second byte. */ uint16_t char_offset = ((raw_entry[0] & 0x3f) - 1) * 13; const uint8_t char_mapping[] = { 1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30 }; for(uint8_t i = 0; i <= 12 && char_offset + i < sizeof(dir_entry->long_name) - 1; ++i) long_name[char_offset + i] = raw_entry[char_mapping[i]]; 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]; /* Windows NT and later versions do not store LFN entries * for 8.3 names which have a lowercase basename, extension * or both when everything else is uppercase. They use two * extra bits to signal a lowercase basename or extension. */ if((raw_entry[12] & 0x08) && raw_entry[i] >= 'A' && raw_entry[i] <= 'Z') long_name[i] += 'a' - 'A'; } if(long_name[0] == 0x05) long_name[0] = (char) FAT_DIRENTRY_DELETED; if(raw_entry[8] != ' ') { long_name[i++] = '.'; uint8_t j = 8; for(; j < 11; ++j) { if(raw_entry[j] == ' ') break; long_name[i] = raw_entry[j]; /* See above for the lowercase 8.3 name handling of * Windows NT and later. */ if((raw_entry[12] & 0x10) && raw_entry[j] >= 'A' && raw_entry[j] <= 'Z') long_name[i] += 'a' - 'A'; ++i; } } long_name[i] = '\0'; } /* extract properties of file and store them within the structure */ dir_entry->attributes = raw_entry[11]; dir_entry->cluster = ltoh16(*((uint16_t*) &raw_entry[26]));#if FAT_FAT32_SUPPORT dir_entry->cluster |= ((cluster_t) ltoh16(*((uint16_t*) &raw_entry[20]))) << 16;#endif dir_entry->file_size = ltoh32(*((uint32_t*) &raw_entry[28]));#if FAT_DATETIME_SUPPORT dir_entry->modification_time = ltoh16(*((uint16_t*) &raw_entry[22])); dir_entry->modification_date = ltoh16(*((uint16_t*) &raw_entry[24]));#endif return 2; }}#if DOXYGEN || FAT_WRITE_SUPPORT/** * \ingroup fat_fs * Searches for space where to store a directory entry. * * \param[in] fs The filesystem on which to operate. * \param[in] parent The directory in which to search. * \param[in] dir_entry The directory entry for which to search space. * \returns 0 on failure, a device offset on success. */offset_t fat_find_offset_for_dir_entry(const struct fat_fs_struct* fs, const struct fat_dir_struct* parent, const struct fat_dir_entry_struct* dir_entry){ if(!fs || !dir_entry) return 0; /* search for a place where to write the directory entry to disk */ uint8_t free_dir_entries_needed = (strlen(dir_entry->long_name) + 12) / 13 + 1; uint8_t free_dir_entries_found = 0; cluster_t cluster_num = parent->dir_entry.cluster; offset_t dir_entry_offset = 0; offset_t offset = 0; offset_t offset_to = 0;#if FAT_FAT32_SUPPORT uint8_t is_fat32 = (fs->partition->type == PARTITION_TYPE_FAT32);#endif if(cluster_num == 0) {#if FAT_FAT32_SUPPORT if(is_fat32) { cluster_num = fs->header.root_dir_cluster; } else#endif { /* 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 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. */ cluster_t cluster_next = fat_get_next_cluster(fs, cluster_num); if(!cluster_next) { cluster_next = fat_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 + (offset_t) (cluster_next - 2) * fs->header.cluster_size; /* clear cluster to avoid garbage directory entries */ fat_clear_cluster(fs, cluster_next); break; } cluster_num = cluster_next; } offset = fat_cluster_offset(fs, cluster_num); offset_to = offset + fs->header.cluster_size; dir_entry_offset = offset; free_dir_entries_found = 0; } /* read next lfn or 8.3 entry */ uint8_t 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 == FAT_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;}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -