| /**************************************************************************** |
| * fs/nxffs/nxffs_inode.c |
| * |
| * 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. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <stdint.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include <nuttx/crc32.h> |
| #include <nuttx/lib/lib.h> |
| #include <nuttx/mtd/mtd.h> |
| |
| #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 = 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: %08" PRIx32 |
| " CRC calculated: %08" PRIx32 "\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) |
| { |
| lib_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: %jd\n", |
| (intmax_t)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); |
| } |