| /* |
| * 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. |
| */ |
| |
| #include <errno.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <assert.h> |
| |
| #include "os/mynewt.h" |
| #include "bsp/bsp.h" |
| #include "hal/hal_flash.h" |
| #include "flash_map/flash_map.h" |
| #include "stats/stats.h" |
| #include "nffs_priv.h" |
| #include "nffs/nffs.h" |
| #include "fs/fs_if.h" |
| #include "disk/disk.h" |
| |
| struct nffs_area *nffs_areas; |
| uint8_t nffs_num_areas; |
| uint8_t nffs_scratch_area_idx; |
| uint16_t nffs_block_max_data_sz; |
| struct nffs_area_desc *nffs_current_area_descs; |
| |
| struct os_mempool nffs_file_pool; |
| struct os_mempool nffs_dir_pool; |
| struct os_mempool nffs_inode_entry_pool; |
| struct os_mempool nffs_block_entry_pool; |
| struct os_mempool nffs_cache_inode_pool; |
| struct os_mempool nffs_cache_block_pool; |
| |
| void *nffs_file_mem; |
| void *nffs_inode_mem; |
| void *nffs_block_entry_mem; |
| void *nffs_cache_inode_mem; |
| void *nffs_cache_block_mem; |
| void *nffs_dir_mem; |
| |
| struct nffs_inode_entry *nffs_root_dir; |
| struct nffs_inode_entry *nffs_lost_found_dir; |
| |
| static struct os_mutex nffs_mutex; |
| |
| static int nffs_open(const char *path, uint8_t access_flags, |
| struct fs_file **out_file); |
| static int nffs_close(struct fs_file *fs_file); |
| static int nffs_read(struct fs_file *fs_file, uint32_t len, void *out_data, |
| uint32_t *out_len); |
| static int nffs_write(struct fs_file *fs_file, const void *data, int len); |
| static int nffs_seek(struct fs_file *fs_file, uint32_t offset); |
| static uint32_t nffs_getpos(const struct fs_file *fs_file); |
| static int nffs_file_len(const struct fs_file *fs_file, uint32_t *out_len); |
| static int nffs_unlink(const char *path); |
| static int nffs_rename(const char *from, const char *to); |
| static int nffs_mkdir(const char *path); |
| static int nffs_opendir(const char *path, struct fs_dir **out_fs_dir); |
| static int nffs_readdir(struct fs_dir *dir, struct fs_dirent **out_dirent); |
| static int nffs_closedir(struct fs_dir *dir); |
| static int nffs_dirent_name(const struct fs_dirent *fs_dirent, size_t max_len, |
| char *out_name, uint8_t *out_name_len); |
| static int nffs_dirent_is_dir(const struct fs_dirent *fs_dirent); |
| |
| struct fs_ops nffs_ops = { |
| .f_open = nffs_open, |
| .f_close = nffs_close, |
| .f_read = nffs_read, |
| .f_write = nffs_write, |
| |
| .f_seek = nffs_seek, |
| .f_getpos = nffs_getpos, |
| .f_filelen = nffs_file_len, |
| |
| .f_unlink = nffs_unlink, |
| .f_rename = nffs_rename, |
| .f_mkdir = nffs_mkdir, |
| |
| .f_opendir = nffs_opendir, |
| .f_readdir = nffs_readdir, |
| .f_closedir = nffs_closedir, |
| |
| .f_dirent_name = nffs_dirent_name, |
| .f_dirent_is_dir = nffs_dirent_is_dir, |
| |
| .f_name = "nffs" |
| }; |
| |
| STATS_SECT_DECL(nffs_stats) nffs_stats; |
| STATS_NAME_START(nffs_stats) |
| STATS_NAME(nffs_stats, nffs_hashcnt_ins) |
| STATS_NAME(nffs_stats, nffs_hashcnt_rm) |
| STATS_NAME(nffs_stats, nffs_object_count) |
| STATS_NAME(nffs_stats, nffs_iocnt_read) |
| STATS_NAME(nffs_stats, nffs_iocnt_write) |
| STATS_NAME(nffs_stats, nffs_gccnt) |
| STATS_NAME(nffs_stats, nffs_readcnt_data) |
| STATS_NAME(nffs_stats, nffs_readcnt_block) |
| STATS_NAME(nffs_stats, nffs_readcnt_crc) |
| STATS_NAME(nffs_stats, nffs_readcnt_copy) |
| STATS_NAME(nffs_stats, nffs_readcnt_format) |
| STATS_NAME(nffs_stats, nffs_readcnt_gccollate) |
| STATS_NAME(nffs_stats, nffs_readcnt_inode) |
| STATS_NAME(nffs_stats, nffs_readcnt_inodeent) |
| STATS_NAME(nffs_stats, nffs_readcnt_rename) |
| STATS_NAME(nffs_stats, nffs_readcnt_update) |
| STATS_NAME(nffs_stats, nffs_readcnt_filename) |
| STATS_NAME(nffs_stats, nffs_readcnt_object) |
| STATS_NAME(nffs_stats, nffs_readcnt_detect) |
| STATS_NAME_END(nffs_stats) |
| |
| static void |
| nffs_lock(void) |
| { |
| int rc; |
| |
| rc = os_mutex_pend(&nffs_mutex, 0xffffffff); |
| assert(rc == 0 || rc == OS_NOT_STARTED); |
| } |
| |
| static void |
| nffs_unlock(void) |
| { |
| int rc; |
| |
| rc = os_mutex_release(&nffs_mutex); |
| assert(rc == 0 || rc == OS_NOT_STARTED); |
| } |
| |
| static int |
| nffs_stats_init(void) |
| { |
| int rc; |
| |
| rc = stats_init_and_reg( |
| STATS_HDR(nffs_stats), |
| STATS_SIZE_INIT_PARMS(nffs_stats, STATS_SIZE_32), |
| STATS_NAME_INIT_PARMS(nffs_stats), |
| "nffs_stats"); |
| if (rc) { |
| if (rc < 0) { |
| /* multiple initializations are okay */ |
| rc = 0; |
| } else { |
| rc = FS_EOS; |
| } |
| } |
| return rc; |
| } |
| |
| /** |
| * Opens a file at the specified path. The result of opening a nonexistent |
| * file depends on the access flags specified. All intermediate directories |
| * must already exist. |
| * |
| * The mode strings passed to fopen() map to nffs_open()'s access flags as |
| * follows: |
| * "r" - FS_ACCESS_READ |
| * "r+" - FS_ACCESS_READ | FS_ACCESS_WRITE |
| * "w" - FS_ACCESS_WRITE | FS_ACCESS_TRUNCATE |
| * "w+" - FS_ACCESS_READ | FS_ACCESS_WRITE | FS_ACCESS_TRUNCATE |
| * "a" - FS_ACCESS_WRITE | FS_ACCESS_APPEND |
| * "a+" - FS_ACCESS_READ | FS_ACCESS_WRITE | FS_ACCESS_APPEND |
| * |
| * @param path The path of the file to open. |
| * @param access_flags Flags controlling file access; see above table. |
| * @param out_file On success, a pointer to the newly-created file |
| * handle gets written here. |
| * |
| * @return 0 on success; nonzero on failure. |
| */ |
| static int |
| nffs_open(const char *path, uint8_t access_flags, struct fs_file **out_fs_file) |
| { |
| int rc; |
| struct nffs_file *out_file; |
| char *filepath = NULL; |
| |
| nffs_lock(); |
| |
| if (!nffs_misc_ready()) { |
| rc = FS_EUNINIT; |
| goto done; |
| } |
| |
| filepath = disk_filepath_from_path(path); |
| |
| rc = nffs_file_open(&out_file, filepath, access_flags); |
| if (rc != 0) { |
| goto done; |
| } |
| *out_fs_file = (struct fs_file *)out_file; |
| done: |
| if (filepath) { |
| free(filepath); |
| } |
| nffs_unlock(); |
| if (rc != 0) { |
| *out_fs_file = NULL; |
| } |
| return rc; |
| } |
| |
| /** |
| * Closes the specified file and invalidates the file handle. If the file has |
| * already been unlinked, and this is the last open handle to the file, this |
| * operation causes the file to be deleted from disk. |
| * |
| * @param file The file handle to close. |
| * |
| * @return 0 on success; nonzero on failure. |
| */ |
| static int |
| nffs_close(struct fs_file *fs_file) |
| { |
| int rc; |
| struct nffs_file *file = (struct nffs_file *)fs_file; |
| |
| if (file == NULL) { |
| return 0; |
| } |
| |
| nffs_lock(); |
| rc = nffs_file_close(file); |
| nffs_unlock(); |
| |
| return rc; |
| } |
| |
| /** |
| * Positions a file's read and write pointer at the specified offset. The |
| * offset is expressed as the number of bytes from the start of the file (i.e., |
| * seeking to offset 0 places the pointer at the first byte in the file). |
| * |
| * @param file The file to reposition. |
| * @param offset The 0-based file offset to seek to. |
| * |
| * @return 0 on success; nonzero on failure. |
| */ |
| static int |
| nffs_seek(struct fs_file *fs_file, uint32_t offset) |
| { |
| int rc; |
| struct nffs_file *file = (struct nffs_file *)fs_file; |
| |
| nffs_lock(); |
| rc = nffs_file_seek(file, offset); |
| nffs_unlock(); |
| |
| return rc; |
| } |
| |
| /** |
| * Retrieves the current read and write position of the specified open file. |
| * |
| * @param file The file to query. |
| * |
| * @return The file offset, in bytes. |
| */ |
| static uint32_t |
| nffs_getpos(const struct fs_file *fs_file) |
| { |
| uint32_t offset; |
| const struct nffs_file *file = (const struct nffs_file *)fs_file; |
| |
| nffs_lock(); |
| offset = file->nf_offset; |
| nffs_unlock(); |
| |
| return offset; |
| } |
| |
| /** |
| * Retrieves the current length of the specified open file. |
| * |
| * @param file The file to query. |
| * @param out_len On success, the number of bytes in the file gets |
| * written here. |
| * |
| * @return 0 on success; nonzero on failure. |
| */ |
| static int |
| nffs_file_len(const struct fs_file *fs_file, uint32_t *out_len) |
| { |
| int rc; |
| const struct nffs_file *file = (const struct nffs_file *)fs_file; |
| |
| nffs_lock(); |
| rc = nffs_inode_data_len(file->nf_inode_entry, out_len); |
| nffs_unlock(); |
| |
| return rc; |
| } |
| |
| /** |
| * Reads data from the specified file. If more data is requested than remains |
| * in the file, all available data is retrieved and a success code is returned. |
| * |
| * @param file The file to read from. |
| * @param len The number of bytes to attempt to read. |
| * @param out_data The destination buffer to read into. |
| * @param out_len On success, the number of bytes actually read gets |
| * written here. Pass null if you don't care. |
| * |
| * @return 0 on success; nonzero on failure. |
| */ |
| static int |
| nffs_read(struct fs_file *fs_file, uint32_t len, void *out_data, |
| uint32_t *out_len) |
| { |
| int rc; |
| struct nffs_file *file = (struct nffs_file *)fs_file; |
| |
| nffs_lock(); |
| rc = nffs_file_read(file, len, out_data, out_len); |
| nffs_unlock(); |
| |
| return rc; |
| } |
| |
| /** |
| * Writes the supplied data to the current offset of the specified file handle. |
| * |
| * @param file The file to write to. |
| * @param data The data to write. |
| * @param len The number of bytes to write. |
| * |
| * @return 0 on success; nonzero on failure. |
| */ |
| static int |
| nffs_write(struct fs_file *fs_file, const void *data, int len) |
| { |
| int rc; |
| struct nffs_file *file = (struct nffs_file *)fs_file; |
| |
| nffs_lock(); |
| |
| if (!nffs_misc_ready()) { |
| rc = FS_EUNINIT; |
| goto done; |
| } |
| |
| rc = nffs_write_to_file(file, data, len); |
| if (rc != 0) { |
| goto done; |
| } |
| |
| rc = 0; |
| |
| done: |
| nffs_unlock(); |
| return rc; |
| } |
| |
| /** |
| * Unlinks the file or directory at the specified path. If the path refers to |
| * a directory, all the directory's descendants are recursively unlinked. Any |
| * open file handles refering to an unlinked file remain valid, and can be |
| * read from and written to. |
| * |
| * @path The path of the file or directory to unlink. |
| * |
| * @return 0 on success; nonzero on failure. |
| */ |
| static int |
| nffs_unlink(const char *path) |
| { |
| int rc; |
| |
| nffs_lock(); |
| |
| if (!nffs_misc_ready()) { |
| rc = FS_EUNINIT; |
| goto done; |
| } |
| |
| rc = nffs_path_unlink(path); |
| if (rc != 0) { |
| goto done; |
| } |
| |
| rc = 0; |
| |
| done: |
| nffs_unlock(); |
| return rc; |
| } |
| |
| /** |
| * Performs a rename and / or move of the specified source path to the |
| * specified destination. The source path can refer to either a file or a |
| * directory. All intermediate directories in the destination path must |
| * already exist. If the source path refers to a file, the destination path |
| * must contain a full filename path, rather than just the new parent |
| * directory. If an object already exists at the specified destination path, |
| * this function causes it to be unlinked prior to the rename (i.e., the |
| * destination gets clobbered). |
| * |
| * @param from The source path. |
| * @param to The destination path. |
| * |
| * @return 0 on success; |
| * nonzero on failure. |
| */ |
| static int |
| nffs_rename(const char *from, const char *to) |
| { |
| int rc; |
| |
| nffs_lock(); |
| |
| if (!nffs_misc_ready()) { |
| rc = FS_EUNINIT; |
| goto done; |
| } |
| |
| rc = nffs_path_rename(from, to); |
| if (rc != 0) { |
| goto done; |
| } |
| |
| rc = 0; |
| |
| done: |
| nffs_unlock(); |
| return rc; |
| } |
| |
| /** |
| * Creates the directory represented by the specified path. All intermediate |
| * directories must already exist. The specified path must start with a '/' |
| * character. |
| * |
| * @param path The name of the directory to create. |
| * |
| * @return 0 on success; |
| * nonzero on failure. |
| */ |
| static int |
| nffs_mkdir(const char *path) |
| { |
| int rc; |
| |
| nffs_lock(); |
| |
| if (!nffs_misc_ready()) { |
| rc = FS_EUNINIT; |
| goto done; |
| } |
| |
| rc = nffs_path_new_dir(path, NULL); |
| if (rc != 0) { |
| goto done; |
| } |
| |
| done: |
| nffs_unlock(); |
| return rc; |
| } |
| |
| /** |
| * Opens the directory at the specified path. The directory's contents can be |
| * read with subsequent calls to nffs_readdir(). When you are done with the |
| * directory handle, close it with nffs_closedir(). |
| * |
| * Unlinking files from the directory while it is open may result in |
| * unpredictable behavior. New files can be created inside the directory. |
| * |
| * @param path The name of the directory to open. |
| * @param out_dir On success, points to the directory handle. |
| * |
| * @return 0 on success; |
| * FS_ENOENT if the specified directory does not |
| * exist; |
| * other nonzero on error. |
| */ |
| static int |
| nffs_opendir(const char *path, struct fs_dir **out_fs_dir) |
| { |
| int rc; |
| struct nffs_dir **out_dir = (struct nffs_dir **)out_fs_dir; |
| char *filepath = NULL; |
| |
| nffs_lock(); |
| |
| if (!nffs_misc_ready()) { |
| rc = FS_EUNINIT; |
| goto done; |
| } |
| |
| filepath = disk_filepath_from_path(path); |
| |
| rc = nffs_dir_open(filepath, out_dir); |
| |
| done: |
| if (filepath) { |
| free(filepath); |
| } |
| nffs_unlock(); |
| if (rc != 0) { |
| *out_dir = NULL; |
| } |
| return rc; |
| } |
| |
| /** |
| * Reads the next entry in an open directory. |
| * |
| * @param dir The directory handle to read from. |
| * @param out_dirent On success, points to the next child entry in |
| * the specified directory. |
| * |
| * @return 0 on success; |
| * FS_ENOENT if there are no more entries in the |
| * parent directory; |
| * other nonzero on error. |
| */ |
| static int |
| nffs_readdir(struct fs_dir *fs_dir, struct fs_dirent **out_fs_dirent) |
| { |
| int rc; |
| struct nffs_dir *dir = (struct nffs_dir *)fs_dir; |
| struct nffs_dirent **out_dirent = (struct nffs_dirent **)out_fs_dirent; |
| |
| nffs_lock(); |
| rc = nffs_dir_read(dir, out_dirent); |
| nffs_unlock(); |
| |
| return rc; |
| } |
| |
| /** |
| * Closes the specified directory handle. |
| * |
| * @param dir The name of the directory to close. |
| * |
| * @return 0 on success; nonzero on failure. |
| */ |
| static int |
| nffs_closedir(struct fs_dir *fs_dir) |
| { |
| int rc; |
| struct nffs_dir *dir = (struct nffs_dir *)fs_dir; |
| |
| nffs_lock(); |
| rc = nffs_dir_close(dir); |
| nffs_unlock(); |
| |
| return rc; |
| } |
| |
| /** |
| * Retrieves the filename of the specified directory entry. The retrieved |
| * filename is always null-terminated. To ensure enough space to hold the full |
| * filename plus a null-termintor, a destination buffer of size |
| * (NFFS_FILENAME_MAX_LEN + 1) should be used. |
| * |
| * @param dirent The directory entry to query. |
| * @param max_len The size of the "out_name" character buffer. |
| * @param out_name On success, the entry's filename is written |
| * here; always null-terminated. |
| * @param out_name_len On success, contains the actual length of the |
| * filename, NOT including the |
| * null-terminator. |
| * |
| * @return 0 on success; nonzero on failure. |
| */ |
| static int |
| nffs_dirent_name(const struct fs_dirent *fs_dirent, size_t max_len, |
| char *out_name, uint8_t *out_name_len) |
| { |
| int rc; |
| struct nffs_dirent *dirent = (struct nffs_dirent *)fs_dirent; |
| |
| nffs_lock(); |
| |
| assert(dirent != NULL && dirent->nde_inode_entry != NULL); |
| rc = nffs_inode_read_filename(dirent->nde_inode_entry, max_len, out_name, |
| out_name_len); |
| |
| nffs_unlock(); |
| |
| return rc; |
| } |
| |
| /** |
| * Tells you whether the specified directory entry is a sub-directory or a |
| * regular file. |
| * |
| * @param dirent The directory entry to query. |
| * |
| * @return 1: The entry is a directory; |
| * 0: The entry is a regular file. |
| */ |
| static int |
| nffs_dirent_is_dir(const struct fs_dirent *fs_dirent) |
| { |
| uint32_t id; |
| const struct nffs_dirent *dirent = (const struct nffs_dirent *)fs_dirent; |
| |
| nffs_lock(); |
| |
| assert(dirent != NULL && dirent->nde_inode_entry != NULL); |
| id = dirent->nde_inode_entry->nie_hash_entry.nhe_id; |
| |
| nffs_unlock(); |
| |
| return nffs_hash_id_is_dir(id); |
| } |
| |
| /** |
| * Erases all the specified areas and initializes them with a clean nffs |
| * file system. |
| * |
| * @param area_descs The set of areas to format. |
| * |
| * @return 0 on success; |
| * nonzero on failure. |
| */ |
| int |
| nffs_format(const struct nffs_area_desc *area_descs) |
| { |
| int rc; |
| |
| nffs_lock(); |
| rc = nffs_format_full(area_descs); |
| nffs_unlock(); |
| |
| return rc; |
| } |
| |
| /** |
| * Searches for a valid nffs file system among the specified areas. This |
| * function succeeds if a file system is detected among any subset of the |
| * supplied areas. If the area set does not contain a valid file system, |
| * a new one can be created via a separate call to nffs_format(). |
| * |
| * @param area_descs The area set to search. This array must be |
| * terminated with a 0-length area. |
| * |
| * @return 0 on success; |
| * FS_ECORRUPT if no valid file system was detected; |
| * other nonzero on error. |
| */ |
| int |
| nffs_detect(const struct nffs_area_desc *area_descs) |
| { |
| int rc; |
| |
| nffs_lock(); |
| rc = nffs_restore_full(area_descs); |
| nffs_unlock(); |
| |
| return rc; |
| } |
| |
| /** |
| * Initializes internal nffs memory and data structures. This must be called |
| * before any nffs operations are attempted. |
| * |
| * @return 0 on success; nonzero on error. |
| */ |
| int |
| nffs_init(void) |
| { |
| int rc; |
| |
| nffs_config_init(); |
| |
| nffs_cache_clear(); |
| |
| rc = nffs_stats_init(); |
| if (rc != 0) { |
| return FS_EOS; |
| } |
| |
| rc = os_mutex_init(&nffs_mutex); |
| if (rc != 0) { |
| return FS_EOS; |
| } |
| |
| free(nffs_file_mem); |
| nffs_file_mem = malloc( |
| OS_MEMPOOL_BYTES(nffs_config.nc_num_files, sizeof (struct nffs_file))); |
| if (nffs_file_mem == NULL) { |
| return FS_ENOMEM; |
| } |
| |
| free(nffs_inode_mem); |
| nffs_inode_mem = malloc( |
| OS_MEMPOOL_BYTES(nffs_config.nc_num_inodes, |
| sizeof (struct nffs_inode_entry))); |
| if (nffs_inode_mem == NULL) { |
| return FS_ENOMEM; |
| } |
| |
| free(nffs_block_entry_mem); |
| nffs_block_entry_mem = malloc( |
| OS_MEMPOOL_BYTES(nffs_config.nc_num_blocks, |
| sizeof (struct nffs_hash_entry))); |
| if (nffs_block_entry_mem == NULL) { |
| return FS_ENOMEM; |
| } |
| |
| free(nffs_cache_inode_mem); |
| nffs_cache_inode_mem = malloc( |
| OS_MEMPOOL_BYTES(nffs_config.nc_num_cache_inodes, |
| sizeof (struct nffs_cache_inode))); |
| if (nffs_cache_inode_mem == NULL) { |
| return FS_ENOMEM; |
| } |
| |
| free(nffs_cache_block_mem); |
| nffs_cache_block_mem = malloc( |
| OS_MEMPOOL_BYTES(nffs_config.nc_num_cache_blocks, |
| sizeof (struct nffs_cache_block))); |
| if (nffs_cache_block_mem == NULL) { |
| return FS_ENOMEM; |
| } |
| |
| free(nffs_dir_mem); |
| nffs_dir_mem = malloc( |
| OS_MEMPOOL_BYTES(nffs_config.nc_num_dirs, |
| sizeof (struct nffs_dir))); |
| if (nffs_dir_mem == NULL) { |
| return FS_ENOMEM; |
| } |
| |
| rc = nffs_misc_reset(); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| fs_register(&nffs_ops); |
| return 0; |
| } |
| |
| void |
| nffs_pkg_init(void) |
| { |
| struct nffs_area_desc descs[MYNEWT_VAL(NFFS_NUM_AREAS) + 1]; |
| int cnt; |
| int rc; |
| |
| /* Ensure this function only gets called by sysinit. */ |
| SYSINIT_ASSERT_ACTIVE(); |
| |
| /* Initialize nffs's internal state. */ |
| rc = nffs_init(); |
| SYSINIT_PANIC_ASSERT(rc == 0); |
| |
| /* Convert the set of flash blocks we intend to use for nffs into an array |
| * of nffs area descriptors. |
| */ |
| cnt = MYNEWT_VAL(NFFS_NUM_AREAS); |
| rc = nffs_misc_desc_from_flash_area( |
| MYNEWT_VAL(NFFS_FLASH_AREA), &cnt, descs); |
| SYSINIT_PANIC_ASSERT(rc == 0); |
| |
| /* Attempt to restore an existing nffs file system from flash. */ |
| rc = nffs_detect(descs); |
| switch (rc) { |
| case 0: |
| break; |
| |
| case FS_ECORRUPT: |
| /* No valid nffs instance detected; act based on configued detection |
| * failure policy. |
| */ |
| switch (MYNEWT_VAL(NFFS_DETECT_FAIL)) { |
| case NFFS_DETECT_FAIL_IGNORE: |
| break; |
| |
| case NFFS_DETECT_FAIL_FORMAT: |
| rc = nffs_format(descs); |
| SYSINIT_PANIC_ASSERT(rc == 0); |
| break; |
| |
| default: |
| SYSINIT_PANIC(); |
| break; |
| } |
| break; |
| |
| default: |
| SYSINIT_PANIC(); |
| break; |
| } |
| } |