include/nffs/nffs.h (447 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef H_NFFS_ #define H_NFFS_ #include <stddef.h> #include <inttypes.h> #include <nffs/config.h> #include <nffs/queue.h> #include <nffs/os.h> #ifdef __cplusplus extern "C" { #endif /* * File access flags. */ #define FS_ACCESS_READ 0x01 #define FS_ACCESS_WRITE 0x02 #define FS_ACCESS_APPEND 0x04 #define FS_ACCESS_TRUNCATE 0x08 /* * File access return codes. */ #define FS_EOK 0 /* Success */ #define FS_ECORRUPT 1 /* File system corrupt */ #define FS_EHW 2 /* Error accessing storage medium */ #define FS_EOFFSET 3 /* Invalid offset */ #define FS_EINVAL 4 /* Invalid argument */ #define FS_ENOMEM 5 /* Insufficient memory */ #define FS_ENOENT 6 /* No such file or directory */ #define FS_EEMPTY 7 /* Specified region is empty (internal only) */ #define FS_EFULL 8 /* Disk full */ #define FS_EUNEXP 9 /* Disk contains unexpected metadata */ #define FS_EOS 10 /* OS error */ #define FS_EEXIST 11 /* File or directory already exists */ #define FS_EACCESS 12 /* Operation prohibited by file open mode */ #define FS_EUNINIT 13 /* File system not initialized */ #define NFFS_FILENAME_MAX_LEN 256 /* Does not require null terminator. */ #define NFFS_HASH_SIZE 256 #define NFFS_ID_DIR_MIN 0 #define NFFS_ID_DIR_MAX 0x10000000 #define NFFS_ID_FILE_MIN 0x10000000 #define NFFS_ID_FILE_MAX 0x80000000 #define NFFS_ID_BLOCK_MIN 0x80000000 #define NFFS_ID_BLOCK_MAX 0xffffffff #define NFFS_ID_ROOT_DIR 0 #define NFFS_ID_NONE 0xffffffff #define NFFS_HASH_ENTRY_NONE 0xffffffff #define NFFS_AREA_MAGIC0 0xb98a31e2 #define NFFS_AREA_MAGIC1 0x7fb0428c #define NFFS_AREA_MAGIC2 0xace08253 #define NFFS_AREA_MAGIC3 0xb185fc8e #define NFFS_BLOCK_MAGIC 0x53ba23b9 #define NFFS_INODE_MAGIC 0x925f8bc0 #define NFFS_AREA_ID_NONE 0xff #define NFFS_AREA_VER_0 0 #define NFFS_AREA_VER_1 1 #define NFFS_AREA_VER NFFS_AREA_VER_1 #define NFFS_AREA_OFFSET_ID 23 #define NFFS_SHORT_FILENAME_LEN 3 #define NFFS_BLOCK_MAX_DATA_SZ_MAX NFFS_CONFIG_MAX_BLOCK_SIZE #define NFFS_DETECT_FAIL_IGNORE 1 #define NFFS_DETECT_FAIL_FORMAT 2 struct nffs_flash_desc { uint8_t id; uint32_t sector_count; uint32_t area_offset; uint32_t area_size; }; struct nffs_area_desc { uint32_t nad_offset; /* Flash offset of start of area. */ uint32_t nad_length; /* Size of area, in bytes. */ uint8_t nad_flash_id; /* Logical flash id */ }; /** On-disk representation of an area header. */ struct nffs_disk_area { uint32_t nda_magic[4]; /* NFFS_AREA_MAGIC{0,1,2,3} */ uint32_t nda_length; /* Total size of area, in bytes. */ uint8_t nda_ver; /* Current nffs version: 0 */ uint8_t nda_gc_seq; /* Garbage collection count. */ uint8_t reserved8; uint8_t nda_id; /* 0xff if scratch area. */ }; /** On-disk representation of an inode (file or directory). */ struct nffs_disk_inode { uint32_t ndi_id; /* Unique object ID. */ uint32_t ndi_parent_id; /* Object ID of parent directory inode. */ uint32_t ndi_lastblock_id; /* Object ID of parent directory inode. */ uint16_t ndi_seq; /* Sequence number; greater supersedes lesser. */ uint16_t reserved16; uint8_t ndi_flags; /* flags */ uint8_t ndi_filename_len; /* Length of filename, in bytes. */ uint16_t ndi_crc16; /* Covers rest of header and filename. */ /* Followed by filename. */ }; #define NFFS_DISK_INODE_OFFSET_CRC 18 /** On-disk representation of a data block. */ struct nffs_disk_block { uint32_t ndb_id; /* Unique object ID. */ uint32_t ndb_inode_id; /* Object ID of owning inode. */ uint32_t ndb_prev_id; /* Object ID of previous block in file; NFFS_ID_NONE if this is the first block. */ uint16_t ndb_seq; /* Sequence number; greater supersedes lesser. */ uint16_t reserved16; uint16_t ndb_data_len; /* Length of data contents, in bytes. */ uint16_t ndb_crc16; /* Covers rest of header and data. */ /* Followed by 'ndb_data_len' bytes of data. */ }; #define NFFS_DISK_BLOCK_OFFSET_CRC 18 /** * What gets stored in the hash table. Each entry represents a data block or * an inode. */ struct nffs_hash_entry { SLIST_ENTRY(nffs_hash_entry) nhe_next; uint32_t nhe_id; /* 0 - 0x7fffffff if inode; else if block. */ uint32_t nhe_flash_loc; /* Upper-byte = area idx; rest = area offset. */ }; SLIST_HEAD(nffs_hash_list, nffs_hash_entry); SLIST_HEAD(nffs_inode_list, nffs_inode_entry); /** Each inode hash entry is actually one of these. */ struct nffs_inode_entry { struct nffs_hash_entry nie_hash_entry; SLIST_ENTRY(nffs_inode_entry) nie_sibling_next; union { struct nffs_inode_list nie_child_list; /* If directory */ struct nffs_hash_entry *nie_last_block_entry; /* If file */ uint32_t nie_lastblock_id; }; uint8_t nie_refcnt; uint8_t nie_flags; uint8_t nie_blkcnt; uint8_t reserved8; }; #define NFFS_INODE_FLAG_FREE 0x00 #define NFFS_INODE_FLAG_DUMMY 0x01 /* inode is a dummy */ #define NFFS_INODE_FLAG_DUMMYPARENT 0x02 /* parent not in cache */ #define NFFS_INODE_FLAG_DUMMYLSTBLK 0x04 /* lastblock not in cache */ #define NFFS_INODE_FLAG_DUMMYINOBLK 0x08 /* dummy inode for blk */ #define NFFS_INODE_FLAG_OBSOLETE 0x10 /* always replace if same ID */ #define NFFS_INODE_FLAG_INTREE 0x20 /* in directory structure */ #define NFFS_INODE_FLAG_INHASH 0x40 /* in hash table */ #define NFFS_INODE_FLAG_DELETED 0x80 /* inode deleted */ #define nie_id nie_hash_entry.nhe_id #define nie_flash_loc nie_hash_entry.nhe_flash_loc /** Full inode representation; not stored permanently RAM. */ struct nffs_inode { struct nffs_inode_entry *ni_inode_entry; /* Points to real inode entry. */ uint32_t ni_seq; /* Sequence number; greater supersedes lesser. */ struct nffs_inode_entry *ni_parent; /* Points to parent directory. */ uint8_t ni_filename_len; /* # chars in filename. */ uint8_t ni_filename[NFFS_SHORT_FILENAME_LEN]; /* First 3 bytes. */ }; /** Full data block representation; not stored permanently RAM. */ struct nffs_block { struct nffs_hash_entry *nb_hash_entry; /* Points to real block entry. */ uint32_t nb_seq; /* Sequence number; greater supersedes lesser. */ struct nffs_inode_entry *nb_inode_entry; /* Owning inode. */ struct nffs_hash_entry *nb_prev; /* Previous block in file. */ uint16_t nb_data_len; /* # of data bytes in block. */ uint16_t reserved16; }; struct nffs_file { OS_MULTIFS_CONTAINER; struct nffs_inode_entry *nf_inode_entry; uint32_t nf_offset; uint8_t nf_access_flags; }; struct nffs_area { uint32_t na_offset; uint32_t na_length; uint32_t na_cur; uint16_t na_id; uint8_t na_gc_seq; uint8_t na_flash_id; uint32_t na_obsolete; /* deleted bytecount */ }; struct nffs_disk_object { int ndo_type; uint8_t ndo_area_idx; uint32_t ndo_offset; union { struct nffs_disk_inode ndo_disk_inode; struct nffs_disk_block ndo_disk_block; } ndo_un_obj; }; #define ndo_disk_inode ndo_un_obj.ndo_disk_inode #define ndo_disk_block ndo_un_obj.ndo_disk_block struct nffs_seek_info { struct nffs_block nsi_last_block; uint32_t nsi_block_file_off; uint32_t nsi_file_len; }; #define NFFS_OBJECT_TYPE_INODE 1 #define NFFS_OBJECT_TYPE_BLOCK 2 #define NFFS_PATH_TOKEN_NONE 0 #define NFFS_PATH_TOKEN_BRANCH 1 #define NFFS_PATH_TOKEN_LEAF 2 struct nffs_path_parser { int npp_token_type; const char *npp_path; const char *npp_token; int npp_token_len; int npp_off; }; /** Represents a single cached data block. */ struct nffs_cache_block { TAILQ_ENTRY(nffs_cache_block) ncb_link; /* Next / prev cached block. */ struct nffs_block ncb_block; /* Full data block. */ uint32_t ncb_file_offset; /* File offset of this block. */ }; TAILQ_HEAD(nffs_cache_block_list, nffs_cache_block); /** Represents a single cached file inode. */ struct nffs_cache_inode { TAILQ_ENTRY(nffs_cache_inode) nci_link; /* Sorted; LRU at tail. */ struct nffs_inode nci_inode; /* Full inode. */ struct nffs_cache_block_list nci_block_list; /* List of cached blocks. */ uint32_t nci_file_size; /* Total file size. */ }; struct nffs_dirent { OS_MULTIFS_CONTAINER; struct nffs_inode_entry *nde_inode_entry; }; struct nffs_dir { OS_MULTIFS_CONTAINER; struct nffs_inode_entry *nd_parent_inode_entry; struct nffs_dirent nd_dirent; }; #ifdef STATS_SECT_START /* Mynewt team can enable statistic once they * start building this codebase. */ STATS_SECT_START(nffs_stats) STATS_SECT_ENTRY(nffs_hashcnt_ins) STATS_SECT_ENTRY(nffs_hashcnt_rm) STATS_SECT_ENTRY(nffs_object_count) STATS_SECT_ENTRY(nffs_iocnt_read) STATS_SECT_ENTRY(nffs_iocnt_write) STATS_SECT_ENTRY(nffs_gccnt) STATS_SECT_ENTRY(nffs_readcnt_data) STATS_SECT_ENTRY(nffs_readcnt_block) STATS_SECT_ENTRY(nffs_readcnt_crc) STATS_SECT_ENTRY(nffs_readcnt_copy) STATS_SECT_ENTRY(nffs_readcnt_format) STATS_SECT_ENTRY(nffs_readcnt_gccollate) STATS_SECT_ENTRY(nffs_readcnt_inode) STATS_SECT_ENTRY(nffs_readcnt_inodeent) STATS_SECT_ENTRY(nffs_readcnt_rename) STATS_SECT_ENTRY(nffs_readcnt_update) STATS_SECT_ENTRY(nffs_readcnt_filename) STATS_SECT_ENTRY(nffs_readcnt_object) STATS_SECT_ENTRY(nffs_readcnt_detect) STATS_SECT_END; extern STATS_SECT_DECL(nffs_stats) nffs_stats; #else #define STATS_INC(sectvarname, var) #endif extern void *nffs_file_mem; extern void *nffs_block_entry_mem; extern void *nffs_inode_mem; extern void *nffs_cache_inode_mem; extern void *nffs_cache_block_mem; extern void *nffs_dir_mem; extern uint32_t nffs_hash_next_file_id; extern uint32_t nffs_hash_next_dir_id; extern uint32_t nffs_hash_next_block_id; #if NFFS_CONFIG_USE_HEAP struct nffs_area *nffs_areas; #else extern struct nffs_area nffs_areas[NFFS_CONFIG_MAX_AREAS]; #endif extern uint8_t nffs_num_areas; extern uint8_t nffs_scratch_area_idx; extern uint16_t nffs_block_max_data_sz; extern unsigned int nffs_gc_count; extern struct nffs_area_desc *nffs_current_area_descs; #define NFFS_FLASH_BUF_SZ 256 extern uint8_t nffs_flash_buf[NFFS_FLASH_BUF_SZ]; extern struct nffs_hash_list *nffs_hash; extern struct nffs_inode_entry *nffs_root_dir; extern struct nffs_inode_entry *nffs_lost_found_dir; extern struct log nffs_log; /* @area */ int nffs_area_magic_is_set(const struct nffs_disk_area *disk_area); int nffs_area_is_scratch(const struct nffs_disk_area *disk_area); int nffs_area_is_current_version(const struct nffs_disk_area *disk_area); void nffs_area_to_disk(const struct nffs_area *area, struct nffs_disk_area *out_disk_area); uint32_t nffs_area_free_space(const struct nffs_area *area); int nffs_area_find_corrupt_scratch(uint16_t *out_good_idx, uint16_t *out_bad_idx); /* @block */ struct nffs_hash_entry *nffs_block_entry_alloc(void); void nffs_block_entry_free(struct nffs_hash_entry *entry); int nffs_block_entry_reserve(struct nffs_hash_entry **out_block_entry); int nffs_block_read_disk(uint8_t area_idx, uint32_t area_offset, struct nffs_disk_block *out_disk_block); int nffs_block_write_disk(const struct nffs_disk_block *disk_block, const void *data, uint8_t *out_area_idx, uint32_t *out_area_offset); int nffs_block_delete_from_ram(struct nffs_hash_entry *entry); void nffs_block_delete_list_from_ram(struct nffs_block *first, struct nffs_block *last); void nffs_block_delete_list_from_disk(const struct nffs_block *first, const struct nffs_block *last); void nffs_block_to_disk(const struct nffs_block *block, struct nffs_disk_block *out_disk_block); int nffs_block_find_predecessor(struct nffs_hash_entry *start, uint32_t sought_id); int nffs_block_from_hash_entry_no_ptrs(struct nffs_block *out_block, struct nffs_hash_entry *entry); int nffs_block_from_hash_entry(struct nffs_block *out_block, struct nffs_hash_entry *entry); int nffs_block_read_data(const struct nffs_block *block, uint16_t offset, uint16_t length, void *dst); int nffs_block_is_dummy(struct nffs_hash_entry *entry); /* @cache */ void nffs_cache_inode_delete(const struct nffs_inode_entry *inode_entry); int nffs_cache_inode_ensure(struct nffs_cache_inode **out_entry, struct nffs_inode_entry *inode_entry); int nffs_cache_inode_refresh(void); void nffs_cache_inode_range(const struct nffs_cache_inode *cache_inode, uint32_t *out_start, uint32_t *out_end); int nffs_cache_seek(struct nffs_cache_inode *cache_inode, uint32_t to, struct nffs_cache_block **out_cache_block); void nffs_cache_clear(void); /* @crc */ int nffs_crc_flash(uint16_t initial_crc, uint8_t area_idx, uint32_t area_offset, uint32_t len, uint16_t *out_crc); uint16_t nffs_crc_disk_block_hdr(const struct nffs_disk_block *disk_block); int nffs_crc_disk_block_validate(const struct nffs_disk_block *disk_block, uint8_t area_idx, uint32_t area_offset); void nffs_crc_disk_block_fill(struct nffs_disk_block *disk_block, const void *data); int nffs_crc_disk_inode_validate(const struct nffs_disk_inode *disk_inode, uint8_t area_idx, uint32_t area_offset); void nffs_crc_disk_inode_fill(struct nffs_disk_inode *disk_inode, const char *filename); /* @config */ void nffs_config_init(void); /* @dir */ int nffs_dir_open(const char *path, struct nffs_dir **out_dir); int nffs_dir_read(struct nffs_dir *dir, struct nffs_dirent **out_dirent); int nffs_dir_close(struct nffs_dir *dir); /* @file */ int nffs_file_open(struct nffs_file **out_file, const char *filename, uint8_t access_flags); int nffs_file_seek(struct nffs_file *file, uint32_t offset); int nffs_file_read(struct nffs_file *file, uint32_t len, void *out_data, uint32_t *out_len); int nffs_file_close(struct nffs_file *file); int nffs_file_new(struct nffs_inode_entry *parent, const char *filename, uint8_t filename_len, int is_dir, struct nffs_inode_entry **out_inode_entry); /* @format */ int nffs_format_area(uint8_t area_idx, int is_scratch); int nffs_format_from_scratch_area(uint8_t area_idx, uint8_t area_id); int nffs_format_full(const struct nffs_area_desc *area_descs); /* @gc */ int nffs_gc(uint8_t *out_area_idx); int nffs_gc_until(uint32_t space, uint8_t *out_area_idx); /* @flash */ struct nffs_area *nffs_flash_find_area(uint16_t logical_id); int nffs_flash_read(uint8_t area_idx, uint32_t offset, void *data, uint32_t len); int nffs_flash_write(uint8_t area_idx, uint32_t offset, const void *data, uint32_t len); int nffs_flash_copy(uint8_t area_id_from, uint32_t offset_from, uint8_t area_id_to, uint32_t offset_to, uint32_t len); uint32_t nffs_flash_loc(uint8_t area_idx, uint32_t offset); void nffs_flash_loc_expand(uint32_t flash_loc, uint8_t *out_area_idx, uint32_t *out_area_offset); /* @hash */ int nffs_hash_id_is_dir(uint32_t id); int nffs_hash_id_is_file(uint32_t id); int nffs_hash_id_is_inode(uint32_t id); int nffs_hash_id_is_block(uint32_t id); struct nffs_hash_entry *nffs_hash_find(uint32_t id); struct nffs_inode_entry *nffs_hash_find_inode(uint32_t id); struct nffs_hash_entry *nffs_hash_find_block(uint32_t id); void nffs_hash_insert(struct nffs_hash_entry *entry); void nffs_hash_remove(struct nffs_hash_entry *entry); int nffs_hash_init(void); int nffs_hash_entry_is_dummy(struct nffs_hash_entry *he); int nffs_hash_id_is_dummy(uint32_t id); /* @inode */ struct nffs_inode_entry *nffs_inode_entry_alloc(void); void nffs_inode_entry_free(struct nffs_inode_entry *inode_entry); int nffs_inode_entry_reserve(struct nffs_inode_entry **out_inode_entry); int nffs_inode_calc_data_length(struct nffs_inode_entry *inode_entry, uint32_t *out_len); int nffs_inode_data_len(struct nffs_inode_entry *inode_entry, uint32_t *out_len); uint32_t nffs_inode_parent_id(const struct nffs_inode *inode); int nffs_inode_delete_from_disk(struct nffs_inode *inode); int nffs_inode_entry_from_disk(struct nffs_inode_entry *out_inode, const struct nffs_disk_inode *disk_inode, uint8_t area_idx, uint32_t offset); int nffs_inode_rename(struct nffs_inode_entry *inode_entry, struct nffs_inode_entry *new_parent, const char *new_filename); int nffs_inode_update(struct nffs_inode_entry *inode_entry); void nffs_inode_insert_block(struct nffs_inode *inode, struct nffs_block *block); int nffs_inode_read_disk(uint8_t area_idx, uint32_t offset, struct nffs_disk_inode *out_disk_inode); int nffs_inode_write_disk(const struct nffs_disk_inode *disk_inode, const char *filename, uint8_t area_idx, uint32_t offset); int nffs_inode_inc_refcnt(struct nffs_inode_entry *inode_entry); int nffs_inode_dec_refcnt(struct nffs_inode_entry *inode_entry); int nffs_inode_add_child(struct nffs_inode_entry *parent, struct nffs_inode_entry *child); void nffs_inode_remove_child(struct nffs_inode *child); int nffs_inode_is_root(const struct nffs_disk_inode *disk_inode); int nffs_inode_read_filename(struct nffs_inode_entry *inode_entry, size_t max_len, char *out_name, uint8_t *out_full_len); int nffs_inode_filename_cmp_ram(const struct nffs_inode *inode, const char *name, int name_len, int *result); int nffs_inode_filename_cmp_flash(const struct nffs_inode *inode1, const struct nffs_inode *inode2, int *result); int nffs_inode_read(struct nffs_inode_entry *inode_entry, uint32_t offset, uint32_t len, void *data, uint32_t *out_len); int nffs_inode_seek(struct nffs_inode_entry *inode_entry, uint32_t offset, uint32_t length, struct nffs_seek_info *out_seek_info); int nffs_inode_from_entry(struct nffs_inode *out_inode, struct nffs_inode_entry *entry); int nffs_inode_unlink_from_ram(struct nffs_inode *inode, struct nffs_hash_entry **out_next); int nffs_inode_unlink_from_ram_corrupt_ok(struct nffs_inode *inode, struct nffs_hash_entry **out_next); int nffs_inode_unlink(struct nffs_inode *inode); int nffs_inode_is_dummy(struct nffs_inode_entry *inode_entry); int nffs_inode_is_deleted(struct nffs_inode_entry *inode_entry); int nffs_inode_setflags(struct nffs_inode_entry *entry, uint8_t flag); int nffs_inode_unsetflags(struct nffs_inode_entry *entry, uint8_t flag); int nffs_inode_getflags(struct nffs_inode_entry *entry, uint8_t flag); /* @misc */ int nffs_misc_gc_if_oom(void *resource, int *out_rc); int nffs_misc_reserve_space(uint16_t space, uint8_t *out_area_idx, uint32_t *out_area_offset); int nffs_misc_set_num_areas(uint8_t num_areas); int nffs_misc_validate_root_dir(void); int nffs_misc_validate_scratch(void); int nffs_misc_create_lost_found_dir(void); int nffs_misc_set_max_block_data_len(uint16_t min_data_len); int nffs_misc_reset(void); int nffs_misc_ready(void); int nffs_misc_desc_from_flash_area(const struct nffs_flash_desc *flash, int *cnt, struct nffs_area_desc *nad); /* @path */ int nffs_path_parse_next(struct nffs_path_parser *parser); void nffs_path_parser_new(struct nffs_path_parser *parser, const char *path); int nffs_path_find(struct nffs_path_parser *parser, struct nffs_inode_entry **out_inode_entry, struct nffs_inode_entry **out_parent); int nffs_path_find_inode_entry(const char *filename, struct nffs_inode_entry **out_inode_entry); int nffs_path_unlink(const char *filename); int nffs_path_rename(const char *from, const char *to); int nffs_path_new_dir(const char *path, struct nffs_inode_entry **out_inode_entry); /* @restore */ int nffs_restore_full(const struct nffs_area_desc *area_descs); /* @write */ int nffs_write_to_file(struct nffs_file *file, const void *data, int len); #define NFFS_HASH_FOREACH(entry, i, next) \ for ((i) = 0; (i) < NFFS_HASH_SIZE; (i)++) \ for ((entry) = SLIST_FIRST(nffs_hash + (i)); \ (entry) && (((next)) = SLIST_NEXT((entry), nhe_next), 1); \ (entry) = ((next))) #define NFFS_FLASH_LOC_NONE nffs_flash_loc(NFFS_AREA_ID_NONE, 0) #if __ZEPHYR__ #define NFFS_LOG(lvl, ...) #define MYNEWT_VAL(val) 0 #define ASSERT_IF_TEST(cond) #elif MYNEWT /* Default to Mynewt */ #include "log/log.h" #include "testutil/testutil.h" #define NFFS_LOG(lvl, ...) \ LOG_ ## lvl(&nffs_log, LOG_MODULE_NFFS, __VA_ARGS__) #endif #ifdef __cplusplus } #endif #endif