?? fat16.c
字號:
/* * Copyright (c) 2006-2008 by Roland Riegel <feedback@roland-riegel.de> * * This file is free software; you can redistribute it and/or modify * it under the terms of either the GNU General Public License version 2 * or the GNU Lesser General Public License version 2.1, both as * published by the Free Software Foundation. */#include "partition.h"#include "fat16.h"#include "fat16_config.h"#include "sd-reader_config.h"#include <string.h>#if USE_DYNAMIC_MEMORY #include <stdlib.h>#endif/** * \addtogroup fat16 FAT16 support * * This module implements FAT16 read and write access. * * The following features are supported: * - File names up to 31 characters long. * - Unlimited depth of subdirectories. * - Short 8.3 and long filenames. * - Creating and deleting files. * - Reading and writing from and to files. * - File resizing. * - File sizes of up to 4 gigabytes. * * @{ *//** * \file * FAT16 implementation (license: GPLv2 or LGPLv2.1) * * \author Roland Riegel *//** * \addtogroup fat16_config FAT16 configuration * Preprocessor defines to configure the FAT16 implementation. *//** * \addtogroup fat16_fs FAT16 access * Basic functions for handling a FAT16 filesystem. *//** * \addtogroup fat16_file FAT16 file functions * Functions for managing files. *//** * \addtogroup fat16_dir FAT16 directory functions * Functions for managing directories. *//** * @} */#define FAT16_CLUSTER_FREE 0x0000#define FAT16_CLUSTER_RESERVED_MIN 0xfff0#define FAT16_CLUSTER_RESERVED_MAX 0xfff6#define FAT16_CLUSTER_BAD 0xfff7#define FAT16_CLUSTER_LAST_MIN 0xfff8#define FAT16_CLUSTER_LAST_MAX 0xffff#define FAT16_DIRENTRY_DELETED 0xe5#define FAT16_DIRENTRY_LFNLAST (1 << 6)#define FAT16_DIRENTRY_LFNSEQMASK ((1 << 6) - 1)/* Each entry within the directory table has a size of 32 bytes * and either contains a 8.3 DOS-style file name or a part of a * long file name, which may consist of several directory table * entries at once. * * multi-byte integer values are stored little-endian! * * 8.3 file name entry: * ==================== * offset length description * 0 8 name (space padded) * 8 3 extension (space padded) * 11 1 attributes (FAT16_ATTRIB_*) * * long file name (lfn) entry ordering for a single file name: * =========================================================== * LFN entry n * ... * LFN entry 2 * LFN entry 1 * 8.3 entry (see above) * * lfn entry: * ========== * offset length description * 0 1 ordinal field * 1 2 unicode character 1 * 3 3 unicode character 2 * 5 3 unicode character 3 * 7 3 unicode character 4 * 9 3 unicode character 5 * 11 1 attribute (always 0x0f) * 12 1 type (reserved, always 0) * 13 1 checksum * 14 2 unicode character 6 * 16 2 unicode character 7 * 18 2 unicode character 8 * 20 2 unicode character 9 * 22 2 unicode character 10 * 24 2 unicode character 11 * 26 2 cluster (unused, always 0) * 28 2 unicode character 12 * 30 2 unicode character 13 * * The ordinal field contains a descending number, from n to 1. * For the n'th lfn entry the ordinal field is or'ed with 0x40. * For deleted lfn entries, the ordinal field is set to 0xe5. */struct fat16_header_struct{ uint32_t size; uint32_t fat_offset; uint32_t fat_size; uint16_t sector_size; uint16_t cluster_size; uint32_t root_dir_offset; uint32_t cluster_zero_offset;};struct fat16_fs_struct{ struct partition_struct* partition; struct fat16_header_struct header;};struct fat16_file_struct{ struct fat16_fs_struct* fs; struct fat16_dir_entry_struct dir_entry; uint32_t pos; uint16_t pos_cluster;};struct fat16_dir_struct{ struct fat16_fs_struct* fs; struct fat16_dir_entry_struct dir_entry; uint16_t entry_cluster; uint16_t entry_offset;};struct fat16_read_dir_callback_arg{ struct fat16_dir_entry_struct* dir_entry; uint16_t bytes_read; uint8_t finished;};struct fat16_usage_count_callback_arg{ uint16_t cluster_count; uint8_t buffer_size;};#if !USE_DYNAMIC_MEMORYstatic struct fat16_fs_struct fat16_fs_handles[FAT16_FS_COUNT];static struct fat16_file_struct fat16_file_handles[FAT16_FILE_COUNT];static struct fat16_dir_struct fat16_dir_handles[FAT16_DIR_COUNT];#endifstatic uint8_t fat16_read_header(struct fat16_fs_struct* fs);static uint16_t fat16_get_next_cluster(const struct fat16_fs_struct* fs, uint16_t cluster_num);static uint16_t fat16_append_clusters(const struct fat16_fs_struct* fs, uint16_t cluster_num, uint16_t count);static uint8_t fat16_free_clusters(const struct fat16_fs_struct* fs, uint16_t cluster_num);static uint8_t fat16_terminate_clusters(const struct fat16_fs_struct* fs, uint16_t cluster_num);static uint8_t fat16_clear_cluster(const struct fat16_fs_struct* fs, uint16_t cluster_num);static uint16_t fat16_clear_cluster_callback(uint8_t* buffer, uint32_t offset, void* p);static uint32_t fat16_cluster_offset(const struct fat16_fs_struct* fs, uint16_t cluster_num);static uint8_t fat16_dir_entry_read_callback(uint8_t* buffer, uint32_t offset, void* p);static uint8_t fat16_interpret_dir_entry(struct fat16_dir_entry_struct* dir_entry, const uint8_t* raw_entry);static uint32_t 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);static uint8_t fat16_write_dir_entry(const struct fat16_fs_struct* fs, struct fat16_dir_entry_struct* dir_entry);static uint8_t fat16_get_fs_free_callback(uint8_t* buffer, uint32_t offset, void* p);static void fat16_set_file_modification_date(struct fat16_dir_entry_struct* dir_entry, uint16_t year, uint8_t month, uint8_t day);static void fat16_set_file_modification_time(struct fat16_dir_entry_struct* dir_entry, uint8_t hour, uint8_t min, uint8_t sec);/** * \ingroup fat16_fs * Opens a FAT16 filesystem. * * \param[in] partition Discriptor of partition on which the filesystem resides. * \returns 0 on error, a FAT16 filesystem descriptor on success. * \see fat16_open */struct fat16_fs_struct* fat16_open(struct partition_struct* partition){ if(!partition ||#if FAT16_WRITE_SUPPORT !partition->device_write || !partition->device_write_interval#else 0#endif ) return 0;#if USE_DYNAMIC_MEMORY struct fat16_fs_struct* fs = malloc(sizeof(*fs)); if(!fs) return 0;#else struct fat16_fs_struct* fs = fat16_fs_handles; uint8_t i; for(i = 0; i < FAT16_FS_COUNT; ++i) { if(!fs->partition) break; ++fs; } if(i >= FAT16_FS_COUNT) return 0;#endif memset(fs, 0, sizeof(*fs)); fs->partition = partition; if(!fat16_read_header(fs)) {#if USE_DYNAMIC_MEMORY free(fs);#else fs->partition = 0;#endif return 0; } return fs;}/** * \ingroup fat16_fs * Closes a FAT16 filesystem. * * When this function returns, the given filesystem descriptor * will be invalid. * * \param[in] fs The filesystem to close. * \see fat16_open */void fat16_close(struct fat16_fs_struct* fs){ if(!fs) return;#if USE_DYNAMIC_MEMORY free(fs);#else fs->partition = 0;#endif}/** * \ingroup fat16_fs * Reads and parses the header of a FAT16 filesystem. * * \param[inout] fs The filesystem for which to parse the header. * \returns 0 on failure, 1 on success. */uint8_t fat16_read_header(struct fat16_fs_struct* fs){ if(!fs) return 0; struct partition_struct* partition = fs->partition; if(!partition) return 0; /* read fat parameters */ uint8_t buffer[25]; uint32_t partition_offset = partition->offset * 512; if(!partition->device_read(partition_offset + 0x0b, buffer, sizeof(buffer))) return 0; uint16_t bytes_per_sector = ((uint16_t) buffer[0x00]) | ((uint16_t) buffer[0x01] << 8); uint8_t sectors_per_cluster = buffer[0x02]; uint16_t reserved_sectors = ((uint16_t) buffer[0x03]) | ((uint16_t) buffer[0x04] << 8); uint8_t fat_copies = buffer[0x05]; uint16_t max_root_entries = ((uint16_t) buffer[0x06]) | ((uint16_t) buffer[0x07] << 8); uint16_t sector_count_16 = ((uint16_t) buffer[0x08]) | ((uint16_t) buffer[0x09] << 8); uint16_t sectors_per_fat = ((uint16_t) buffer[0x0b]) | ((uint16_t) buffer[0x0c] << 8); uint32_t sector_count = ((uint32_t) buffer[0x15]) | ((uint32_t) buffer[0x16] << 8) | ((uint32_t) buffer[0x17] << 16) | ((uint32_t) buffer[0x18] << 24); if(sectors_per_fat == 0) /* this is not a FAT16 */ return 0; if(sector_count == 0) { if(sector_count_16 == 0) /* illegal volume size */ return 0; else sector_count = sector_count_16; } /* ensure we really have a FAT16 fs here */ uint32_t data_sector_count = sector_count - reserved_sectors - (uint32_t) sectors_per_fat * fat_copies - ((max_root_entries * 32 + bytes_per_sector - 1) / bytes_per_sector); uint32_t data_cluster_count = data_sector_count / sectors_per_cluster; if(data_cluster_count < 4085 || data_cluster_count >= 65525) /* this is not a FAT16 */ return 0; partition->type = PARTITION_TYPE_FAT16; /* fill header information */ struct fat16_header_struct* header = &fs->header; memset(header, 0, sizeof(*header)); header->size = sector_count * bytes_per_sector; header->fat_offset = /* jump to partition */ partition_offset + /* jump to fat */ (uint32_t) reserved_sectors * bytes_per_sector; header->fat_size = (data_cluster_count + 2) * 2; header->sector_size = bytes_per_sector; header->cluster_size = (uint32_t) bytes_per_sector * sectors_per_cluster; header->root_dir_offset = /* jump to fats */ header->fat_offset + /* jump to root directory entries */ (uint32_t) fat_copies * sectors_per_fat * bytes_per_sector; header->cluster_zero_offset = /* jump to root directory entries */ header->root_dir_offset + /* skip root directory entries */ (uint32_t) max_root_entries * 32; return 1;}/** * \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; while(1) { if(path[0] == '\0') return 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* sub_path = strchr(path, '/'); uint8_t length_to_sep; if(sub_path) { length_to_sep = sub_path - path; ++sub_path; } else { length_to_sep = strlen(path); sub_path = path + length_to_sep; } /* 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 */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -