/**************************************************************************** * fs/smartfs/smartfs_smart.c * * Copyright (C) 2013 Ken Pettit. All rights reserved. * Author: Ken Pettit * * 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 #include #include #include #include #include #include #include #include #include #include "smartfs.h" /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static int smartfs_open(FAR struct file *filep, const char *relpath, int oflags, mode_t mode); static int smartfs_close(FAR struct file *filep); static ssize_t smartfs_read(FAR struct file *filep, char *buffer, size_t buflen); static ssize_t smartfs_write(FAR struct file *filep, const char *buffer, size_t buflen); static off_t smartfs_seek(FAR struct file *filep, off_t offset, int whence); static int smartfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg); static int smartfs_sync(FAR struct file *filep); static int smartfs_dup(FAR const struct file *oldp, FAR struct file *newp); static int smartfs_fstat(FAR const struct file *filep, FAR struct stat *buf); static int smartfs_truncate(FAR struct file *filep, off_t length); static int smartfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s *dir); static int smartfs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int smartfs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int smartfs_bind(FAR struct inode *blkdriver, FAR const void *data, FAR void **handle); static int smartfs_unbind(void *handle, FAR struct inode **blkdriver, unsigned int flags); static int smartfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf); static int smartfs_unlink(FAR struct inode *mountpt, FAR const char *relpath); static int smartfs_mkdir(FAR struct inode *mountpt, FAR const char *relpath, mode_t mode); static int smartfs_rmdir(FAR struct inode *mountpt, FAR const char *relpath); static int smartfs_rename(FAR struct inode *mountpt, FAR const char *oldrelpath, const char *newrelpath); static void smartfs_stat_common(FAR struct smartfs_mountpt_s *fs, FAR struct smartfs_entry_s *entry, FAR struct stat *buf); static int smartfs_stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf); /**************************************************************************** * Private Data ****************************************************************************/ static uint8_t g_seminitialized = FALSE; static sem_t g_sem; /**************************************************************************** * Public Data ****************************************************************************/ /* See fs_mount.c -- this structure is explicitly externed there. * We use the old-fashioned kind of initializers so that this will compile * with any compiler. */ const struct mountpt_operations smartfs_operations = { smartfs_open, /* open */ smartfs_close, /* close */ smartfs_read, /* read */ smartfs_write, /* write */ smartfs_seek, /* seek */ smartfs_ioctl, /* ioctl */ smartfs_sync, /* sync */ smartfs_dup, /* dup */ smartfs_fstat, /* fstat */ smartfs_truncate, /* truncate */ smartfs_opendir, /* opendir */ NULL, /* closedir */ smartfs_readdir, /* readdir */ smartfs_rewinddir, /* rewinddir */ smartfs_bind, /* bind */ smartfs_unbind, /* unbind */ smartfs_statfs, /* statfs */ smartfs_unlink, /* unlinke */ smartfs_mkdir, /* mkdir */ smartfs_rmdir, /* rmdir */ smartfs_rename, /* rename */ smartfs_stat /* stat */ }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: smartfs_open ****************************************************************************/ static int smartfs_open(FAR struct file *filep, const char *relpath, int oflags, mode_t mode) { struct inode *inode; struct smartfs_mountpt_s *fs; int ret; uint16_t parentdirsector; const char *filename; struct smartfs_ofile_s *sf; #ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER struct smart_read_write_s readwrite; #endif /* Sanity checks */ DEBUGASSERT((filep->f_priv == NULL) && (filep->f_inode != NULL)); /* Get the mountpoint inode reference from the file structure and the * mountpoint private data from the inode structure */ inode = filep->f_inode; fs = inode->i_private; DEBUGASSERT(fs != NULL); /* Take the semaphore */ ret = smartfs_semtake(fs); if (ret < 0) { return ret; } /* Locate the directory entry for this path */ sf = (struct smartfs_ofile_s *)kmm_malloc(sizeof *sf); if (sf == NULL) { ret = -ENOMEM; goto errout_with_semaphore; } /* Allocate a sector buffer if CRC enabled in the MTD layer */ #ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER sf->buffer = (uint8_t *)kmm_malloc(fs->fs_llformat.availbytes); if (sf->buffer == NULL) { /* Error ... no memory */ kmm_free(sf); ret = -ENOMEM; goto errout_with_semaphore; } sf->bflags = 0; #endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */ sf->entry.name = NULL; ret = smartfs_finddirentry(fs, &sf->entry, relpath, &parentdirsector, &filename); /* Three possibilities: (1) a node exists for the relpath and * dirinfo describes the directory entry of the entity, (2) the * node does not exist, or (3) some error occurred. */ if (ret == OK) { /* The name exists -- but is is a file or a directory? */ if (sf->entry.flags & SMARTFS_DIRENT_TYPE_DIR) { /* Can't open a dir as a file! */ ret = -EISDIR; goto errout_with_buffer; } /* It would be an error if we are asked to create it exclusively */ if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) { /* Already exists -- can't create it exclusively */ ret = -EEXIST; goto errout_with_buffer; } /* TODO: Test open mode based on the file mode */ /* If O_TRUNC is specified and the file is opened for writing, * then truncate the file. This operation requires that the file * is writeable. O_TRUNC without write access is ignored. */ if ((oflags & (O_TRUNC | O_WROK)) == (O_TRUNC | O_WROK)) { /* Truncate the file as part of the open */ ret = smartfs_shrinkfile(fs, sf, 0); if (ret < 0) { goto errout_with_buffer; } } } else if (ret == -ENOENT) { /* The file does not exist. Were we asked to create it? */ if ((oflags & O_CREAT) == 0) { /* No.. then we fail with -ENOENT */ ret = -ENOENT; goto errout_with_buffer; } /* Yes... test if the parent directory is valid */ if (parentdirsector != 0xffff) { /* We can create in the given parent directory */ ret = smartfs_createentry(fs, parentdirsector, filename, SMARTFS_DIRENT_TYPE_FILE, mode, &sf->entry, 0xffff, sf); if (ret != OK) { goto errout_with_buffer; } } else { /* Trying to create in a directory that doesn't exist */ ret = -ENOENT; goto errout_with_buffer; } } else { goto errout_with_buffer; } /* Now perform the "open" on the file in direntry */ sf->oflags = oflags; sf->crefs = 1; sf->filepos = 0; sf->curroffset = sizeof(struct smartfs_chain_header_s); sf->currsector = sf->entry.firstsector; sf->byteswritten = 0; #ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER /* When using sector buffering, current sector with its header should * always be present in sf->buffer. Otherwise data corruption may arise * when writing. */ if (sf->currsector != SMARTFS_ERASEDSTATE_16BIT) { readwrite.logsector = sf->currsector; readwrite.offset = 0; readwrite.count = fs->fs_llformat.availbytes; readwrite.buffer = (uint8_t *) sf->buffer; ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); if (ret < 0) { ferr("ERROR: Error %d reading sector %d header\n", ret, sf->currsector); goto errout_with_buffer; } } #endif /* Test if we opened for APPEND mode. If we did, then seek to the * end of the file. */ if (oflags & O_APPEND) { /* Perform the seek */ smartfs_seek_internal(fs, sf, 0, SEEK_END); } /* Attach the private date to the struct file instance */ filep->f_priv = sf; /* Then insert the new instance into the mountpoint structure. * It needs to be there (1) to handle error conditions that effect * all files, and (2) to inform the umount logic that we are busy * (but a simple reference count could have done that). */ sf->fnext = fs->fs_head; fs->fs_head = sf; ret = OK; goto errout_with_semaphore; errout_with_buffer: if (sf->entry.name != NULL) { /* Free the space for the name too */ kmm_free(sf->entry.name); sf->entry.name = NULL; } #ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER kmm_free(sf->buffer); #endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */ kmm_free(sf); errout_with_semaphore: smartfs_semgive(fs); if (ret == -EINVAL) { ret = -EIO; } return ret; } /**************************************************************************** * Name: smartfs_close ****************************************************************************/ static int smartfs_close(FAR struct file *filep) { struct inode *inode; struct smartfs_mountpt_s *fs; struct smartfs_ofile_s *sf; struct smartfs_ofile_s *nextfile; struct smartfs_ofile_s *prevfile; int ret; /* Sanity checks */ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); /* Recover our private data from the struct file instance */ inode = filep->f_inode; fs = inode->i_private; sf = filep->f_priv; /* Sync the file */ smartfs_sync(filep); /* Take the semaphore */ ret = smartfs_semtake(fs); if (ret < 0) { return ret; } /* Check if we are the last one with a reference to the file and * only close if we are. */ if (sf->crefs > 1) { /* The file is opened more than once. Just decrement the * reference count and return. */ sf->crefs--; goto okout; } /* Remove ourselves from the linked list */ nextfile = fs->fs_head; prevfile = nextfile; while ((nextfile != sf) && (nextfile != NULL)) { /* Save the previous file pointer too */ prevfile = nextfile; nextfile = nextfile->fnext; } if (nextfile != NULL) { /* Test if we were the first entry */ if (nextfile == fs->fs_head) { /* Assign a new head */ fs->fs_head = nextfile->fnext; } else { /* Take ourselves out of the list */ prevfile->fnext = nextfile->fnext; } } /* Now free the pointer */ filep->f_priv = NULL; if (sf->entry.name != NULL) { /* Free the space for the name too */ kmm_free(sf->entry.name); sf->entry.name = NULL; } #ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER if (sf->buffer) { kmm_free(sf->buffer); } #endif kmm_free(sf); okout: smartfs_semgive(fs); return OK; } /**************************************************************************** * Name: smartfs_read ****************************************************************************/ static ssize_t smartfs_read(FAR struct file *filep, char *buffer, size_t buflen) { struct inode *inode; struct smartfs_mountpt_s *fs; struct smartfs_ofile_s *sf; struct smart_read_write_s readwrite; struct smartfs_chain_header_s *header; int ret = OK; uint32_t bytesread; uint16_t bytestoread; uint16_t bytesinsector; /* Sanity checks */ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); /* Recover our private data from the struct file instance */ sf = filep->f_priv; inode = filep->f_inode; fs = inode->i_private; DEBUGASSERT(fs != NULL); /* Take the semaphore */ ret = smartfs_semtake(fs); if (ret < 0) { return (ssize_t)ret; } /* Loop until all byte read or error */ bytesread = 0; while (bytesread != buflen) { /* Test if we are at the end of data */ if (sf->currsector == SMARTFS_ERASEDSTATE_16BIT) { /* Break and return the number of bytes we read (may be zero) */ break; } /* Read the current sector into our buffer */ readwrite.logsector = sf->currsector; readwrite.offset = 0; readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; readwrite.count = fs->fs_llformat.availbytes; ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); if (ret < 0) { ferr("ERROR: Error %d reading sector %d data\n", ret, sf->currsector); goto errout_with_semaphore; } /* Point header to the read data to get used byte count */ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; /* Get number of used bytes in this sector */ bytesinsector = *((uint16_t *) header->used); if (bytesinsector == SMARTFS_ERASEDSTATE_16BIT) { /* No bytes to read from this sector */ bytesinsector = 0; } /* Calculate the number of bytes to read into the buffer */ bytestoread = bytesinsector - (sf->curroffset - sizeof(struct smartfs_chain_header_s)); if (bytestoread + bytesread > buflen) { /* Truncate bytesto read based on buffer len */ bytestoread = buflen - bytesread; } /* Copy data to the read buffer */ if (bytestoread > 0) { /* Do incremental copy from this sector */ memcpy(&buffer[bytesread], &fs->fs_rwbuffer[sf->curroffset], bytestoread); bytesread += bytestoread; sf->filepos += bytestoread; sf->curroffset += bytestoread; } /* Test if we are at the end of the data in this sector */ if ((bytestoread == 0) || (sf->curroffset == fs->fs_llformat.availbytes)) { /* Set the next sector as the current sector */ sf->currsector = SMARTFS_NEXTSECTOR(header); sf->curroffset = sizeof(struct smartfs_chain_header_s); /* Test if at end of data */ if (sf->currsector == SMARTFS_ERASEDSTATE_16BIT) { /* No more data! Return what we have */ break; } } } /* Return the number of bytes we read */ ret = bytesread; errout_with_semaphore: smartfs_semgive(fs); return ret; } /**************************************************************************** * Name: smartfs_write ****************************************************************************/ static ssize_t smartfs_write(FAR struct file *filep, const char *buffer, size_t buflen) { struct inode *inode; struct smartfs_mountpt_s *fs; struct smartfs_ofile_s *sf; struct smart_read_write_s readwrite; struct smartfs_chain_header_s *header; size_t byteswritten; int ret; /* Sanity checks. I have seen the following assertion misfire if * CONFIG_DEBUG_MM is enabled while re-directing output to a * file. In this case, the debug output can get generated while * the file is being opened, FAT data structures are being allocated, * and things are generally in a perverse state. */ #ifdef CONFIG_DEBUG_MM if (filep->f_priv == NULL || filep->f_inode == NULL) { return -ENXIO; } #else DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); #endif /* Recover our private data from the struct file instance */ sf = filep->f_priv; inode = filep->f_inode; fs = inode->i_private; DEBUGASSERT(fs != NULL); /* Take the semaphore */ ret = smartfs_semtake(fs); if (ret < 0) { return (ssize_t)ret; } /* Test the permissions. Only allow write if the file was opened with * write flags. */ if ((sf->oflags & O_WROK) == 0) { ret = -EACCES; goto errout_with_semaphore; } /* Test if we opened for APPEND mode. If we did, then seek to the * end of the file. */ if (sf->oflags & O_APPEND) { ret = smartfs_seek_internal(fs, sf, 0, SEEK_END); if (ret < 0) { ret = -EIO; goto errout_with_semaphore; } } /* First test if we are overwriting an existing location or writing to * a new one. */ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; byteswritten = 0; while ((sf->filepos < sf->entry.datlen) && (buflen > 0)) { /* Overwriting data caused by a seek, etc. In this case, we need * to check if the write causes the file length to be extended * or not and update it accordingly. We will write data up to * the current end-of-file and then break, allowing the next while * loop below to write the additional data to the end of the file. */ readwrite.offset = sf->curroffset; readwrite.logsector = sf->currsector; readwrite.buffer = (uint8_t *) &buffer[byteswritten]; readwrite.count = fs->fs_llformat.availbytes - sf->curroffset; /* Limit the write based on available data to write */ if (readwrite.count > buflen) { readwrite.count = buflen; } /* Limit the write based on current file length */ if (readwrite.count > sf->entry.datlen - sf->filepos) { /* Limit the write length so we write to the current EOF. */ readwrite.count = sf->entry.datlen - sf->filepos; } /* Now perform the write. */ if (readwrite.count > 0) { ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); if (ret < 0) { ferr("ERROR: Error %d writing sector %d data\n", ret, sf->currsector); goto errout_with_semaphore; } /* Update our control variables */ sf->filepos += readwrite.count; sf->curroffset += readwrite.count; buflen -= readwrite.count; byteswritten += readwrite.count; } /* Test if we wrote to the end of the current sector */ if (sf->curroffset == fs->fs_llformat.availbytes) { /* Wrote to the end of the sector. Update to point to the * next sector for additional writes. First read the sector * header to get the sector chain info. */ readwrite.offset = 0; readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; readwrite.count = sizeof(struct smartfs_chain_header_s); ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); if (ret < 0) { ferr("ERROR: Error %d reading sector %d header\n", ret, sf->currsector); goto errout_with_semaphore; } /* Now get the chained sector info and reset the offset */ sf->curroffset = sizeof(struct smartfs_chain_header_s); sf->currsector = SMARTFS_NEXTSECTOR(header); } } /* Now append data to end of the file. */ while (buflen > 0) { /* We will fill up the current sector. Write data to * the current sector first. */ #ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER readwrite.count = fs->fs_llformat.availbytes - sf->curroffset; if (readwrite.count > buflen) { readwrite.count = buflen; } memcpy(&sf->buffer[sf->curroffset], &buffer[byteswritten], readwrite.count); sf->bflags |= SMARTFS_BFLAG_DIRTY; #else /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */ readwrite.offset = sf->curroffset; readwrite.logsector = sf->currsector; readwrite.buffer = (uint8_t *) &buffer[byteswritten]; readwrite.count = fs->fs_llformat.availbytes - sf->curroffset; if (readwrite.count > buflen) { /* Limit the write base on remaining bytes to write */ readwrite.count = buflen; } /* Perform the write */ if (readwrite.count > 0) { ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); if (ret < 0) { ferr("ERROR: Error %d writing sector %d data\n", ret, sf->currsector); goto errout_with_semaphore; } } #endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */ /* Update our control variables */ sf->entry.datlen += readwrite.count; sf->byteswritten += readwrite.count; sf->filepos += readwrite.count; sf->curroffset += readwrite.count; buflen -= readwrite.count; byteswritten += readwrite.count; /* Test if we wrote a full sector of data */ #ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER if (sf->curroffset == fs->fs_llformat.availbytes && buflen) { /* First get a new chained sector */ ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xffff); if (ret < 0) { ferr("ERROR: Error %d allocating new sector\n", ret); goto errout_with_semaphore; } /* Copy the new sector to the old one and chain it */ header = (struct smartfs_chain_header_s *) sf->buffer; *((uint16_t *) header->nextsector) = (uint16_t) ret; /* Now sync the file to write this sector out */ ret = smartfs_sync_internal(fs, sf); if (ret != OK) { goto errout_with_semaphore; } /* Record the new sector in our tracking variables and * reset the offset to "zero". */ if (sf->currsector == SMARTFS_NEXTSECTOR(header)) { /* Error allocating logical sector! */ ferr("ERROR: Duplicate logical sector %d\n", sf->currsector); } sf->bflags = SMARTFS_BFLAG_DIRTY; sf->currsector = SMARTFS_NEXTSECTOR(header); sf->curroffset = sizeof(struct smartfs_chain_header_s); memset(sf->buffer, CONFIG_SMARTFS_ERASEDSTATE, fs->fs_llformat.availbytes); header->type = SMARTFS_DIRENT_TYPE_FILE; } #else /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */ if (sf->curroffset == fs->fs_llformat.availbytes) { /* Sync the file to write this sector out */ ret = smartfs_sync_internal(fs, sf); if (ret != OK) { goto errout_with_semaphore; } /* Allocate a new sector if needed */ if (buflen > 0) { /* Allocate a new sector */ ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xffff); if (ret < 0) { ferr("ERROR: Error %d allocating new sector\n", ret); goto errout_with_semaphore; } /* Copy the new sector to the old one and chain it */ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; *((uint16_t *) header->nextsector) = (uint16_t) ret; readwrite.offset = offsetof(struct smartfs_chain_header_s, nextsector); readwrite.buffer = (uint8_t *) header->nextsector; readwrite.count = sizeof(uint16_t); ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); if (ret < 0) { ferr("ERROR: Error %d writing next sector\n", ret); goto errout_with_semaphore; } /* Record the new sector in our tracking variables and * reset the offset to "zero". */ if (sf->currsector == SMARTFS_NEXTSECTOR(header)) { /* Error allocating logical sector! */ ferr("ERROR: Duplicate logical sector %d\n", sf->currsector); } sf->currsector = SMARTFS_NEXTSECTOR(header); sf->curroffset = sizeof(struct smartfs_chain_header_s); } } #endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */ } ret = byteswritten; errout_with_semaphore: smartfs_semgive(fs); return ret; } /**************************************************************************** * Name: smartfs_seek ****************************************************************************/ static off_t smartfs_seek(FAR struct file *filep, off_t offset, int whence) { struct inode *inode; struct smartfs_mountpt_s *fs; struct smartfs_ofile_s *sf; int ret; /* Sanity checks */ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); /* Recover our private data from the struct file instance */ sf = filep->f_priv; inode = filep->f_inode; fs = inode->i_private; DEBUGASSERT(fs != NULL); /* Take the semaphore */ ret = smartfs_semtake(fs); if (ret < 0) { return (off_t)ret; } /* Call our internal routine to perform the seek */ ret = smartfs_seek_internal(fs, sf, offset, whence); if (ret >= 0) { filep->f_pos = ret; } smartfs_semgive(fs); return ret; } /**************************************************************************** * Name: smartfs_ioctl ****************************************************************************/ static int smartfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { /* We don't use any ioctls */ return -ENOSYS; } /**************************************************************************** * Name: smartfs_sync * * Description: Synchronize the file state on disk to match internal, in- * memory state. * ****************************************************************************/ static int smartfs_sync(FAR struct file *filep) { struct inode *inode; struct smartfs_mountpt_s *fs; struct smartfs_ofile_s *sf; int ret; /* Sanity checks */ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); /* Recover our private data from the struct file instance */ sf = filep->f_priv; inode = filep->f_inode; fs = inode->i_private; DEBUGASSERT(fs != NULL); /* Take the semaphore */ ret = smartfs_semtake(fs); if (ret < 0) { return ret; } ret = smartfs_sync_internal(fs, sf); smartfs_semgive(fs); return ret; } /**************************************************************************** * Name: smart_dup * * Description: Duplicate open file data in the new file structure. * ****************************************************************************/ static int smartfs_dup(FAR const struct file *oldp, FAR struct file *newp) { struct smartfs_ofile_s *sf; finfo("Dup %p->%p\n", oldp, newp); /* Sanity checks */ DEBUGASSERT(oldp->f_priv != NULL && newp->f_priv == NULL && newp->f_inode != NULL); /* Recover our private data from the struct file instance */ sf = oldp->f_priv; DEBUGASSERT(sf != NULL); /* Just increment the reference count on the ofile */ sf->crefs++; newp->f_priv = (FAR void *)sf; return OK; } /**************************************************************************** * Name: smartfs_fstat * * Description: * Obtain information about an open file associated with the file * descriptor 'fd', and will write it to the area pointed to by 'buf'. * ****************************************************************************/ static int smartfs_fstat(FAR const struct file *filep, FAR struct stat *buf) { FAR struct inode *inode; FAR struct smartfs_mountpt_s *fs; FAR struct smartfs_ofile_s *sf; int ret; DEBUGASSERT(filep != NULL && buf != NULL); /* Recover our private data from the struct file instance */ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); sf = filep->f_priv; inode = filep->f_inode; fs = inode->i_private; DEBUGASSERT(fs != NULL); /* Take the semaphore */ ret = smartfs_semtake(fs); if (ret < 0) { return ret; } /* Return information about the directory entry in the stat structure */ smartfs_stat_common(fs, &sf->entry, buf); smartfs_semgive(fs); return OK; } /**************************************************************************** * Name: smartfs_truncate * * Description: * Set the length of the open, regular file associated with the file * structure 'filep' to 'length'. * ****************************************************************************/ static int smartfs_truncate(FAR struct file *filep, off_t length) { FAR struct inode *inode; FAR struct smartfs_mountpt_s *fs; FAR struct smartfs_ofile_s *sf; off_t oldsize; int ret; DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); /* Recover our private data from the struct file instance */ sf = filep->f_priv; inode = filep->f_inode; fs = inode->i_private; DEBUGASSERT(fs != NULL); /* Take the semaphore */ ret = smartfs_semtake(fs); if (ret < 0) { return ret; } /* Test the permissions. Only allow truncation if the file was opened with * write flags. */ if ((sf->oflags & O_WROK) == 0) { ret = -EACCES; goto errout_with_semaphore; } /* Are we shrinking the file? Or extending it? */ oldsize = sf->entry.datlen; if (oldsize == length) { /* Let's not and say we did */ ret = OK; } else if (oldsize > length) { /* We are shrinking the file */ ret = smartfs_shrinkfile(fs, sf, length); } else { /* Otherwise we are extending the file. This is essentially the same * as a write except that (1) we write zeros and (2) we don't update * the file position. */ ret = smartfs_extendfile(fs, sf, length); } errout_with_semaphore: /* Relinquish exclusive access */ smartfs_semgive(fs); return ret; } /**************************************************************************** * Name: smartfs_opendir * * Description: Open a directory for read access * ****************************************************************************/ static int smartfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s *dir) { struct smartfs_mountpt_s *fs; int ret; struct smartfs_entry_s entry; uint16_t parentdirsector; const char *filename; /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover our private data from the inode instance */ fs = mountpt->i_private; /* Take the semaphore */ ret = smartfs_semtake(fs); if (ret < 0) { return ret; } /* Search for the path on the volume */ entry.name = NULL; ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector, &filename); if (ret < 0) { goto errout_with_semaphore; } /* Populate our private data in the fs_dirent_s struct */ dir->u.smartfs.fs_firstsector = entry.firstsector; dir->u.smartfs.fs_currsector = entry.firstsector; dir->u.smartfs.fs_curroffset = sizeof(struct smartfs_chain_header_s); ret = OK; errout_with_semaphore: /* If space for the entry name was allocated, then free it */ if (entry.name != NULL) { kmm_free(entry.name); entry.name = NULL; } smartfs_semgive(fs); return ret; } /**************************************************************************** * Name: smartfs_readdir * * Description: Read the next directory entry * ****************************************************************************/ static int smartfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir) { struct smartfs_mountpt_s *fs; int ret; uint16_t entrysize; uint16_t namelen; struct smartfs_chain_header_s *header; struct smart_read_write_s readwrite; struct smartfs_entry_header_s *entry; /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Recover our private data from the inode instance */ fs = mountpt->i_private; /* Take the semaphore */ ret = smartfs_semtake(fs); if (ret < 0) { return ret; } /* Read sectors and search entries until one found or no more */ entrysize = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize; while (dir->u.smartfs.fs_currsector != SMARTFS_ERASEDSTATE_16BIT) { /* Read the logical sector */ readwrite.logsector = dir->u.smartfs.fs_currsector; readwrite.count = fs->fs_llformat.availbytes; readwrite.buffer = (uint8_t *)fs->fs_rwbuffer; readwrite.offset = 0; ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); if (ret < 0) { goto errout_with_semaphore; } /* Now search for entries, starting at curroffset */ while (dir->u.smartfs.fs_curroffset < ret) { /* Point to next entry */ entry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[ dir->u.smartfs.fs_curroffset]; /* Test if this entry is valid and active */ if (((entry->flags & SMARTFS_DIRENT_EMPTY) == (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) || ((entry->flags & SMARTFS_DIRENT_ACTIVE) != (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE))) { /* This entry isn't valid, skip it */ dir->u.smartfs.fs_curroffset += entrysize; entry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[dir->u.smartfs.fs_curroffset]; continue; } /* Entry found! Report it */ if ((entry->flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_DIR) { dir->fd_dir.d_type = DTYPE_DIRECTORY; } else { dir->fd_dir.d_type = DTYPE_FILE; } /* Copy the entry name to dirent */ namelen = fs->fs_llformat.namesize; if (namelen > NAME_MAX) { namelen = NAME_MAX; } memset(dir->fd_dir.d_name, 0, namelen); strncpy(dir->fd_dir.d_name, entry->name, namelen); /* Now advance to the next entry */ dir->u.smartfs.fs_curroffset += entrysize; if (dir->u.smartfs.fs_curroffset + entrysize >= fs->fs_llformat.availbytes) { /* We advanced past the end of the sector. Go to next sector */ dir->u.smartfs.fs_curroffset = sizeof(struct smartfs_chain_header_s); header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; dir->u.smartfs.fs_currsector = SMARTFS_NEXTSECTOR(header); } /* Now exit */ ret = OK; goto errout_with_semaphore; } /* No more entries in this sector. Move on to next sector and * continue the search. If no more sectors, then we are all * done and will report ENOENT. */ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; dir->u.smartfs.fs_curroffset = sizeof(struct smartfs_chain_header_s); dir->u.smartfs.fs_currsector = SMARTFS_NEXTSECTOR(header); } /* If we arrive here, then there are no more entries */ ret = -ENOENT; errout_with_semaphore: smartfs_semgive(fs); return ret; } /**************************************************************************** * Name: smartfs_rewindir * * Description: Reset directory read to the first entry * ****************************************************************************/ static int smartfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir) { int ret = OK; /* Sanity checks */ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); /* Reset the directory to the first entry */ dir->u.smartfs.fs_currsector = dir->u.smartfs.fs_firstsector; dir->u.smartfs.fs_curroffset = sizeof(struct smartfs_chain_header_s); return ret; } /**************************************************************************** * Name: smartfs_bind * * Description: This implements a portion of the mount operation. This * function allocates and initializes the mountpoint private data and * binds the blockdriver inode to the filesystem private data. The final * binding of the private data (containing the blockdriver) to the * mountpoint is performed by mount(). * ****************************************************************************/ static int smartfs_bind(FAR struct inode *blkdriver, const void *data, void **handle) { struct smartfs_mountpt_s *fs; int ret; /* Open the block driver */ if (!blkdriver || !blkdriver->u.i_bops) { return -ENODEV; } if (blkdriver->u.i_bops->open && blkdriver->u.i_bops->open(blkdriver) != OK) { return -ENODEV; } /* Create an instance of the mountpt state structure */ fs = (struct smartfs_mountpt_s *) kmm_zalloc(sizeof(struct smartfs_mountpt_s)); if (!fs) { return -ENOMEM; } /* If the global semaphore hasn't been initialized, then * initialized it now. */ fs->fs_sem = &g_sem; if (!g_seminitialized) { nxsem_init(&g_sem, 0, 0); /* Initialize the semaphore that controls access */ g_seminitialized = TRUE; } else { /* Take the semaphore for the mount */ ret = smartfs_semtake(fs); if (ret < 0) { kmm_free(fs); return ret; } } /* Initialize the allocated mountpt state structure. The filesystem is * responsible for one reference ont the blkdriver inode and does not * have to addref() here (but does have to release in ubind(). */ fs->fs_blkdriver = blkdriver; /* Save the block driver reference */ fs->fs_head = NULL; /* Now perform the mount. */ ret = smartfs_mount(fs, true); if (ret != 0) { smartfs_semgive(fs); kmm_free(fs); return ret; } *handle = (FAR void *)fs; smartfs_semgive(fs); return OK; } /**************************************************************************** * Name: smartfs_unbind * * Description: This implements the filesystem portion of the umount * operation. * ****************************************************************************/ static int smartfs_unbind(FAR void *handle, FAR struct inode **blkdriver, unsigned int flags) { FAR struct smartfs_mountpt_s *fs = (FAR struct smartfs_mountpt_s *)handle; int ret; if (!fs) { return -EINVAL; } /* Check if there are sill any files opened on the filesystem. */ ret = OK; /* Assume success */ ret = smartfs_semtake(fs); if (ret < 0) { return ret; } if (fs->fs_head != NULL) { /* We cannot unmount now.. there are open files */ smartfs_semgive(fs); /* This implementation currently only supports unmounting if there are * no open file references. */ return (flags != 0) ? -ENOSYS : -EBUSY; } else { /* Unmount ... close the block driver */ ret = smartfs_unmount(fs); } smartfs_semgive(fs); kmm_free(fs); return ret; } /**************************************************************************** * Name: smartfs_statfs * * Description: Return filesystem statistics * ****************************************************************************/ static int smartfs_statfs(struct inode *mountpt, struct statfs *buf) { struct smartfs_mountpt_s *fs; int ret; /* Sanity checks */ DEBUGASSERT(mountpt && mountpt->i_private); /* Get the mountpoint private data from the inode structure */ fs = mountpt->i_private; ret = smartfs_semtake(fs); if (ret < 0) { return ret; } /* Implement the logic!! */ memset(buf, 0, sizeof(struct statfs)); buf->f_type = SMARTFS_MAGIC; /* Re-request the low-level format info to update free blocks */ ret = FS_IOCTL(fs, BIOC_GETFORMAT, (unsigned long) &fs->fs_llformat); buf->f_namelen = fs->fs_llformat.namesize; buf->f_bsize = fs->fs_llformat.sectorsize; buf->f_blocks = fs->fs_llformat.nsectors; if (buf->f_blocks == 65535) { buf->f_blocks++; } buf->f_bfree = fs->fs_llformat.nfreesectors; buf->f_bavail = fs->fs_llformat.nfreesectors; buf->f_files = 0; buf->f_ffree = fs->fs_llformat.nfreesectors; smartfs_semgive(fs); return ret; } /**************************************************************************** * Name: smartfs_unlink * * Description: Remove a file * ****************************************************************************/ static int smartfs_unlink(struct inode *mountpt, const char *relpath) { struct smartfs_mountpt_s *fs; int ret; struct smartfs_entry_s entry; const char *filename; uint16_t parentdirsector; /* Sanity checks */ DEBUGASSERT(mountpt && mountpt->i_private); /* Get the mountpoint private data from the inode structure */ fs = mountpt->i_private; ret = smartfs_semtake(fs); if (ret < 0) { return ret; } /* Locate the directory entry for this path */ entry.name = NULL; ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector, &filename); if (ret == OK) { /* The name exists -- validate it is a file, not a dir */ if ((entry.flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_DIR) { ret = -EISDIR; goto errout_with_semaphore; } /* TODO: Need to check permissions? */ /* Okay, we are clear to delete the file. Use the deleteentry routine. */ smartfs_deleteentry(fs, &entry); } else { /* Just report the error */ goto errout_with_semaphore; } ret = OK; errout_with_semaphore: if (entry.name != NULL) { kmm_free(entry.name); } smartfs_semgive(fs); return ret; } /**************************************************************************** * Name: smartfs_mkdir * * Description: Create a directory * ****************************************************************************/ static int smartfs_mkdir(struct inode *mountpt, const char *relpath, mode_t mode) { struct smartfs_mountpt_s *fs; int ret; struct smartfs_entry_s entry; uint16_t parentdirsector; const char *filename; /* Sanity checks */ DEBUGASSERT(mountpt && mountpt->i_private); /* Get the mountpoint private data from the inode structure */ fs = mountpt->i_private; ret = smartfs_semtake(fs); if (ret < 0) { return ret; } /* Locate the directory entry for this path */ entry.name = NULL; ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector, &filename); /* Three possibililities: (1) a node exists for the relpath and * dirinfo describes the directory entry of the entity, (2) the * node does not exist, or (3) some error occurred. */ if (ret == OK) { /* The name exists -- can't create */ ret = -EEXIST; goto errout_with_semaphore; } else if (ret == -ENOENT) { /* It doesn't exist ... we can create it, but only if we have * the right permissions and if the parentdirsector is valid. */ if (parentdirsector == 0xffff) { /* Invalid entry in the path (non-existent dir segment) */ goto errout_with_semaphore; } /* Check mode */ /* Create the directory */ ret = smartfs_createentry(fs, parentdirsector, filename, SMARTFS_DIRENT_TYPE_DIR, mode, &entry, 0xffff, NULL); if (ret != OK) { goto errout_with_semaphore; } ret = OK; } errout_with_semaphore: if (entry.name != NULL) { /* Free the filename space allocation */ kmm_free(entry.name); entry.name = NULL; } smartfs_semgive(fs); return ret; } /**************************************************************************** * Name: smartfs_rmdir * * Description: Remove a directory * ****************************************************************************/ int smartfs_rmdir(struct inode *mountpt, const char *relpath) { struct smartfs_mountpt_s *fs; int ret; struct smartfs_entry_s entry; const char *filename; uint16_t parentdirsector; /* Sanity checks */ DEBUGASSERT(mountpt && mountpt->i_private); /* Get the mountpoint private data from the inode structure */ fs = mountpt->i_private; /* Take the semaphore */ ret = smartfs_semtake(fs); if (ret < 0) { return ret; } /* Locate the directory entry for this path */ entry.name = NULL; ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector, &filename); if (ret == OK) { /* The name exists -- validate it is a dir, not a file */ if ((entry.flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_FILE) { ret = -ENOTDIR; goto errout_with_semaphore; } /* TODO: Need to check permissions? */ /* Check if the directory is empty */ ret = smartfs_countdirentries(fs, &entry); if (ret < 0) { goto errout_with_semaphore; } /* Only continue if there are zero entries in the directory */ if (ret != 0) { ret = -ENOTEMPTY; goto errout_with_semaphore; } /* Okay, we are clear to delete the directory. Use the deleteentry routine. */ ret = smartfs_deleteentry(fs, &entry); if (ret < 0) { goto errout_with_semaphore; } } else { /* Just report the error */ goto errout_with_semaphore; } ret = OK; errout_with_semaphore: if (entry.name != NULL) { kmm_free(entry.name); } smartfs_semgive(fs); return ret; } /**************************************************************************** * Name: smartfs_rename * * Description: Rename a file or directory * ****************************************************************************/ int smartfs_rename(struct inode *mountpt, const char *oldrelpath, const char *newrelpath) { struct smartfs_mountpt_s *fs; int ret; struct smartfs_entry_s oldentry; uint16_t oldparentdirsector; const char *oldfilename; struct smartfs_entry_s newentry; uint16_t newparentdirsector; const char *newfilename; mode_t mode; uint16_t type; struct smartfs_entry_header_s *direntry; struct smart_read_write_s readwrite; /* Sanity checks */ DEBUGASSERT(mountpt && mountpt->i_private); /* Get the mountpoint private data from the inode structure */ fs = mountpt->i_private; ret = smartfs_semtake(fs); if (ret < 0) { return ret; } /* Search for old entry to validate it exists */ oldentry.name = NULL; newentry.name = NULL; ret = smartfs_finddirentry(fs, &oldentry, oldrelpath, &oldparentdirsector, &oldfilename); if (ret < 0) { goto errout_with_semaphore; } /* Search for the new entry and validate it DOESN'T exist. */ ret = smartfs_finddirentry(fs, &newentry, newrelpath, &newparentdirsector, &newfilename); if (ret == OK) { /* It is an error if the directory entry at newrelpath already * exists. The necessary steps to avoid this case should have been * handled by higher level logic in the VFS. */ ret = -EEXIST; goto errout_with_semaphore; } /* Test if the new parent directory is valid */ if (newparentdirsector != 0xffff) { /* We can move to the given parent directory */ mode = oldentry.flags & SMARTFS_DIRENT_MODE; type = oldentry.flags & SMARTFS_DIRENT_TYPE; ret = smartfs_createentry(fs, newparentdirsector, newfilename, type, mode, &newentry, oldentry.firstsector, NULL); if (ret != OK) { goto errout_with_semaphore; } /* Now mark the old entry as inactive */ readwrite.logsector = oldentry.dsector; readwrite.offset = 0; readwrite.count = fs->fs_llformat.availbytes; readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); if (ret < 0) { ferr("ERROR: Error %d reading sector %d data\n", ret, oldentry.dsector); goto errout_with_semaphore; } direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[oldentry.doffset]; #if CONFIG_SMARTFS_ERASEDSTATE == 0xff direntry->flags &= ~SMARTFS_DIRENT_ACTIVE; #else direntry->flags |= SMARTFS_DIRENT_ACTIVE; #endif /* Now write the updated flags back to the device */ readwrite.offset = oldentry.doffset; readwrite.count = sizeof(direntry->flags); readwrite.buffer = (uint8_t *) &direntry->flags; ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); if (ret < 0) { ferr("ERROR: Error %d writing flag bytes for sector %d\n", ret, readwrite.logsector); goto errout_with_semaphore; } } else { /* Trying to create in a directory that doesn't exist */ ret = -ENOENT; goto errout_with_semaphore; } ret = OK; errout_with_semaphore: if (oldentry.name != NULL) { kmm_free(oldentry.name); oldentry.name = NULL; } if (newentry.name != NULL) { kmm_free(newentry.name); newentry.name = NULL; } smartfs_semgive(fs); return ret; } /**************************************************************************** * Name: smartfs_stat_common * * Description: * Return information about a directory entry * ****************************************************************************/ static void smartfs_stat_common(FAR struct smartfs_mountpt_s *fs, FAR struct smartfs_entry_s *entry, FAR struct stat *buf) { /* Initialize the stat structure */ memset(buf, 0, sizeof(struct stat)); if (entry->firstsector == fs->fs_rootsector) { /* It's directory name of the mount point */ buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR | S_IWOTH | S_IWGRP | S_IWUSR; } else { /* Mask out the file type */ buf->st_mode = entry->flags & ~S_IFMT; /* Add the file type based on the SmartFS entry flags */ if ((entry->flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_DIR) { buf->st_mode |= S_IFDIR; } else { buf->st_mode |= S_IFREG; } buf->st_size = entry->datlen; buf->st_blksize = fs->fs_llformat.availbytes; buf->st_blocks = (buf->st_size + buf->st_blksize - 1) / buf->st_blksize; } } /**************************************************************************** * Name: smartfs_stat * * Description: * Return information about a file or directory * ****************************************************************************/ static int smartfs_stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf) { FAR struct smartfs_mountpt_s *fs; FAR struct smartfs_entry_s entry; FAR const char *filename; uint16_t parentdirsector; int ret; /* Sanity checks */ DEBUGASSERT(mountpt && mountpt->i_private); /* Get the mountpoint private data from the inode structure */ fs = mountpt->i_private; ret = smartfs_semtake(fs); if (ret < 0) { return ret; } /* Find the directory entry corresponding to relpath */ entry.name = NULL; ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector, &filename); if (ret < 0) { goto errout_with_semaphore; } /* Return information about the directory entry in the stat structure */ smartfs_stat_common(fs, &entry, buf); ret = OK; errout_with_semaphore: if (entry.name != NULL) { kmm_free(entry.name); entry.name = NULL; } smartfs_semgive(fs); return ret; } /**************************************************************************** * Public Functions ****************************************************************************/