| /**************************************************************************** |
| * fs/nxffs/nxffs_open.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 <fcntl.h> |
| #include <time.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include <nuttx/crc32.h> |
| #include <nuttx/lib/lib.h> |
| #include <nuttx/fs/fs.h> |
| #include <nuttx/mtd/mtd.h> |
| |
| #include "nxffs.h" |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* Since we are limited to a single file opened for writing, it makes sense |
| * to pre-allocate the write state structure. |
| */ |
| |
| #ifdef CONFIG_NXFFS_PREALLOCATED |
| static struct nxffs_wrfile_s g_wrfile; |
| #endif |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: nxffs_hdrpos |
| * |
| * Description: |
| * Find a valid location for the inode header. A valid location will have |
| * these properties: |
| * |
| * 1. It will lie in the free flash region. |
| * 2. It will have enough contiguous memory to hold the entire header |
| * (excluding the file name which may lie in the next block). |
| * 3. The memory at this location will be fully erased. |
| * |
| * This function will only perform the checks of 1) and 2). |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * wrfile - Contains the current guess for the header position. On |
| * successful return, this field will hold the selected header |
| * position. |
| * |
| * Returned Value: |
| * Zero is returned on success. Otherwise, a negated errno value is |
| * returned indicating the nature of the failure. Of special interest |
| * the return error of -ENOSPC which means that the FLASH volume is |
| * full and should be repacked. |
| * |
| * On successful return the following are also valid: |
| * |
| * wrfile->ofile.entry.hoffset - FLASH offset to candidate header |
| * position |
| * volume->ioblock - Read/write block number of the block containing the |
| * header position |
| * volume->iooffset - The offset in the block to the candidate header |
| * position. |
| * volume->froffset - Updated offset to the first free FLASH block. |
| * |
| ****************************************************************************/ |
| |
| static inline int nxffs_hdrpos(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_wrfile_s *wrfile) |
| { |
| int ret; |
| |
| /* Reserve memory for the object */ |
| |
| ret = nxffs_wrreserve(volume, SIZEOF_NXFFS_INODE_HDR); |
| if (ret == OK) |
| { |
| /* Save the offset to the FLASH region reserved for the inode header */ |
| |
| wrfile->ofile.entry.hoffset = nxffs_iotell(volume); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_nampos |
| * |
| * Description: |
| * Find a valid location for the inode name. A valid location will have |
| * these properties: |
| * |
| * 1. It will lie in the free flash region. |
| * 2. It will have enough contiguous memory to hold the entire name |
| * 3. The memory at this location will be fully erased. |
| * |
| * This function will only perform the checks of 1) and 2). |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * wrfile - Contains the current guess for the name position. On |
| * successful return, this field will hold the selected name |
| * position. |
| * namlen - The length of the name. |
| * |
| * Returned Value: |
| * Zero is returned on success. Otherwise, a negated errno value is |
| * returned indicating the nature of the failure. Of special interest |
| * the return error of -ENOSPC which means that the FLASH volume is |
| * full and should be repacked. |
| * |
| * On successful return the following are also valid: |
| * |
| * wrfile->ofile.entry.noffset - FLASH offset to candidate name position |
| * volume->ioblock - Read/write block number of the block containing the |
| * name position |
| * volume->iooffset - The offset in the block to the candidate name |
| * position. |
| * volume->froffset - Updated offset to the first free FLASH block. |
| * |
| ****************************************************************************/ |
| |
| static inline int nxffs_nampos(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_wrfile_s *wrfile, |
| int namlen) |
| { |
| int ret; |
| |
| /* Reserve memory for the object */ |
| |
| ret = nxffs_wrreserve(volume, namlen); |
| if (ret == OK) |
| { |
| /* Save the offset to the FLASH region reserved for the inode name */ |
| |
| wrfile->ofile.entry.noffset = nxffs_iotell(volume); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_hdrerased |
| * |
| * Description: |
| * Find a valid location for the inode header. A valid location will have |
| * these properties: |
| * |
| * 1. It will lie in the free flash region. |
| * 2. It will have enough contiguous memory to hold the entire header |
| * (excluding the file name which may lie in the next block). |
| * 3. The memory at this location will be fully erased. |
| * |
| * This function will only perform the check 3). |
| * |
| * On entry it assumes: |
| * |
| * volume->ioblock - Read/write block number of the block containing the |
| * header position |
| * volume->iooffset - The offset in the block to the candidate header |
| * position. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * wrfile - Contains the current guess for the header position. On |
| * successful return, this field will hold the selected header |
| * position. |
| * |
| * Returned Value: |
| * Zero is returned on success. Otherwise, a negated errno value is |
| * returned indicating the nature of the failure. Of special interest |
| * the return error of -ENOSPC which means that the FLASH volume is |
| * full and should be repacked. |
| * |
| * On successful return the following are also valid: |
| * |
| * wrfile->ofile.entry.hoffset - FLASH offset to candidate header |
| * position |
| * volume->ioblock - Read/write block number of the block containing the |
| * header position |
| * volume->iooffset - The offset in the block to the candidate header |
| * position. |
| * volume->froffset - Updated offset to the first free FLASH block. |
| * |
| ****************************************************************************/ |
| |
| static inline int nxffs_hdrerased(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_wrfile_s *wrfile) |
| { |
| int ret; |
| |
| /* Find a valid location to save the inode header */ |
| |
| ret = nxffs_wrverify(volume, SIZEOF_NXFFS_INODE_HDR); |
| if (ret == OK) |
| { |
| /* This is where we will put the header */ |
| |
| wrfile->ofile.entry.hoffset = nxffs_iotell(volume); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_namerased |
| * |
| * Description: |
| * Find a valid location for the inode name. A valid location will have |
| * these properties: |
| * |
| * 1. It will lie in the free flash region. |
| * 2. It will have enough contiguous memory to hold the entire name |
| * (excluding the file name which may lie in the next block). |
| * 3. The memory at this location will be fully erased. |
| * |
| * This function will only perform the check 3). |
| * |
| * On entry it assumes: |
| * |
| * volume->ioblock - Read/write block number of the block containing the |
| * name position |
| * volume->iooffset - The offset in the block to the candidate name |
| * position. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * wrfile - Contains the current guess for the name position. On |
| * successful return, this field will hold the selected name |
| * position. |
| * |
| * Returned Value: |
| * Zero is returned on success. Otherwise, a negated errno value is |
| * returned indicating the nature of the failure. Of special interest |
| * the return error of -ENOSPC which means that the FLASH volume is |
| * full and should be repacked. |
| * |
| * On successful return the following are also valid: |
| * |
| * wrfile->ofile.entry.noffset - FLASH offset to candidate name position |
| * volume->ioblock - Read/write block number of the block containing the |
| * name position |
| * volume->iooffset - The offset in the block to the candidate name |
| * position. |
| * volume->froffset - Updated offset to the first free FLASH block. |
| * |
| ****************************************************************************/ |
| |
| static inline int nxffs_namerased(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_wrfile_s *wrfile, |
| int namlen) |
| { |
| int ret; |
| |
| /* Find a valid location to save the inode name */ |
| |
| ret = nxffs_wrverify(volume, namlen); |
| if (ret == OK) |
| { |
| /* This is where we will put the name */ |
| |
| wrfile->ofile.entry.noffset = nxffs_iotell(volume); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_wrname |
| * |
| * Description: |
| * Write the inode name to cache at the position verified by |
| * nxffs_namerased(). |
| * |
| * On entry it assumes: |
| * |
| * entry->noffset - FLASH offset to final name position |
| * volume->ioblock - Read/write block number of the block containing the |
| * name position |
| * volume->iooffset - The offset in the block to the candidate name |
| * position. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * entry - Describes the entry to be written. |
| * |
| * Returned Value: |
| * Zero is returned on success. Otherwise, a negated errno value is |
| * returned indicating the nature of the failure. |
| * |
| ****************************************************************************/ |
| |
| static inline int nxffs_wrname(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_entry_s *entry, |
| int namlen) |
| { |
| int ret; |
| |
| /* Seek to the inode name position and assure that it is in the volume |
| * cache. |
| */ |
| |
| nxffs_ioseek(volume, entry->noffset); |
| ret = nxffs_rdcache(volume, volume->ioblock); |
| if (ret < 0) |
| { |
| ferr("ERROR: Failed to read inode name block %jd: %d\n", |
| (intmax_t)volume->ioblock, -ret); |
| return ret; |
| } |
| |
| /* Copy the inode name to the volume cache and write the inode name block */ |
| |
| memcpy(&volume->cache[volume->iooffset], entry->name, namlen); |
| ret = nxffs_wrcache(volume); |
| if (ret < 0) |
| { |
| ferr("ERROR: Failed to write inode header block %jd: %d\n", |
| (intmax_t)volume->ioblock, -ret); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_wropen |
| * |
| * Description: |
| * Handle opening for writing. Only a single writer is permitted and only |
| * file creation is supported. |
| * |
| ****************************************************************************/ |
| |
| static inline int nxffs_wropen(FAR struct nxffs_volume_s *volume, |
| FAR const char *name, mode_t oflags, |
| FAR struct nxffs_ofile_s **ppofile) |
| { |
| FAR struct nxffs_wrfile_s *wrfile; |
| FAR struct nxffs_entry_s entry; |
| bool packed; |
| bool truncate = false; |
| int namlen; |
| int ret; |
| |
| /* Limitation: Only a single writer is permitted. Writing may involve |
| * extension of the file system in FLASH. Since files are contiguous |
| * in FLASH, only a single file may be extending the FLASH region. |
| */ |
| |
| ret = nxsem_wait(&volume->wrsem); |
| if (ret < 0) |
| { |
| ferr("ERROR: nxsem_wait failed: %d\n", ret); |
| goto errout; |
| } |
| |
| /* Get exclusive access to the volume. Note that the volume lock |
| * protects the open file list. Note that lock is ALWAYS taken |
| * after wrsem to avoid deadlocks. |
| */ |
| |
| ret = nxmutex_lock(&volume->lock); |
| if (ret < 0) |
| { |
| ferr("ERROR: nxmutex_lock failed: %d\n", ret); |
| goto errout_with_wrsem; |
| } |
| |
| /* Check if the file exists */ |
| |
| ret = nxffs_findinode(volume, name, &entry); |
| if (ret == OK) |
| { |
| FAR struct nxffs_ofile_s *ofile; |
| |
| /* It exists. Release the entry. */ |
| |
| nxffs_freeentry(&entry); |
| |
| /* Is the file already open for reading? */ |
| |
| ofile = nxffs_findofile(volume, name); |
| if (ofile) |
| { |
| /* The file is already open. |
| * Limitation: Files cannot be open both for reading and writing. |
| */ |
| |
| ferr("ERROR: File is open for reading\n"); |
| ret = -ENOSYS; |
| goto errout_with_lock; |
| } |
| |
| /* It would be an error if we are asked to create the file |
| * exclusively. |
| */ |
| |
| else if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) |
| { |
| ferr("ERROR: File exists, can't create O_EXCL\n"); |
| ret = -EEXIST; |
| goto errout_with_lock; |
| } |
| |
| /* Were we asked to truncate the file? NOTE: Don't truncate the |
| * file if we were not also asked to created it. See below... |
| * we will not re-create the file unless O_CREAT is also specified. |
| */ |
| |
| else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) |
| { |
| /* Just schedule the removal the file and fall through to re-create |
| * it. |
| * Note that the old file of the same name will not actually be |
| * removed until the new file is successfully written. |
| */ |
| |
| truncate = true; |
| } |
| |
| /* The file exists and we were not asked to truncate (and recreate) it. |
| * Limitation: Cannot write to existing files. |
| */ |
| |
| else |
| { |
| ferr("ERROR: File %s exists and we were not asked to " |
| "truncate it\n", name); |
| ret = -ENOSYS; |
| goto errout_with_lock; |
| } |
| } |
| |
| /* Okay, the file is not open and does not exists (maybe because we deleted |
| * it). Now, make sure that we were asked to created it. |
| */ |
| |
| if ((oflags & O_CREAT) == 0) |
| { |
| ferr("ERROR: Not asked to create the file\n"); |
| ret = -ENOENT; |
| goto errout_with_lock; |
| } |
| |
| /* Make sure that the length of the file name will fit in a uint8_t */ |
| |
| namlen = strlen(name); |
| if (namlen > CONFIG_NXFFS_MAXNAMLEN) |
| { |
| ferr("ERROR: Name is too long: %d\n", namlen); |
| ret = -EINVAL; |
| goto errout_with_lock; |
| } |
| |
| /* Yes.. Create a new structure that will describe the state of this open |
| * file. NOTE that a special variant of the open file structure is used |
| * that includes additional information to support the write operation. |
| */ |
| |
| #ifdef CONFIG_NXFFS_PREALLOCATED |
| wrfile = &g_wrfile; |
| memset(wrfile, 0, sizeof(struct nxffs_wrfile_s)); |
| #else |
| wrfile = (FAR struct nxffs_wrfile_s *) |
| kmm_zalloc(sizeof(struct nxffs_wrfile_s)); |
| if (!wrfile) |
| { |
| ret = -ENOMEM; |
| goto errout_with_lock; |
| } |
| #endif |
| |
| /* Initialize the open file state structure */ |
| |
| wrfile->ofile.crefs = 1; |
| wrfile->ofile.oflags = oflags; |
| wrfile->ofile.entry.utc = time(NULL); |
| wrfile->truncate = truncate; |
| |
| /* Save a copy of the inode name. */ |
| |
| wrfile->ofile.entry.name = strdup(name); |
| if (!wrfile->ofile.entry.name) |
| { |
| ret = -ENOMEM; |
| goto errout_with_ofile; |
| } |
| |
| /* Allocate FLASH memory for the file and set up for the write. |
| * |
| * Loop until the inode header is configured or until a failure occurs. |
| * Note that nothing is written to FLASH. The inode header is not |
| * written until the file is closed. |
| */ |
| |
| packed = false; |
| for (; ; ) |
| { |
| /* File a valid location to position the inode header. Start with the |
| * first byte in the free FLASH region. |
| */ |
| |
| ret = nxffs_hdrpos(volume, wrfile); |
| if (ret == OK) |
| { |
| /* Find a region of memory in the block that is fully erased */ |
| |
| ret = nxffs_hdrerased(volume, wrfile); |
| if (ret == OK) |
| { |
| /* Valid memory for the inode header was found. Break out of |
| * the loop. |
| */ |
| |
| break; |
| } |
| } |
| |
| /* If no valid memory is found searching to the end of the volume, |
| * then -ENOSPC will be returned. Other errors are not handled. |
| */ |
| |
| if (ret != -ENOSPC || packed) |
| { |
| ferr("ERROR: Failed to find inode header memory: %d\n", -ret); |
| goto errout_with_name; |
| } |
| |
| /* -ENOSPC is a special case.. It means that the volume is full. |
| * Try to pack the volume in order to free up some space. |
| */ |
| |
| ret = nxffs_pack(volume); |
| if (ret < 0) |
| { |
| ferr("ERROR: Failed to pack the volume: %d\n", -ret); |
| goto errout_with_name; |
| } |
| |
| /* After packing the volume, froffset will be updated to point to the |
| * new free flash region. Try again. |
| */ |
| |
| packed = true; |
| } |
| |
| /* Loop until the inode name is configured or until a failure occurs. |
| * Note that nothing is written to FLASH. |
| */ |
| |
| for (; ; ) |
| { |
| /* File a valid location to position the inode name. Start with the |
| * first byte in the free FLASH region. |
| */ |
| |
| ret = nxffs_nampos(volume, wrfile, namlen); |
| if (ret == OK) |
| { |
| /* Find a region of memory in the block that is fully erased */ |
| |
| ret = nxffs_namerased(volume, wrfile, namlen); |
| if (ret == OK) |
| { |
| /* Valid memory for the inode header was found. Write the |
| * inode name to this location. |
| */ |
| |
| ret = nxffs_wrname(volume, &wrfile->ofile.entry, namlen); |
| if (ret < 0) |
| { |
| ferr("ERROR: Failed to write the inode name: %d\n", -ret); |
| goto errout_with_name; |
| } |
| |
| /* Then just break out of the loop reporting success. Note |
| * that the alllocated inode name string is retained; it |
| * will be needed later to calculate the inode CRC. |
| */ |
| |
| break; |
| } |
| } |
| |
| /* If no valid memory is found searching to the end of the volume, |
| * then -ENOSPC will be returned. Other errors are not handled. |
| */ |
| |
| if (ret != -ENOSPC || packed) |
| { |
| ferr("ERROR: Failed to find inode name memory: %d\n", -ret); |
| goto errout_with_name; |
| } |
| |
| /* -ENOSPC is a special case.. It means that the volume is full. |
| * Try to pack the volume in order to free up some space. |
| */ |
| |
| ret = nxffs_pack(volume); |
| if (ret < 0) |
| { |
| ferr("ERROR: Failed to pack the volume: %d\n", -ret); |
| goto errout_with_name; |
| } |
| |
| /* After packing the volume, froffset will be updated to point to the |
| * new free flash region. Try again. |
| */ |
| |
| packed = true; |
| } |
| |
| /* Add the open file structure to the head of the list of open files */ |
| |
| wrfile->ofile.flink = volume->ofiles; |
| volume->ofiles = &wrfile->ofile; |
| |
| /* Indicate that the volume is open for writing and return the open file |
| * instance. Releasing lock allows other readers while the write is |
| * in progress. But wrsem is still held for this open file, preventing |
| * any further writers until this inode is closed.s |
| */ |
| |
| *ppofile = &wrfile->ofile; |
| nxmutex_unlock(&volume->lock); |
| return OK; |
| |
| errout_with_name: |
| lib_free(wrfile->ofile.entry.name); |
| errout_with_ofile: |
| #ifndef CONFIG_NXFFS_PREALLOCATED |
| kmm_free(wrfile); |
| #endif |
| |
| errout_with_lock: |
| nxmutex_unlock(&volume->lock); |
| errout_with_wrsem: |
| nxsem_post(&volume->wrsem); |
| errout: |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_rdopen |
| * |
| * Description: |
| * Open an existing file for reading. |
| * |
| ****************************************************************************/ |
| |
| static inline int nxffs_rdopen(FAR struct nxffs_volume_s *volume, |
| FAR const char *name, |
| FAR struct nxffs_ofile_s **ppofile) |
| { |
| FAR struct nxffs_ofile_s *ofile; |
| int ret; |
| |
| /* Get exclusive access to the volume. Note that the volume lock |
| * protects the open file list. |
| */ |
| |
| ret = nxmutex_lock(&volume->lock); |
| if (ret != OK) |
| { |
| ferr("ERROR: nxmutex_lock failed: %d\n", ret); |
| goto errout; |
| } |
| |
| /* Check if the file has already been opened (for reading) */ |
| |
| ofile = nxffs_findofile(volume, name); |
| if (ofile) |
| { |
| /* The file is already open. |
| * Limitation: Files cannot be open both for reading and writing. |
| */ |
| |
| if ((ofile->oflags & O_WROK) != 0) |
| { |
| ferr("ERROR: File is open for writing\n"); |
| ret = -ENOSYS; |
| goto errout_with_lock; |
| } |
| |
| /* Just increment the reference count on the ofile */ |
| |
| ofile->crefs++; |
| finfo("crefs: %d\n", ofile->crefs); |
| } |
| |
| /* The file has not yet been opened. |
| * Limitation: The file must exist. We do not support creation of files |
| * read-only. |
| */ |
| |
| else |
| { |
| /* Not already open.. create a new open structure */ |
| |
| ofile = (FAR struct nxffs_ofile_s *) |
| kmm_zalloc(sizeof(struct nxffs_ofile_s)); |
| if (!ofile) |
| { |
| ferr("ERROR: ofile allocation failed\n"); |
| ret = -ENOMEM; |
| goto errout_with_lock; |
| } |
| |
| /* Initialize the open file state structure */ |
| |
| ofile->crefs = 1; |
| ofile->oflags = O_RDOK; |
| |
| /* Find the file on this volume associated with this file name */ |
| |
| ret = nxffs_findinode(volume, name, &ofile->entry); |
| if (ret != OK) |
| { |
| finfo("Inode '%s' not found: %d\n", name, -ret); |
| goto errout_with_ofile; |
| } |
| |
| /* Add the open file structure to the head of the list of open files */ |
| |
| ofile->flink = volume->ofiles; |
| volume->ofiles = ofile; |
| } |
| |
| /* Return the open file state structure */ |
| |
| *ppofile = ofile; |
| nxmutex_unlock(&volume->lock); |
| return OK; |
| |
| errout_with_ofile: |
| kmm_free(ofile); |
| errout_with_lock: |
| nxmutex_unlock(&volume->lock); |
| errout: |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_remofile |
| * |
| * Description: |
| * Remove an entry from the open file list. |
| * |
| ****************************************************************************/ |
| |
| static inline void nxffs_remofile(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_ofile_s *ofile) |
| { |
| FAR struct nxffs_ofile_s *prev; |
| FAR struct nxffs_ofile_s *curr; |
| |
| /* Find the open file structure to be removed */ |
| |
| for (prev = NULL, curr = volume->ofiles; |
| curr && curr != ofile; |
| prev = curr, curr = curr->flink); |
| |
| /* Was it found? */ |
| |
| if (curr) |
| { |
| /* Yes.. at the head of the list? */ |
| |
| if (prev) |
| { |
| prev->flink = ofile->flink; |
| } |
| else |
| { |
| volume->ofiles = ofile->flink; |
| } |
| } |
| else |
| { |
| ferr("ERROR: Open inode %p not found\n", ofile); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_freeofile |
| * |
| * Description: |
| * Free resources held by an open file. |
| * |
| ****************************************************************************/ |
| |
| static inline void nxffs_freeofile(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_ofile_s *ofile) |
| { |
| /* Release the open file entry */ |
| |
| nxffs_freeentry(&ofile->entry); |
| |
| /* Then free the open file container (unless this the pre-alloated |
| * write-only open file container) |
| */ |
| |
| #ifdef CONFIG_NXFFS_PREALLOCATED |
| if ((FAR struct nxffs_wrfile_s *)ofile != &g_wrfile) |
| #endif |
| { |
| kmm_free(ofile); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_wrclose |
| * |
| * Description: |
| * Perform special operations when a file is closed: |
| * 1. Write the file block header |
| * 2. Remove any file with the same name that was discovered when the |
| * file was open for writing, and finally, |
| * 3. Write the new file inode. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * wrfile - Describes the state of the open file |
| * |
| ****************************************************************************/ |
| |
| static inline int nxffs_wrclose(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_wrfile_s *wrfile) |
| { |
| int ret; |
| |
| /* Is there an unfinalized write data? */ |
| |
| if (wrfile->datlen > 0) |
| { |
| /* Yes.. Write the final file block header */ |
| |
| ret = nxffs_wrblkhdr(volume, wrfile); |
| if (ret < 0) |
| { |
| ferr("ERROR: Failed to write the final block of the file: %d\n", |
| -ret); |
| goto errout; |
| } |
| } |
| |
| /* Truncation is implemented by writing the new file, then deleting the |
| * older version of the file. Note that we removed the entry from the |
| * open file list earlier in the close sequence; this will prevent the |
| * open file check from failing when we remove the old version of the |
| * file. |
| */ |
| |
| if (wrfile->truncate && wrfile->ofile.entry.name) |
| { |
| finfo("Removing old file: %s\n", wrfile->ofile.entry.name); |
| |
| ret = nxffs_rminode(volume, wrfile->ofile.entry.name); |
| if (ret < 0) |
| { |
| ferr("ERROR: nxffs_rminode failed: %d\n", -ret); |
| goto errout; |
| } |
| } |
| |
| /* Write the inode header to FLASH */ |
| |
| ret = nxffs_wrinode(volume, &wrfile->ofile.entry); |
| |
| /* The volume is now available for other writers */ |
| |
| errout: |
| nxsem_post(&volume->wrsem); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: nxffs_findofile |
| * |
| * Description: |
| * Search the list of already opened files to see if the inode of this |
| * name is one of the opened files. |
| * |
| * Input Parameters: |
| * name - The name of the inode to check. |
| * |
| * Returned Value: |
| * If an inode of this name is found in the list of opened inodes, then |
| * a reference to the open file structure is returned. NULL is returned |
| * otherwise. |
| * |
| ****************************************************************************/ |
| |
| FAR struct nxffs_ofile_s *nxffs_findofile(FAR struct nxffs_volume_s *volume, |
| FAR const char *name) |
| { |
| FAR struct nxffs_ofile_s *ofile; |
| |
| /* Check every open file. Note that the volume lock protects the |
| * list of open files. |
| */ |
| |
| for (ofile = volume->ofiles; ofile; ofile = ofile->flink) |
| { |
| /* Check for a name match */ |
| |
| if (strcmp(name, ofile->entry.name) == 0) |
| { |
| return ofile; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_findwriter |
| * |
| * Description: |
| * Search the list of already opened files and return the open file |
| * instance for the write. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume. |
| * |
| * Returned Value: |
| * If there is an active writer of the volume, its open file instance is |
| * returned. NULL is returned otherwise. |
| * |
| ****************************************************************************/ |
| |
| FAR struct nxffs_wrfile_s *nxffs_findwriter( |
| FAR struct nxffs_volume_s *volume) |
| { |
| /* We can tell if the write is in-use because it will have an allocated |
| * name attached. |
| */ |
| |
| #ifdef CONFIG_NXFFS_PREALLOCATED |
| return g_wrfile.ofile.entry.name != NULL ? &g_wrfile : NULL; |
| #else |
| # error "Missing implementation" |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_open |
| * |
| * Description: |
| * This is the standard mountpoint open method. |
| * |
| ****************************************************************************/ |
| |
| int nxffs_open(FAR struct file *filep, FAR const char *relpath, |
| int oflags, mode_t mode) |
| { |
| FAR struct nxffs_volume_s *volume; |
| FAR struct nxffs_ofile_s *ofile = NULL; |
| int ret; |
| |
| finfo("Open '%s'\n", relpath); |
| |
| /* Sanity checks */ |
| |
| DEBUGASSERT(filep->f_priv == NULL); |
| |
| /* Get the mountpoint private data from the NuttX inode reference in the |
| * file structure |
| */ |
| |
| volume = filep->f_inode->i_private; |
| DEBUGASSERT(volume != NULL); |
| |
| /* Limitation: A file must be opened for reading or writing, but not both. |
| * There is no general way of extending the size of a file. Extending the |
| * file size of possible if the file to be extended is the last in the |
| * sequence on FLASH, but since that case is not the general case, no file |
| * extension is supported. |
| */ |
| |
| switch (oflags & (O_WROK | O_RDOK)) |
| { |
| case 0: |
| default: |
| ferr("ERROR: One of O_WRONLY/O_RDONLY must be provided\n"); |
| return -EINVAL; |
| |
| case O_WROK: |
| ret = nxffs_wropen(volume, relpath, oflags, &ofile); |
| break; |
| |
| case O_RDOK: |
| ret = nxffs_rdopen(volume, relpath, &ofile); |
| break; |
| |
| case O_WROK | O_RDOK: |
| ferr("ERROR: O_RDWR is not supported\n"); |
| return -ENOSYS; |
| } |
| |
| /* Save the reference to the open-specific state in filep->f_priv */ |
| |
| if (ret == OK) |
| { |
| filep->f_priv = ofile; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: binfs_dup |
| * |
| * Description: |
| * Duplicate open file data in the new file structure. |
| * |
| ****************************************************************************/ |
| |
| int nxffs_dup(FAR const struct file *oldp, FAR struct file *newp) |
| { |
| #ifdef CONFIG_DEBUG_ASSERTIONS |
| FAR struct nxffs_volume_s *volume; |
| #endif |
| FAR struct nxffs_ofile_s *ofile; |
| |
| finfo("Dup %p->%p\n", oldp, newp); |
| |
| /* Sanity checks */ |
| |
| #ifdef CONFIG_DEBUG_ASSERTIONS |
| DEBUGASSERT(oldp->f_priv == NULL && oldp->f_inode != NULL); |
| |
| /* Get the mountpoint private data from the NuttX inode reference in the |
| * file structure |
| */ |
| |
| volume = oldp->f_inode->i_private; |
| DEBUGASSERT(volume != NULL); |
| #endif |
| |
| /* Recover the open file state from the struct file instance */ |
| |
| ofile = (FAR struct nxffs_ofile_s *)oldp->f_priv; |
| |
| /* I do not think we need exclusive access to the volume to do this. |
| * The volume lock protects the open file list and, hence, would |
| * assure that the ofile is stable. However, it is assumed that the |
| * caller holds a value file descriptor associated with this ofile, |
| * so it should be stable throughout the life of this function. |
| */ |
| |
| /* Limitations: I do not think we have to be concerned about the |
| * usual NXFFS file limitations here: dup'ing cannot resulting |
| * in mixed reading and writing to the same file, or multiple |
| * writer to different file. |
| * |
| * I notice that nxffs_wropen will prohibit multiple opens for |
| * writing. But I do not thing that dup'ing a file already opened |
| * for writing suffers from any of these issues. |
| */ |
| |
| /* Just increment the reference count on the ofile */ |
| |
| ofile->crefs++; |
| newp->f_priv = ofile; |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_close |
| * |
| * Description: |
| * This is the standard mountpoint close method. |
| * |
| ****************************************************************************/ |
| |
| int nxffs_close(FAR struct file *filep) |
| { |
| FAR struct nxffs_volume_s *volume; |
| FAR struct nxffs_ofile_s *ofile; |
| int ret; |
| |
| finfo("Closing\n"); |
| |
| /* Sanity checks */ |
| |
| DEBUGASSERT(filep->f_priv != NULL); |
| |
| /* Recover the open file state from the struct file instance */ |
| |
| ofile = (FAR struct nxffs_ofile_s *)filep->f_priv; |
| |
| /* Recover the volume state from the open file */ |
| |
| volume = filep->f_inode->i_private; |
| DEBUGASSERT(volume != NULL); |
| |
| /* Get exclusive access to the volume. Note that the volume lock |
| * protects the open file list. |
| */ |
| |
| ret = nxmutex_lock(&volume->lock); |
| if (ret != OK) |
| { |
| ferr("ERROR: nxmutex_lock failed: %d\n", ret); |
| goto errout; |
| } |
| |
| /* Decrement the reference count on the open file */ |
| |
| ret = OK; |
| if (ofile->crefs == 1) |
| { |
| /* Decrementing the reference count would take it zero. |
| * |
| * Remove the entry from the open file list. We do this early |
| * to avoid some chick-and-egg problems with file truncation. |
| */ |
| |
| nxffs_remofile(volume, ofile); |
| |
| /* Handle special finalization of the write operation. */ |
| |
| if ((ofile->oflags & O_WROK) != 0) |
| { |
| ret = nxffs_wrclose(volume, (FAR struct nxffs_wrfile_s *)ofile); |
| } |
| |
| /* Release all resources held by the open file */ |
| |
| nxffs_freeofile(volume, ofile); |
| } |
| else |
| { |
| /* Just decrement the reference count */ |
| |
| ofile->crefs--; |
| } |
| |
| filep->f_priv = NULL; |
| nxmutex_unlock(&volume->lock); |
| |
| errout: |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_wrinode |
| * |
| * Description: |
| * Write the inode header (only to FLASH. This is done in two contexts: |
| * |
| * 1. When an inode is closed, or |
| * 2. As part of the file system packing logic when an inode is moved. |
| * |
| * Note that in either case, the inode name has already been written to |
| * FLASH. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * entry - Describes the inode header to write |
| * |
| * Returned Value: |
| * Zero is returned on success; Otherwise, a negated errno value is |
| * returned indicating the nature of the failure. |
| * |
| ****************************************************************************/ |
| |
| int nxffs_wrinode(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_entry_s *entry) |
| { |
| FAR struct nxffs_inode_s *inode; |
| uint32_t crc; |
| int namlen; |
| int ret; |
| |
| /* Seek to the inode header position and assure that it is in the volume |
| * cache. |
| */ |
| |
| nxffs_ioseek(volume, entry->hoffset); |
| ret = nxffs_rdcache(volume, volume->ioblock); |
| if (ret < 0) |
| { |
| ferr("ERROR: Failed to read inode header block %jd: %d\n", |
| (intmax_t)volume->ioblock, -ret); |
| goto errout; |
| } |
| |
| /* Get the length of the inode name */ |
| |
| namlen = strlen(entry->name); |
| DEBUGASSERT(namlen < CONFIG_NXFFS_MAXNAMLEN); /* This was verified earlier */ |
| |
| /* Initialize the inode header */ |
| |
| inode = (FAR struct nxffs_inode_s *)&volume->cache[volume->iooffset]; |
| memcpy(inode->magic, g_inodemagic, NXFFS_MAGICSIZE); |
| |
| inode->state = CONFIG_NXFFS_ERASEDSTATE; |
| inode->namlen = namlen; |
| |
| nxffs_wrle32(inode->noffs, entry->noffset); |
| nxffs_wrle32(inode->doffs, entry->doffset); |
| nxffs_wrle32(inode->utc, entry->utc); |
| nxffs_wrle32(inode->crc, 0); |
| nxffs_wrle32(inode->datlen, entry->datlen); |
| |
| /* Calculate the CRC */ |
| |
| crc = crc32((FAR const uint8_t *)inode, SIZEOF_NXFFS_INODE_HDR); |
| crc = crc32part((FAR const uint8_t *)entry->name, namlen, crc); |
| |
| /* Finish the inode header */ |
| |
| inode->state = INODE_STATE_FILE; |
| nxffs_wrle32(inode->crc, crc); |
| |
| /* Write the block with the inode header */ |
| |
| ret = nxffs_wrcache(volume); |
| if (ret < 0) |
| { |
| ferr("ERROR: Failed to write inode header block %jd: %d\n", |
| (intmax_t)volume->ioblock, -ret); |
| } |
| |
| /* The volume is now available for other writers */ |
| |
| errout: |
| nxsem_post(&volume->wrsem); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_updateinode |
| * |
| * Description: |
| * The packing logic has moved an inode. Check if any open files are using |
| * this inode and, if so, move the data in the open file structure as well. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume |
| * entry - Describes the new inode entry |
| * |
| * Returned Value: |
| * Zero is returned on success; Otherwise, a negated errno value is |
| * returned indicating the nature of the failure. |
| * |
| ****************************************************************************/ |
| |
| int nxffs_updateinode(FAR struct nxffs_volume_s *volume, |
| FAR struct nxffs_entry_s *entry) |
| { |
| FAR struct nxffs_ofile_s *ofile; |
| |
| /* Find the open inode structure matching this name */ |
| |
| ofile = nxffs_findofile(volume, entry->name); |
| if (ofile) |
| { |
| /* Yes.. the file is open. Update the FLASH offsets to inode headers */ |
| |
| ofile->entry.hoffset = entry->hoffset; |
| ofile->entry.noffset = entry->noffset; |
| ofile->entry.doffset = entry->doffset; |
| } |
| |
| return OK; |
| } |