/**************************************************************************** * fs/spiffs/src/spiffs_volume.c * SPIFFS Utility Functions for Volume and File Object Support * * Copyright (C) 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * This is a port of version 0.3.7 of SPIFFS by Peter Andersion. That * version was originally released under the MIT license but is here re- * released under the NuttX BSD license. * * Copyright (c) 2013-2017 Peter Andersson (pelleplutt1976@gmail.com) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include "spiffs.h" #include "spiffs_core.h" #include "spiffs_cache.h" /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: spiffs_stat_pgndx * * Description: * Checks if there are any cached writes for the object ID associated with * given file object. If so, these writes are flushed. * * Input Parameters: * fobj - A reference to the file object to flush * * Returned Value: * On success, then number of bytes flushed is returned. A negated errno * value is returned on any failure. * ****************************************************************************/ int spiffs_stat_pgndx(FAR struct spiffs_s *fs, int16_t pgndx, int16_t objid, FAR struct stat *buf) { struct spiffs_pgobj_ndxheader_s objhdr; uint32_t obj_id_addr; int16_t ndx; mode_t mode; int ret; ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_READ, objid, SPIFFS_PAGE_TO_PADDR(fs, pgndx), sizeof(struct spiffs_pgobj_ndxheader_s), (FAR uint8_t *) & objhdr); if (ret < 0) { return ret; } obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pgndx)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pgndx) * sizeof(int16_t); ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, objid, obj_id_addr, sizeof(int16_t), (FAR uint8_t *)&ndx); if (ret < 0) { return ret; } /* Build the struct stat */ mode = S_IRWXO | S_IRWXG | S_IRWXU; /* Assume all permissions */ mode |= S_IFREG; /* Assume regular file */ /* REVISIT: Should the file object type derive from objhdr.type? */ memset(buf, 0, sizeof(struct stat)); buf->st_mode = mode; buf->st_size = objhdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objhdr.size; buf->st_blksize = fs->geo.blocksize; buf->st_blocks = fs->media_size / fs->geo.blocksize; return ret; } /**************************************************************************** * Name: spiffs_find_fobj_bypgndx * * Description: * Given the page index of the object header, find the corresponding file * object instance. * * Input Parameters: * fs - A reference to the SPIFFS volume object instance * pgndx - The page index to match * ppfobj - A user provided location in which to return the matching file * file object instance * * Returned Value: * Zero (OK) is returned on success; A negated errno value is returned on * any failure. * ****************************************************************************/ int spiffs_find_fobj_bypgndx(FAR struct spiffs_s *fs, int16_t pgndx, FAR struct spiffs_file_s **ppfobj) { FAR struct spiffs_file_s *fobj; int ret = -ENOENT; for (fobj = (FAR struct spiffs_file_s *)dq_peek(&fs->objq); fobj != NULL; fobj = (FAR struct spiffs_file_s *)dq_next((FAR dq_entry_t *)fobj)) { if (fobj->objhdr_pgndx == pgndx) { ret = OK; break; } } if (ppfobj != NULL) { *ppfobj = fobj; } return ret; } /**************************************************************************** * Name: spiffs_find_fobj_byobjid * * Description: * Given a object ID, find the corresponding file object instance * * Input Parameters: * fs - A reference to the SPIFFS volume object instance * objid - The object ID to match * ppfobj - A user provided location in which to return the matching file * file object instance * * Returned Value: * Zero (OK) is returned on success; A negated errno value is returned on * any failure. * ****************************************************************************/ int spiffs_find_fobj_byobjid(FAR struct spiffs_s *fs, int16_t objid, FAR struct spiffs_file_s **ppfobj) { FAR struct spiffs_file_s *fobj; int ret = -ENOENT; for (fobj = (FAR struct spiffs_file_s *)dq_peek(&fs->objq); fobj != NULL; fobj = (FAR struct spiffs_file_s *)dq_next((FAR dq_entry_t *)fobj)) { if (fobj->objid == objid) { ret = OK; break; } } if (ppfobj != NULL) { *ppfobj = fobj; } return ret; } /**************************************************************************** * Name: spiffs_fobj_flush * * Description: * Checks if there are any cached writes for the object ID associated with * given file object. If so, these writes are flushed. * * Input Parameters: * fs - A reference to the SPIFFS volume object instance * fobj - A reference to the file object to flush * * Returned Value: * On success, then number of bytes flushed is returned. A negated errno * value is returned on any failure. * ****************************************************************************/ ssize_t spiffs_fobj_flush(FAR struct spiffs_s *fs, FAR struct spiffs_file_s *fobj) { ssize_t nwritten = 0; /* If we were asked to use direct hardware accessed, then don't bother * flushing the cache. */ if ((fobj->oflags & O_DIRECT) == 0) { if (fobj->cache_page == 0) { /* See if object ID is associated with cache already */ fobj->cache_page = spiffs_cache_page_get_byobjid(fs, fobj); } if (fobj->cache_page) { spiffs_cacheinfo("Flushing cache page %d for objid=%d " "offset=%" PRIu32 " size=%d\n", fobj->cache_page->cpndx, fobj->objid, fobj->cache_page->offset, fobj->cache_page->size); nwritten = spiffs_fobj_write(fs, fobj, spiffs_get_cache_page(fs, spiffs_get_cache(fs), fobj->cache_page->cpndx), fobj->cache_page->offset, fobj->cache_page->size); if (nwritten < 0) { ferr("ERROR: spiffs_fobj_write failed %d\n", (int)nwritten); } spiffs_cache_page_release(fs, fobj->cache_page); } } return nwritten; } /**************************************************************************** * Name: spiffs_fobj_write * * Description: * Write to a file object * * Input Parameters: * fs - A reference to the volume structure * fobj - A reference to the file object to write to * buffer - The data to be written * offset - The FLASH offset to be written * len - The number of bytes to be written * * Returned Value: * On success, then number of bytes written is returned. A negated errno * value is returned on any failure. * ****************************************************************************/ ssize_t spiffs_fobj_write(FAR struct spiffs_s *fs, FAR struct spiffs_file_s *fobj, FAR const void *buffer, off_t offset, size_t len) { ssize_t remaining = len; ssize_t total = 0; if (fobj->size != SPIFFS_UNDEFINED_LEN) { while (offset < fobj->size) { ssize_t nwritten; ssize_t wrsize; wrsize = MIN((ssize_t)(fobj->size - offset), remaining); nwritten = spiffs_fobj_modify(fs, fobj, offset, (FAR uint8_t *)buffer, wrsize); if (nwritten <= 0) { return nwritten; } remaining -= nwritten; buffer += nwritten; offset += nwritten; total += nwritten; } } while (remaining > 0) { ssize_t nappend; nappend = spiffs_fobj_append(fs, fobj, offset, (FAR uint8_t *)buffer, remaining); if (nappend < 0) { if (nappend == -ENOSPC && total > 0) { return total; } return nappend; } remaining -= nappend; buffer += nappend; offset += nappend; total += nappend; } return (ssize_t)total; } /**************************************************************************** * Name: spiffs_fobj_read * * Description: * Read from a file object * * Input Parameters: * fs - A reference to the volume structure * fobj - A reference to the file object to read from * buffer - The location that the data is read to * offset - The FLASH offset to be read * len - The number of bytes to be read * fpos - The file position to read from * * Returned Value: * On success, then number of bytes written is returned. A negated errno * value is returned on any failure. * ****************************************************************************/ ssize_t spiffs_fobj_read(FAR struct spiffs_s *fs, FAR struct spiffs_file_s *fobj, FAR void *buffer, size_t buflen, off_t fpos) { ssize_t nread; ssize_t total; /* Make sure that read access is supported */ if ((fobj->oflags & O_RDONLY) == 0) { return -EACCES; } /* Handle the special case of zero-length files */ if (fobj->size == SPIFFS_UNDEFINED_LEN && buflen > 0) { /* Return zero (meaning EOF) */ return 0; } spiffs_fobj_flush(fs, fobj); /* Check for attempts to read beyond the end of the file */ if (fpos + buflen >= fobj->size) { /* Truncate */ buflen = fobj->size - fpos; } /* Read data from the file object until either we have read all of the * requested data, or until a read error occurs. */ total = 0; while (buflen > 0) { /* Read from the file object */ nread = spiffs_object_read(fs, fobj, fpos, buflen, (FAR uint8_t *)buffer); if (nread < 0) { ferr("ERROR: spiffs_object_read() failed: %d\n", (int)nread); return nread; } total += nread; buffer += nread; fpos += nread; buflen -= nread; } return total; } /**************************************************************************** * Name: spiffs_fobj_free * * Description: * Free all resources used a file object * * Input Parameters: * fs - A reference to the volume structure * fobj - A reference to the file object to be removed * * Returned Value: * None * ****************************************************************************/ void spiffs_fobj_free(FAR struct spiffs_s *fs, FAR struct spiffs_file_s *fobj, bool unlink) { FAR struct spiffs_file_s *curr; int ret; finfo("Removing fobj for objid=%04x\n", fobj->objid); /* Flush any buffered write data */ ret = spiffs_fobj_flush(fs, fobj); if (ret < 0) { ferr("ERROR: spiffs_fobj_flush failed: %d\n", ret); } /* Remove the file object from the list of file objects in the volume * structure. */ for (curr = (FAR struct spiffs_file_s *)dq_peek(&fs->objq); curr != NULL; curr = (FAR struct spiffs_file_s *)dq_next((FAR dq_entry_t *)curr)) { /* Is this the entry we are searching for? */ if (curr == fobj) { /* Yes, remove it from the list of file objects */ dq_rem((FAR dq_entry_t *)curr, &fs->objq); break; } } DEBUGASSERT(curr != NULL); /* Now we can remove the file by truncating it to zero length if it was * unlinked. */ if (unlink) { ret = spiffs_fobj_truncate(fs, fobj, 0, true); if (ret < 0) { ferr("ERROR: spiffs_fobj_truncate failed: %d\n", ret); } } /* Then free the file object itself (which contains the lock we hold) */ kmm_free(fobj); }