/**************************************************************************** * fs/nxffs/nxffs_inode.c * * Copyright (C) 2011, 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * References: Linux/Documentation/filesystems/romfs.txt * * 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 #include "nxffs.h" /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: nxffs_rdentry * * Description: * Read the inode entry at this offset. Called only from nxffs_nextentry(). * * Input Parameters: * volume - Describes the current volume. * offset - The byte offset from the beginning of FLASH where the inode * header is expected. * entry - A memory location to return the expanded inode header * information. * * Returned Value: * Zero on success. Otherwise, a negated errno value is returned * indicating the nature of the failure. * ****************************************************************************/ static int nxffs_rdentry(FAR struct nxffs_volume_s *volume, off_t offset, FAR struct nxffs_entry_s *entry) { struct nxffs_inode_s inode; uint32_t ecrc; uint32_t crc; uint8_t state; int namlen; int ret; DEBUGASSERT(volume && entry); memset(entry, 0, sizeof(struct nxffs_entry_s)); /* Read the header at the FLASH offset */ nxffs_ioseek(volume, offset); memcpy(&inode, &volume->cache[volume->iooffset], SIZEOF_NXFFS_INODE_HDR); /* Check if the file state is recognized. */ state = inode.state; if (state != INODE_STATE_FILE && state != INODE_STATE_DELETED) { /* This can't be a valid inode.. don't bother with the rest */ ret = -ENOENT; goto errout_no_offset; } /* Copy the packed header into the user-friendly buffer */ entry->hoffset = offset; entry->noffset = nxffs_rdle32(inode.noffs); entry->doffset = nxffs_rdle32(inode.doffs); entry->utc = nxffs_rdle32(inode.utc); entry->datlen = nxffs_rdle32(inode.datlen); /* Modify the packed header and perform the (partial) CRC calculation */ ecrc = nxffs_rdle32(inode.crc); inode.state = CONFIG_NXFFS_ERASEDSTATE; memset(inode.crc, 0, 4); crc = crc32((FAR const uint8_t *)&inode, SIZEOF_NXFFS_INODE_HDR); /* Allocate memory to hold the variable-length file name */ namlen = inode.namlen; entry->name = (FAR char *)kmm_malloc(namlen + 1); if (!entry->name) { ferr("ERROR: Failed to allocate name, namlen: %d\n", namlen); ret = -ENOMEM; goto errout_no_offset; } /* Seek to the expected location of the name in FLASH */ nxffs_ioseek(volume, entry->noffset); /* Make sure that the block is in memory (the name may not be in the * same block as the inode header. */ ret = nxffs_rdcache(volume, volume->ioblock); if (ret < 0) { ferr("ERROR: nxffsx_rdcache failed: %d\n", -ret); goto errout_with_name; } /* Read the file name from the expected offset in FLASH */ memcpy(entry->name, &volume->cache[volume->iooffset], namlen); entry->name[namlen] = '\0'; /* Finish the CRC calculation and verify the entry */ crc = crc32part((FAR const uint8_t *)entry->name, namlen, crc); if (crc != ecrc) { ferr("ERROR: CRC entry: %08x CRC calculated: %08x\n", ecrc, crc); ret = -EIO; goto errout_with_name; } /* We have a good inode header.. but it still could a deleted file. * Check the file state. */ if (state != INODE_STATE_FILE) { /* It is a deleted file. But still, the data offset and the * start size are good so we can use this information to advance * further in FLASH memory and reduce the search time. */ offset = nxffs_inodeend(volume, entry); nxffs_freeentry(entry); ret = -ENOENT; goto errout; } /* Everything is good.. leave the offset pointing to the valid inode * header. */ return OK; /* On errors where we are suspicious of the validity of the inode header, * we need to increment the file position to just after the "good" magic * word. */ errout_with_name: nxffs_freeentry(entry); errout_no_offset: offset += NXFFS_MAGICSIZE; errout: nxffs_ioseek(volume, offset); return ret; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: nxffs_freeentry * * Description: * The inode values returned by nxffs_nextentry() include allocated memory * (specifically, the file name string). This function should be called * to dispose of that memory when the inode entry is no longer needed. * * Note that the nxffs_entry_s containing structure is not freed. The * caller may call kmm_free upon return of this function if necessary to * free the entry container. * * Input parameters: * entry - The entry to be freed. * * Returned Value: * None * ****************************************************************************/ void nxffs_freeentry(FAR struct nxffs_entry_s *entry) { if (entry->name) { kmm_free(entry->name); entry->name = NULL; } } /**************************************************************************** * Name: nxffs_nextentry * * Description: * Search for the next valid inode starting at the provided FLASH offset. * * Input Parameters: * volume - Describes the NXFFS volume. * offset - The FLASH memory offset to begin searching. * entry - A pointer to memory provided by the caller in which to return * the inode description. * * Returned Value: * Zero is returned on success. Otherwise, a negated errno is returned * that indicates the nature of the failure. * ****************************************************************************/ int nxffs_nextentry(FAR struct nxffs_volume_s *volume, off_t offset, FAR struct nxffs_entry_s *entry) { int nmagic; int ch; int nerased; int ret; /* Seek to the first FLASH offset provided by the caller. */ nxffs_ioseek(volume, offset); /* Then begin searching */ nerased = 0; nmagic = 0; for (; ; ) { /* Read the next character */ ch = nxffs_getc(volume, SIZEOF_NXFFS_INODE_HDR - nmagic); if (ch < 0) { ferr("ERROR: nxffs_getc failed: %d\n", -ch); return ch; } /* Check for another erased byte */ else if (ch == CONFIG_NXFFS_ERASEDSTATE) { /* If we have encountered NXFFS_NERASED number of consecutive * erased bytes, then presume we have reached the end of valid * data. */ if (++nerased >= NXFFS_NERASED) { finfo("No entry found\n"); return -ENOENT; } } else { nerased = 0; /* Check for the magic sequence indicating the start of an NXFFS * inode. There is the possibility of this magic sequnce occurring * in FLASH data. However, the header CRC should distinguish * between real NXFFS inode headers and such false alarms. */ if (ch != g_inodemagic[nmagic]) { /* Ooops... this is the not the right character for the magic * Sequence. Check if we need to restart or to cancel the sequence: */ if (ch == g_inodemagic[0]) { nmagic = 1; } else { nmagic = 0; } } else if (nmagic < NXFFS_MAGICSIZE - 1) { /* We have one more character in the magic sequence */ nmagic++; } /* We have found the magic sequence in the FLASH data that may * indicate the beginning of an NXFFS inode. */ else { /* The the FLASH offset where we found the matching magic number */ offset = nxffs_iotell(volume) - NXFFS_MAGICSIZE; /* Try to extract the inode header from that position */ ret = nxffs_rdentry(volume, offset, entry); if (ret == OK) { finfo("Found a valid fileheader, offset: %d\n", offset); return OK; } /* False alarm.. keep looking */ nmagic = 0; } } } /* We won't get here, but to keep some compilers happy: */ return -ENOENT; } /**************************************************************************** * Name: nxffs_findinode * * Description: * Search for an inode with the provided name starting with the first * valid inode and proceeding to the end FLASH or until the matching * inode is found. * * Input Parameters: * volume - Describes the NXFFS volume * name - The name of the inode to find * entry - The location to return information about the inode. * * Returned Value: * Zero is returned on success. Otherwise, a negated errno is returned * that indicates the nature of the failure. * ****************************************************************************/ int nxffs_findinode(FAR struct nxffs_volume_s *volume, FAR const char *name, FAR struct nxffs_entry_s *entry) { off_t offset; int ret; /* Start with the first valid inode that was discovered when the volume * was created (or modified after the last file system re-packing). */ offset = volume->inoffset; /* Loop, checking each NXFFS inode until either: (1) we find the NXFFS inode * with the matching name, or (2) we reach the end of data written on the * media. */ for (; ; ) { /* Get the next, valid NXFFS inode entry */ ret = nxffs_nextentry(volume, offset, entry); if (ret < 0) { finfo("No inode found: %d\n", -ret); return ret; } /* Is this the NXFFS inode we are looking for? */ else if (strcmp(name, entry->name) == 0) { /* Yes, return success with the entry data in 'entry' */ return OK; } /* Discard this entry and try the next one. Here we set the * next offset using the raw data length as the offset * increment. This is, of course, not accurate because it * does not account for the data headers that enclose the * data. But it is guaranteed to be less than or equal to * the correct offset and, hence, better then searching * byte-for-byte. */ offset = nxffs_inodeend(volume, entry); nxffs_freeentry(entry); } /* We won't get here, but for some compilers: */ return -ENOENT; } /**************************************************************************** * Name: nxffs_inodeend * * Description: * Return an *approximiate* FLASH offset to end of the inode data. The * returned value is guaranteed to be be less then or equal to the offset * of the thing-of-interest in FLASH. Parsing for interesting things * can begin at that point. * * Assumption: The inode header has been verified by the caller and is * known to contain valid data. * * Input Parameters: * volume - Describes the NXFFS volume * entry - Describes the inode. * * Returned Value: * A FLASH offset to the (approximate) end of the inode data. No errors * are detected. * ****************************************************************************/ off_t nxffs_inodeend(FAR struct nxffs_volume_s *volume, FAR struct nxffs_entry_s *entry) { /* A zero length file will have no data blocks */ if (entry->doffset) { /* This is the maximum size of one data block. It is the physcal size * of the block minus the minimum number of headers: block sna data */ uint16_t maxsize = volume->geo.blocksize - SIZEOF_NXFFS_BLOCK_HDR - SIZEOF_NXFFS_DATA_HDR; /* This is the minimum number of blocks require to span all of the * inode data. One additional block could possibly be required -- we * could make this accurate by looking at the size of the first, perhaps * partial, data block. */ off_t minblocks = (entry->datlen + maxsize - 1) / maxsize; /* And this is our best, simple guess at the end of the inode data */ return entry->doffset + entry->datlen + minblocks * SIZEOF_NXFFS_DATA_HDR; } /* Otherwise, return an offset that accounts only for the inode header and * the inode name. */ /* All valid inodes will have a name associated with them */ DEBUGASSERT(entry->noffset); return entry->noffset + strlen(entry->name); }