/**************************************************************************** * 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 #include "smartfs.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /**************************************************************************** * Private Types ****************************************************************************/ /**************************************************************************** * 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_opendir(struct inode *mountpt, const char *relpath, struct fs_dirent_s *dir); static int smartfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir); static int smartfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir); static int smartfs_bind(FAR struct inode *blkdriver, const void *data, void **handle); static int smartfs_unbind(void *handle, FAR struct inode **blkdriver); static int smartfs_statfs(struct inode *mountpt, struct statfs *buf); static int smartfs_unlink(struct inode *mountpt, const char *relpath); static int smartfs_mkdir(struct inode *mountpt, const char *relpath, mode_t mode); static int smartfs_rmdir(struct inode *mountpt, const char *relpath); static int smartfs_rename(struct inode *mountpt, const char *oldrelpath, const char *newrelpath); static int smartfs_stat(struct inode *mountpt, const char *relpath, struct stat *buf); static off_t smartfs_seek_internal(struct smartfs_mountpt_s *fs, struct smartfs_ofile_s *sf, off_t offset, int whence); /**************************************************************************** * Private Variables ****************************************************************************/ static uint8_t g_seminitialized = FALSE; static sem_t g_sem; /**************************************************************************** * Public Variables ****************************************************************************/ /* 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_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; /* 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 */ smartfs_semtake(fs); /* 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; } sf->entry.name = NULL; ret = smartfs_finddirentry(fs, &sf->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 -- 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 */ /* The file exists. Check if we are opening it for O_CREAT or * O_TRUNC mode and delete the sector chain if we are. */ if ((oflags & (O_CREAT | O_TRUNC)) != 0) { /* Don't truncate if open for APPEND */ if (!(oflags & O_APPEND)) { /* Truncate the file as part of the open */ ret = smartfs_truncatefile(fs, &sf->entry); 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); 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; /* 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; } 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; /* 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 */ smartfs_semtake(fs); /* 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; } 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 */ smartfs_semtake(fs); /* 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 curent 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) { fdbg("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_sync_internal * * Description: Synchronize the file state on disk to match internal, in- * memory state. * ****************************************************************************/ static int smartfs_sync_internal(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; /* Test if we have written bytes to the current sector that * need to be recorded in the chain header's used bytes field. */ if (sf->byteswritten > 0) { fvdbg("Syncing sector %d\n", sf->currsector); /* Read the existing sector used bytes value */ readwrite.logsector = sf->currsector; 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) { fdbg("Error %d reading sector %d data\n", ret, sf->currsector); goto errout; } /* Add new byteswritten to existing value */ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; if (*((uint16_t *) header->used) == SMARTFS_ERASEDSTATE_16BIT) { *((uint16_t *) header->used) = sf->byteswritten; } else { *((uint16_t *) header->used) += sf->byteswritten; } readwrite.offset = offsetof(struct smartfs_chain_header_s, used); readwrite.count = sizeof(uint16_t); readwrite.buffer = (uint8_t *) &fs->fs_rwbuffer[readwrite.offset]; ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); if (ret < 0) { fdbg("Error %d writing used bytes for sector %d\n", ret, sf->currsector); goto errout; } sf->byteswritten = 0; } errout: 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 */ smartfs_semtake(fs); /* 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; } /* 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) { fdbg("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) { fdbg("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. */ 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) { fdbg("Error %d writing sector %d data\n", ret, sf->currsector); goto errout_with_semaphore; } /* 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 */ if (sf->curroffset == fs->fs_llformat.availbytes) { /* Sync the file to write this sector out */ smartfs_sync_internal(fs, sf); /* Allocate a new sector if needed */ if (buflen > 0) { /* Allocate a new sector */ ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xFFFF); if (ret < 0) { fdbg("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) { fdbg("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". */ sf->currsector = SMARTFS_NEXTSECTOR(header); sf->curroffset = sizeof(struct smartfs_chain_header_s); } } } ret = byteswritten; errout_with_semaphore: smartfs_semgive(fs); return ret; } /**************************************************************************** * Name: smartfs_seek_internal * * Description: Performs the logic of the seek function. This is an internal * function because it does not provide semaphore protection and * therefore must be called from one of the other public * interface routines (open, seek, etc.). * ****************************************************************************/ static off_t smartfs_seek_internal(struct smartfs_mountpt_s *fs, struct smartfs_ofile_s *sf, off_t offset, int whence) { struct smart_read_write_s readwrite; struct smartfs_chain_header_s *header; int ret; off_t newpos; off_t sectorstartpos; /* Test if this is a seek to get the current file pos */ if ((whence == SEEK_CUR) && (offset == 0)) { return sf->filepos; } /* Test if we need to sync the file */ if (sf->byteswritten > 0) { /* Perform a sync */ smartfs_sync_internal(fs, sf); } /* Calculate the file position to seek to based on current position */ switch (whence) { case SEEK_SET: default: newpos = offset; break; case SEEK_CUR: newpos = sf->filepos + offset; break; case SEEK_END: newpos = sf->entry.datlen - offset; break; } /* Ensure newpos is in range */ if (newpos < 0) { newpos = 0; } if (newpos > sf->entry.datlen) { newpos = sf->entry.datlen; } /* Now perform the seek. Test if we are seeking within the current * sector and can skip the search to save time. */ sectorstartpos = sf->filepos - (sf->curroffset - sizeof(struct smartfs_chain_header_s)); if (newpos >= sectorstartpos && newpos < sectorstartpos + fs->fs_llformat.availbytes - sizeof(struct smartfs_chain_header_s)) { /* Seeking within the current sector. Just update the offset */ sf->curroffset = sizeof(struct smartfs_chain_header_s) + newpos-sectorstartpos; sf->filepos = newpos; return newpos; } /* Nope, we have to search for the sector and offset. If the new pos is greater * than the current pos, then we can start from the beginning of the current * sector, otherwise we have to start from the beginning of the file. */ if (newpos > sf->filepos) { sf->filepos = sectorstartpos; } else { sf->currsector = sf->entry.firstsector; sf->filepos = 0; } header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; while ((sf->currsector != SMARTFS_ERASEDSTATE_16BIT) && (sf->filepos + fs->fs_llformat.availbytes - sizeof(struct smartfs_chain_header_s) < newpos)) { /* Read the sector's header */ readwrite.logsector = sf->currsector; readwrite.offset = 0; readwrite.count = sizeof(struct smartfs_chain_header_s); readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); if (ret < 0) { fdbg("Error %d reading sector %d header\n", ret, sf->currsector); goto errout; } /* Point to next sector and update filepos */ sf->currsector = SMARTFS_NEXTSECTOR(header); sf->filepos += SMARTFS_USED(header); } /* Now calculate the offset */ sf->curroffset = sizeof(struct smartfs_chain_header_s) + newpos - sf->filepos; sf->filepos = newpos; return newpos; errout: 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 */ smartfs_semtake(fs); /* 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 */ smartfs_semtake(fs); 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; fvdbg("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_opendir * * Description: Open a directory for read access * ****************************************************************************/ static int smartfs_opendir(struct inode *mountpt, const char *relpath, 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 */ smartfs_semtake(fs); /* 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 */ smartfs_semtake(fs); /* 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 >= 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) { sem_init(&g_sem, 0, 0); /* Initialize the semaphore that controls access */ g_seminitialized = TRUE; } else { /* Take the semaphore for the mount */ smartfs_semtake(fs); } /* 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) { kmm_free(fs); smartfs_semgive(fs); return ret; } *handle = (void*)fs; smartfs_semgive(fs); return OK; } /**************************************************************************** * Name: smartfs_unbind * * Description: This implements the filesystem portion of the umount * operation. * ****************************************************************************/ static int smartfs_unbind(void *handle, FAR struct inode **blkdriver) { struct smartfs_mountpt_s *fs = (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 */ smartfs_semtake(fs); if (fs->fs_head != NULL) { /* We cannot unmount now.. there are open files */ smartfs_semgive(fs); ret = -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; smartfs_semtake(fs); /* 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; 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; smartfs_semtake(fs); /* 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: 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; smartfs_semtake(fs); /* 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-existant dir segment) */ goto errout_with_semaphore; } /* Check mode */ /* Create the directory */ ret = smartfs_createentry(fs, parentdirsector, filename, SMARTFS_DIRENT_TYPE_DIR, mode, &entry, 0xFFFF); 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 */ smartfs_semtake(fs); /* 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 emtpy */ 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: 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; uint16_t sector; uint16_t offset; uint16_t entrysize; 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; smartfs_semtake(fs); /* 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, unless we * are copying to a directory and keeping the same filename, such as: * * mv /mntpoint/somefile.txt /mntpoint/somedir * * in which case, we need to validate the /mntpoint/somedir/somefile.txt * doens't exsit. */ ret = smartfs_finddirentry(fs, &newentry, newrelpath, &newparentdirsector, &newfilename); if (ret == OK) { /* Test if it's a file. If it is, then it's an error */ if ((newentry.flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_FILE) { ret = -EEXIST; goto errout_with_semaphore; } /* Nope, it's a directory. Now search the directory for oldfilename */ sector = newentry.firstsector; while (sector != SMARTFS_ERASEDSTATE_16BIT) { /* Read the next sector of diretory entries */ readwrite.logsector = sector; 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) { fdbg("Error %d reading sector %d data\n", ret, oldentry.dsector); goto errout_with_semaphore; } /* Search for newfilename in this sector */ offset = sizeof(struct smartfs_chain_header_s); entrysize = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize; while (offset + entrysize < fs->fs_llformat.availbytes) { /* Test the next entry */ direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset]; if (((direntry->flags & SMARTFS_DIRENT_EMPTY) == (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) || ((direntry->flags & SMARTFS_DIRENT_ACTIVE) != (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE))) { /* This entry isn't valid, skip it */ offset += entrysize; continue; } /* Test if this entry matches newfilename */ if (strcmp(newfilename, direntry->name) == 0) { /* Uh-oh, looks like the entry already exists */ ret = -EEXIST; goto errout_with_semaphore; } offset += entrysize; } /* Chain to next sector */ sector = SMARTFS_NEXTSECTOR(((struct smartfs_chain_header_s *)fs->fs_rwbuffer)); } /* Okay, we will create newfilename in the newentry directory */ newparentdirsector = newentry.firstsector; newfilename = oldfilename; } /* 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); 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) { fdbg("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) { fdbg("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 * * Description: Return information about a file or directory * ****************************************************************************/ static int smartfs_stat(struct inode *mountpt, const char *relpath, struct stat *buf) { struct smartfs_mountpt_s *fs; struct smartfs_entry_s entry; int ret; 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; smartfs_semtake(fs); /* 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; } /* 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; ret = OK; goto errout_with_semaphore; } buf->st_mode = entry.flags & 0xFFF; 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; buf->st_atime = 0; buf->st_ctime = 0; ret = OK; errout_with_semaphore: if (entry.name != NULL) { kmm_free(entry.name); entry.name = NULL; } smartfs_semgive(fs); return ret; } /**************************************************************************** * Public Functions ****************************************************************************/