/**************************************************************************** * fs/tmpfs/fs_tmpfs.c * * Copyright (C) 2015 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * 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 "fs_tmpfs.h" #ifndef CONFIG_DISABLE_MOUNTPOINT /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define tmpfs_unlock(fs) (sem_post(&fs->tfs_exclsem)) #define tmpfs_lock_file(tfo) \ (tmpfs_lock_object((FAR struct tmpfs_object_s *)tfo)) #define tmpfs_lock_directory(tdo) \ (tmpfs_lock_object((FAR struct tmpfs_object_s *)tdo)) #define tmpfs_unlock_object(to) (sem_post(&to->to_exclsem)) #define tmpfs_unlock_file(tfo) (sem_post(&tfo->tfo_exclsem)) #define tmpfs_unlock_directory(tdo) (sem_post(&tdo->tdo_exclsem)) /**************************************************************************** * Private Types ****************************************************************************/ /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* TMPFS helpers */ static void tmpfs_lock(FAR struct tmpfs_s *fs); static void tmpfs_lock_object(FAR struct tmpfs_object_s *to); static int tmpfs_realloc_directory(FAR struct tmpfs_directory_s **tdo, unsigned int nentries); static int tmpfs_realloc_file(FAR struct tmpfs_file_s **tfo, size_t newsize); static int tmpfs_find_dirent(FAR struct tmpfs_directory_s *tdo, FAR const char *name); static int tmpfs_remove_dirent(FAR struct tmpfs_directory_s *tdo, FAR const char *name); static int tmpfs_add_dirent(FAR struct tmpfs_directory_s **tdo, FAR struct tmpfs_object_s *to, FAR const char *name); static FAR struct tmpfs_file_s *tmpfs_alloc_file(void); static int tmpfs_create_file(FAR struct tmpfs_s *fs, FAR const char *relpath, FAR struct tmpfs_file_s **tfo); static int tmpfs_find_object(FAR struct tmpfs_s *fs, FAR const char *relpath, FAR struct tmpfs_object_s **object, FAR struct tmpfs_directory_s **parent); static int tmpfs_find_file(FAR struct tmpfs_s *fs, FAR const char *relpath, FAR struct tmpfs_file_s **tfo, FAR struct tmpfs_directory_s **parent); static int tmpfs_find_directory(FAR struct tmpfs_s *fs, FAR const char *relpath, FAR struct tmpfs_directory_s **tdo, FAR struct tmpfs_directory_s **parent); /* File system operations */ static int tmpfs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode); static int tmpfs_close(FAR struct file *filep); static ssize_t tmpfs_read(FAR struct file *filep, FAR char *buffer, size_t buflen); static ssize_t tmpfs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); static off_t tmpfs_seek(FAR struct file *filep, off_t offset, int whence); static int tmpfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg); static int tmpfs_dup(FAR const struct file *oldp, FAR struct file *newp); static int tmpfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s *dir); static int tmpfs_closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int tmpfs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int tmpfs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int tmpfs_bind(FAR struct inode *blkdriver, FAR const void *data, FAR void **handle); static int tmpfs_unbind(FAR void *handle, FAR struct inode **blkdriver, unsigned int flags); static int tmpfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf); static int tmpfs_unlink(FAR struct inode *mountpt, FAR const char *relpath); static int tmpfs_mkdir(FAR struct inode *mountpt, FAR const char *relpath, mode_t mode); static int tmpfs_rmdir(FAR struct inode *mountpt, FAR const char *relpath); static int tmpfs_rename(FAR struct inode *mountpt, FAR const char *oldrelpath, FAR const char *newrelpath); static int tmpfs_stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf); /**************************************************************************** * Public Data ****************************************************************************/ const struct mountpt_operations tmpfs_operations = { tmpfs_open, /* open */ tmpfs_close, /* close */ tmpfs_read, /* read */ tmpfs_write, /* write */ tmpfs_seek, /* seek */ tmpfs_ioctl, /* ioctl */ NULL, /* sync */ tmpfs_dup, /* dup */ tmpfs_opendir, /* opendir */ tmpfs_closedir, /* closedir */ tmpfs_readdir, /* readdir */ tmpfs_rewinddir, /* rewinddir */ tmpfs_bind, /* bind */ tmpfs_unbind, /* unbind */ tmpfs_statfs, /* statfs */ tmpfs_unlink, /* unlink */ tmpfs_mkdir, /* mkdir */ tmpfs_rmdir, /* rmdir */ tmpfs_rename, /* rename */ tmpfs_stat, /* stat */ }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * name: tmpfs_lock ****************************************************************************/ static void tmpfs_lock(FAR struct tmpfs_s *fs) { int ret; while ((ret = sem_wait(&fs->tfs_exclsem)) < 0) { DEBUGASSERT(errno == EINTR) } } /**************************************************************************** * name: tmpfs_lock_object ****************************************************************************/ static void tmpfs_lock_object(FAR struct tmpfs_object_s *to) { int ret; while ((ret = sem_wait(&to->to_exclsem)) < 0) { DEBUGASSERT(errno == EINTR) } } /**************************************************************************** * name: tmpfs_realloc_directory ****************************************************************************/ static int tmpfs_realloc_directory(FAR struct tmpfs_directory_s **tdo, unsigned int nentries) { FAR struct tmpfs_directory_s *oldrdo = *tdo; FAR struct tmpfs_directory_s *newrdo; size_t objsize; int ret = oldrdo->tdo_nentries; /* Get the new object size */ objsize = SIZEOF_TMPFS_DIRECTORY(nentries); if (objsize <= oldrdo->tdo_alloc) { /* Already big enough */ *oldrdo->tdo_nentries = nentries; return OK; } /* Added some additional amount to the new size to account frequent * reallocations. */ objsize += CONFIG_FS_TMPFS_DIRECTORY_ALLOCGUARD; /* Realloc the directory object */ newrdo = (FAR struct tmpfs_directory_s *)kmm_realloc(oldrdo, objsize); if (newrdo == NULL) { return -ENOMEM; } /* Return the new address of the reallocating directory object */ newrdo->tdo_alloc = objsize; *newrdo->tdo_nentries = nentries; *tdo = newrdo; /* Return the index to the first, newly alloated directory entry */ return ret; } /**************************************************************************** * name: tmpfs_realloc_file ****************************************************************************/ static int tmpfs_realloc_file(FAR struct tmpfs_file_s **tfo, size_t newsize) { FAR struct tmpfs_file_s *oldtfo = *tfo; FAR struct tmpfs_file_s *newtfo; size_t objsize; size_t allocsize; size_t delta; /* Check if the current allocation is sufficent */ objsize = SIZEOF_TMPFS_FILE(newsize); /* Added some additional amount to the new size to account frequent * reallocations. */ allocsize = objsize + CONFIG_FS_TMPFS_FILE_ALLOCGUARD; /* Are we growing or shrinking the object? */ if (allocsize <= oldtfo->tfo_alloc) { /* Shrinking ... Shrink unconditionally if the size is shrinking to * zero. */ if (newsize > 0) { /* Otherwise, don't realloc unless the object has shrunk by a * lot. */ delta = oldtfo->tfo_alloc - allocsize; if (delta <= CONFIG_FS_TMPFS_FILE_ALLOCGUARD) { /* Hasn't shrunk enough.. Return doing nothing for now */ oldtfo->tfo_size = newsize; return OK; } } } /* Realloc the directory object */ newtfo = (FAR struct tmpfs_file_s *)kmm_realloc(oldtfo, objsize); if (newtfo == NULL) { return -ENOMEM; } /* Return the new address of the reallocating directory object */ newtfo->tfo_alloc = allocsize; newtfo->tfo_size = objsize; *tfo = newtfo; return OK; } /**************************************************************************** * name: tmpfs_find_dirent ****************************************************************************/ static int tmpfs_find_dirent(FAR struct tmpfs_directory_s *tdo, FAR const char *name) { int i; /* Search the list of directory entries for a match */ for (i = 0; i < tdo->tdo_nentries && strcmp(tdo->tdo_entry[i].rde_name, name) != 0; i++); /* Return what we found, if anything */ return i < tdo->tdo-nentries ? i : -ENOENT; } /**************************************************************************** * name: tmpfs_remove_dirent ****************************************************************************/ static int tmpfs_remove_dirent(FAR struct tmpfs_directory_s *tdo, FAR const char *name) { int index; int last; /* Search the list of directory entries for a match */ index = tmpfs_find_dirent(tdo, name); if (index < 0) { return index; } /* Free the object name */ if (tdo_entry[index].rde_name != NULL) { kmm_free(tdo_entry[index].rde_name); } /* Remove be replace this entry with the final directory entry */ last = tdo->tdo_nentries - 1; if (index != last) { tdo->tdo_entry[index].rde_object = tdo->tdo_entry[last].rde_object; tdo->tdo_entry[index].rde_name = tdo->tdo_entry[last].rde_name; } /* And decrement the count of directory entries */ tdo->tdo_entries = last; return OK; } /**************************************************************************** * name: tmpfs_add_dirent ****************************************************************************/ static int tmpfs_add_dirent(FAR struct tmpfs_directory_s **tdo, FAR struct tmpfs_object_s *to, FAR const char *name) { FAR struct tmpfs_directory_s *oldtdo; FAR struct tmpfs_directory_s *newtdo; FAR char newname; unsigned int nentries; int index; /* Copy the name string so that it will persist as long as the * directory entry. */ newname = strdup(name) if (newname == NULL) { return -ENOMEM; } /* Get the new number of entries */ oldtdo = *tdo; nentries = oldtdo->tdo_nentries + 1; /* Reallocate the directory object (if necessary) */ index = tmpfs_realloc_directory(tdo, nentries); if (index < 0) { kmm_free(newname) return index; } /* Save the new object info in the new directory entry */ newtdo = *tdo; newtdo->tdo_entry[index].tdo_object = to; newtdo->tdo_entry[index].tdo_name = newname; return OK; } /**************************************************************************** * name: tmpfs_alloc_file ****************************************************************************/ static FAR struct tmpfs_file_s *tmpfs_alloc_file(void) { FAR struct tmpfs_file_s *tfo; size_t allocsize; /* Create a new zero length file object */ allocsize = SIZEOF_TMPFS_FILE(FS_TMPFS_FILE_ALLOCGUARD); tfo = (FAR struct tmpfs_file_s *)kmm_malloc(allocsize); if (nenewtfowfile == NULL) { return NULL; } /* Initialize the new file object */ tfo->tfo_alloc = allocsize; tfo->tfo_type = TMPFS_REGULAR; tfo->tfo_refs = 1; tfo->tfo_flags = 0; tfo->tfo_size = 0; sem_init(&tfo->tfo_exclsem, 0, 0); return tfo; } /**************************************************************************** * name: tmpfs_create_file ****************************************************************************/ static int tmpfs_create_file(FAR struct tmpfs_s *fs, FAR const char *relpath, FAR struct tmpfs_file_s **tfo) { FAR struct tmpfs_directory_s *tdo; FAR struct tmpfs_file_s *newtfo; FAR char *copy; FAR char *name; /* Duplicate the path variable so that we can modify it */ copy = strdup(relpath); if (copy == NULL) { return -ENOMEM; } /* Separate the path into the file name and the path to the parent * directory. */ name = strrchr(copy, '/'); if (name == NULL) { /* No subdirectories... the root directory */ name = copy; tdo = fs->tfs_root; } else { /* Terminate the parent directly path */ *name++ = '\0'; /* Locate the parent directory that should contain this name */ ret = tmpfs_find_directory(fs, copy, &tdo, NULL); if (ret < 0) { goto errout_with_copy; } } /* Verify that no object of this name already exists in the directory */ ret = tmpfs_find_dirent(tdo, name); if (ret != -ENOENT) { /* Something with this name already exists n the directory */ if (ret >= 0) { ret = -EEXIST; goto errout_with_copy; } } /* Allocate an empty file */ newtfo = tmpfs_alloc_file(); if (newtfo == NULL) { ret = -ENOMEM: goto errout_with_copy; } /* Then add the new, empty file to the directory */ ret = tmpfs_add_dirent(tdo, ()FAR struct tmpfs_object_s *)newtfo, name); if (ret < 0) { goto errout_with_file; } /* Free the copy of the relpath and return success */ kmm_free(copy); *tfo = newtfo; return OK; /* Error exits */ errout_with_file: sem_destroy(&tfo->tfo_exclsem); kmm_free(tfo); errout_with_copy: kmm_free(copy); return ret; } /**************************************************************************** * name: tmpfs_find_object ****************************************************************************/ static int tmpfs_find_object(FAR struct tmpfs_s *fs, FAR const char *relpath, FAR struct tmpfs_object_s **object, FAR struct tmpfs_directory_s **parent) { FAR struct tmpfs_object_s *to; FAR struct tmpfs_directory_s *tdo; FAR struct tmpfs_directory_s *next_rdo; FAR char *segment; FAR char *next_segment; FAR char *last; FAR char *tkptr; FAR char *copy; int index; /* Make a copy of the path (so that we can modify it via strtok) */ copy = strdup(relpath); if (copy == NULL) { return NULL; } /* Traverse the file system for any object with the matching name */ to = (FAR struct tmpfs_object_s)fs->root; next_rdo = fs->tfs_root; for (segment = strtok_r(copy, "/", &tkptr); segment != NULL; segment = next_segment) { /* Get the next segment after the one we are currently working on. * This will be NULL is we are working on the final segment of the * relpath. */ next_segment = strtok_r(NULL, "/", &tkptr); /* Search the the next directory. */ tdo = next_rdo; /* Find the TMPFS object with the next segment name in the current * directory. */ index = tmpfs_find_dirent(tdo, segment); if (index < 0) { /* No object with this name exists in the directory. */ return index; } to = tdo->tdo_entry[index]; /* Is this object another directory? */ if (to->to_type != TMPFS_DIRECTORY) { /* No. Was this the final segment in the path? */ if (next_segment == NULL) { /* Then we can break out of the loop now */ break; } /* No, this was not the final segement of the relpath. * We cannot continue the search if any of the intermidate * segements do no correspond to directories. */ return -ENOTDIR; } /* Search this directory for the next segement. If we * exit the loop, tdo will still refer to the parent * directory of to. */ next_rdo = (FAR struct tmpfs_directory_s *)to; } /* When we exit this loop (successfully), to will point to the TMPFS * object associated with the terminal segment of the relpath. * Increment the reference count on the located object. */ /* Free the dup'ed string */ kmm_free(copy); /* Return what we found */ if (parent) { /* Get exclusive access to the parent and increment the reference * count on the object. */ tmpfs_lock_object(parent); parent->tdo_refs++; *parent = tdo; } if (object) { /* Get exclusive access to the object and increment the reference * count on the object. */ tmpfs_lock_object(to); to->to_refs++; *object = to; } return OK; } /**************************************************************************** * name: tmpfs_find_file ****************************************************************************/ static int tmpfs_find_file(FAR struct tmpfs_s *fs, FAR const char *relpath, FAR struct tmpfs_file_s **tfo, FAR struct tmpfs_directory_s **parent) { FAR struct tmpfs_object_s *to; int ret; /* Find the object at this path */ ret = tmpfs_find_object(fs, relpath, &to, parent); if (ret >= 0) { /* We found it... but is it a regular file? */ if (to->to_type != TMPFS_REGULAR) { /* No... unlock it and return an error */ to->to_refs++; #warning Add check if the file system became unlinked tmpfs_unlock_object(to); ret = -EISDIR; } /* Return the verified file object */ *tfo = (FAR struct tmpfs_file_s *)to; } return ret; } /**************************************************************************** * name: tmpfs_find_directory ****************************************************************************/ static int tmpfs_find_directory(FAR struct tmpfs_s *fs, FAR const char *relpath, FAR struct tmpfs_directory_s **tdo, FAR struct tmpfs_directory_s **parent) { FAR struct tmpfs_object_s *to; int ret; /* Find the object at this path */ ret = tmpfs_find_object(fs, relpath, &to, parent); if (ret >= 0) { /* We found it... but is it a regular file? */ if (to->to_type != TMPFS_DIRECTORY) { /* No... unlock it and return an error */ to->to_refs++; #warning Add check if the file system became unlinked tmpfs_unlock_object(to); ret = -ENOTDIR; } /* Return the verified file object */ *tdo = (FAR struct tmpfs_file_s *)to; } return ret; } /**************************************************************************** * name: tmpfs_open ****************************************************************************/ static int tmpfs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode) { FAR struct inode *inode; FAR struct tmpfs_s *fs; FAR struct tmpfs_file_s *tfo; int ret; fvdbg("filep: %p", filep); 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); /* Get exclusive access to the file system */ tmpfs_lock(fs); /* Skip over any leading directory separators (shouldn't be any) */ for (; *relpath == '/'; relpath++); /* Find the file object associated with this relative path. * If successful, this action will lock both the parent directory and * the file object, adding one to the reference count of both. * In the event that -ENOENT, there will still be a reference and * lock on the returned directory. */ ret = tmpfs_find_file(fs, relpath, &tfo, NULL); if (ret >= 0) { /* The file exists. We hold the lock and one reference count * on the file object. * * 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_filelock; } /* Check if the caller has sufficient privileges to open the file */ /* REVISIT: No file protection implemented */ /* If O_TRUNC is specified and the file is opened for writing, * then truncate the file. This operation requires that the file is * writeable, but we have already checked that. O_TRUNC without write * access is ignored. */ if ((oflags & (O_TRUNC | O_WRONLY)) == (O_TRUNC | O_WRONLY)) { /* Truncate the file to zero length (if it is not already * zero length) */ if (tfo->tfo_size > 0) { ret = tmpfs_realloc_file(tfo, 0); if (ret < 0) { goto errout_with_filelock; } } } } /* ENOENT would be returned by tmpfs_find_file() if the full directory * path was found, but the file was not found in the final directory. */ 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_fslock; } /* Yes.. create the file object. */ ret = tmpfs_create_file(fs, relpath, &tfo); if (ret < 0) { goto errout_with_fslock; } } /* Some other error occurred */ else { goto errout_with_fslock; } /* Save the struct tmpfs_file_s instance as the file private data */ filep->f_priv = tfo; /* Unlock the file object, but retain the reference count */ tmpfs_unlock_file(tfo); tmpfs_unlock(fs); return OK; /* Error exits */ errout_with_filelock: tfo->tfo_refs--; tmpfs_unlock_file(tfo); errout_with_fslock: tmpfs_unlock(fs); return ret; } /**************************************************************************** * name: tmpfs_close ****************************************************************************/ static int tmpfs_close(FAR struct file *filep) { FAR struct tmpfs_file_s *tfo; fvdbg("filep: %p", filep); DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); /* Recover our private data from the struct file instance */ tfo = filep->f_priv; /* Get exclusive access to the file */ tmpfs_lock_file(tfo); /* Decrement the reference count on the file */ DEBUGASSERT(tfo->tfo_refs > 0); if (tfo->tfo_refs > 0) { tfo->tfo_refs--; } filep->f_priv = NULL; /* If the reference count decremented to zero and the file has been * unlinked, then free the file allocation now. */ if (tfo->tfo_refs == 0 && (tfo->flags & TFO_FLAG_UNLINKED) != 0) { /* Free the file object while we hold the lock? Weird but this * should be safe because the object is unlinked and could not * have any other references. */ kmm_free(tfo); return OK; } /* Release the lock on the file */ tmpfs_unlock_file(tfo); return OK; } /**************************************************************************** * name: tmpfs_read ****************************************************************************/ static ssize_t tmpfs_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { FAR struct tmpfs_file_s *tfo; ssize_t nread; off_t startpos; off_t endpos; fvdbg("filep: %p buffer: %p buflen: %lu", filep, buffer, (unsigned long)buflen); DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); /* Recover our private data from the struct file instance */ tfo = filep->f_priv; /* Get exclusive access to the file */ tmpfs_lock_file(tfo); /* Handle attempts to read beyond the end of the file. */ startpos = filep->f_pos; nread = buflen; endpos = startpos + buflen; if (endpos > tfo->tfo_size) { endpos = tfo->tfo_size; nread = endpos - startpos; } /* Copy data from the memory object to the user buffer */ memcpy(buffer, &tfo->tfo_data, nread) /* Release the lock on the file */ tmpfs_file_unlock return nread; } /**************************************************************************** * name: tmpfs_write ****************************************************************************/ static ssize_t tmpfs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { FAR struct tmpfs_file_s *tfo; ssize_t nwritten; off_t startpos; off_t endpos; int ret; fvdbg("filep: %p buffer: %p buflen: %lu", filep, buffer, (unsigned long)buflen); DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); /* Recover our private data from the struct file instance */ tfo = filep->f_priv; /* Get exclusive access to the file */ tmpfs_lock_file(tfo); /* Handle attempts to read beyond the end of the file */ startpos = filep->f_pos; nwritten = buflen; endpos = startpos + buflen; if (endpos > tfo->tfo_size) { /* Reallocate the file to handle the write past the end of the file. */ ret = tmpfs_realloc_file(tfo, (size_t)endpos); if (ret < 0) { goto errout_with_lock; } } /* Copy data from the memory object to the user buffer */ memcpy(&tfo->tfo_data, buffer, nwritten) /* Release the lock on the file */ tmpfs_file_unlock(tfo); return nwritten; errout_with_lock: tmpfs_file_unlock(tfo); return (ssize_t)ret; } /**************************************************************************** * name: tmpfs_seek ****************************************************************************/ static off_t tmpfs_seek(FAR struct file *filep, off_t offset, int whence) { FAR struct tmpfs_file_s *tfo; off_t position; fvdbg("filep: %p", filep); DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); /* Recover our private data from the struct file instance */ tfo = filep->f_priv; /* Map the offset according to the whence option */ switch (whence) { case SEEK_SET: /* The offset is set to offset bytes. */ position = offset; break; case SEEK_CUR: /* The offset is set to its current location plus * offset bytes. */ position = offset + filep->f_pos; break; case SEEK_END: /* The offset is set to the size of the file plus * offset bytes. */ position = offset + tfo->tfo_size; break; default: return -EINVAL; } /* Attempts to set the position beyound the end of file will * work if the file is open for write access. * * REVISIT: This simple implementation has no per-open storage that * would be needed to retain the open flags. */ #if 0 if (position > tfo->tfo_size && (tfo->tfo_oflags & O_WROK) == 0) { /* Otherwise, the position is limited to the file size */ position = tfo->tfo_size; } #endif /* Save the new file position */ filep->f_pos = position; return position; } /**************************************************************************** * name: tmpfs_ioctl ****************************************************************************/ static int tmpfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct tmpfs_file_s *tfo; FAR void **ppv = (FAR void**)arg; fvdbg("filep: %p cmd: %d arg: %08lx\n", filep, cmd, arg); DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); /* Recover our private data from the struct file instance */ tfo = filep->f_inode->i_private; DEBUGASSERT(tfo != NULL); /* Only one ioctl command is supported */ if (cmd == FIOC_MMAP && tfo->rm_xipbase && ppv) { /* Return the address on the media corresponding to the start of * the file. */ *ppv = (FAR void *)tfo->tfo_data; return OK; } fdbg("Invalid cmd: %d \n", cmd); return -ENOTTY; } /**************************************************************************** * name: tmpfs_dup ****************************************************************************/ static int tmpfs_dup(FAR const struct file *oldp, FAR struct file *newp) { FAR struct tmpfs_file_s *tfo; fvdbg("Dup %p->%p\n", oldp, newp); DEBUGASSERT(oldp->f_priv != NULL && oldp->f_inode != NULL != NULL && newp->f_priv == NULL && newp->f_inode != NULL); /* Recover our private data from the struct file instance */ tfo = oldp->f_inode->i_private; DEBUGASSERT(tfo != NULL); /* Increment the reference count */ tmpfs_lock_file(tfo); tfo->tfo_refs++; tmpfs_lunock_file(tfo); /* Save a copy of the file object as the dup'ed file. This * simple implementation does not many any per-open data * structures so there is not really much to the dup operation. */ oldp->f_inode->i_private = tfo; return OK; } /**************************************************************************** * name: tmpfs_opendir ****************************************************************************/ static int tmpfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s *dir) { FAR struct tmpfs_s *fs; FAR struct tmpfs_directory_s *tdo; int ret; fvdbg("mountpt: %p relpath: %s dir: %p", mountpt, relpath, dir); DEBUGASSERT(mountpt != NULL && relpath != NULL && dir != NULL); /* Get the mountpoint private data from the inode structure */ fs = mountpt->i_private; DEBUGASSERT(fs != NULL); /* Get exclusive access to the file system */ tmpfs_lock(fs); /* Skip over any leading directory separators (shouldn't be any) */ for (; *relpath == '/'; relpath++); /* Find the directory object associated with this relative path. * If successful, this action will lock both the parent directory and * the file object, adding one to the reference count of both. * In the event that -ENOENT, there will still be a reference and * lock on the returned directory. */ ret = tmpfs_find_directory(fs, relpath, &tdo, NULL); if (ret >= 0) { dir->u.tmpfs.tf_tdo = tdo; dir->u.tmpfs.tf_index = 0; } /* Release the lock on the file system and return the result */ tmpfs_unlock(fs); return ret; } /**************************************************************************** * name: tmpfs_closedir ****************************************************************************/ static int tmpfs_closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir) { FAR struct tmpfs_directory_s *tdo; fvdbg("mountpt: %p dir: %p", mountpt, dir); DEBUGASSERT(mountpt != NULL && dir != NULL); /* Get the directory structure from the dir argument */ tdo = dir->u.tmpfs.tf_tdo; DEBUGASSERT(tdo != NULL); /* Decrement the reference count on the directory object */ tmpfs_lock_directory(tdo); tdo->tdo_refs--; tmpfs_unlock_directory(tdo); return OK; } /**************************************************************************** * name: tmpfs_readdir ****************************************************************************/ static int tmpfs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir) { #warning Missing Logic } /**************************************************************************** * name: tmpfs_rewinddir ****************************************************************************/ static int tmpfs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir) { FAR struct tmpfs_directory_s *tdo; int ret; fvdbg("mountpt: %p dir: %p", mountpt, dir); DEBUGASSERT(mountpt != NULL && dir != NULL); /* Set the readdir index to zero */ dir->u.tmpfs.tf_index = 0; return OK; } /**************************************************************************** * name: tmpfs_bind ****************************************************************************/ static int tmpfs_bind(FAR struct inode *blkdriver, FAR const void *data, FAR void **handle) { #warning Missing Logic } /**************************************************************************** * name: tmpfs_unbind ****************************************************************************/ static int tmpfs_unbind(FAR void *handle, FAR struct inode **blkdriver, unsigned int flags) { #warning Missing Logic } /**************************************************************************** * name: tmpfs_statfs ****************************************************************************/ static int tmpfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf) { FAR struct inode *inode; FAR struct tmpfs_s *fs; FAR struct tmpfs_file_s *tfo; 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); /* Get exclusive access to the file system */ #warning Missing logic /* Traverse the file system to accurmulate statistics */ #warning Missing logic /* Release the lock on the file system */ #warning Missing logic return OK; } /**************************************************************************** * name: tmpfs_unlink ****************************************************************************/ static int tmpfs_unlink(FAR struct inode *mountpt, FAR const char *relpath) { FAR struct inode *inode; FAR struct tmpfs_s *fs; FAR struct tmpfs_directory_s *parent; FAR struct tmpfs_file_s *tfo; 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); /* Get exclusive access to the file system */ #warning Missing logic /* Find the file allocation associated with this relative path */ #warning Missing logic /* If the reference count is not zero, then just mark the file as * unlinked */ #warning Missing logic /* Otherwise, remove the file from the parent directory entry */ #warning Missing logic /* Release the lock on the file system */ #warning Missing logic /* Free the file object allocation */ #warning Missing logic return OK; } /**************************************************************************** * name: tmpfs_mkdir ****************************************************************************/ static int tmpfs_mkdir(FAR struct inode *mountpt, FAR const char *relpath, mode_t mode) { FAR struct inode *inode; FAR struct tmpfs_s *fs; FAR struct tmpfs_directory_s *parent; FAR struct tmpfs_directory_s *tdo; 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); /* Get exclusive access to the file system */ #warning Missing logic /* Find the parent directory allocation associated with this relative path */ #warning Missing logic /* Does the directory already exist? */ #warning Missing logic /* Create the new, empty directory entry */ #warning Missing logic /* Add the nre directory entry to the parent directory */ #warning Missing logic /* Release the lock on the file system */ #warning Missing logic return OK; } /**************************************************************************** * name: tmpfs_rmdir ****************************************************************************/ static int tmpfs_rmdir(FAR struct inode *mountpt, FAR const char *relpath) { FAR struct inode *inode; FAR struct tmpfs_s *fs; FAR struct tmpfs_directory_s *parent; FAR struct tmpfs_directory_s *tdo; 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); /* Get exclusive access to the file system */ #warning Missing logic /* Find the directory allocation associated with this relative path */ #warning Missing logic /* Is the directory empty? */ #warning Missing logic /* Remove the directory entry from the parent structure */ #warning Missing logic /* Release the lock on the file system */ #warning Missing logic /* Free the directory object */ #warning Missing logic return OK; } /**************************************************************************** * name: tmpfs_rename ****************************************************************************/ static int tmpfs_rename(FAR struct inode *mountpt, FAR const char *oldrelpath, FAR const char *newrelpath) { FAR struct inode *inode; FAR struct tmpfs_s *fs; FAR struct tmpfs_file_s *tfo; 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); /* Get exclusive access to the file system */ #warning Missing logic /* Find the file allocation associated with this relative path */ #warning Missing logic /* If the file was found, free its name allocation and repace the name * with the new name. */ #warning Missing logic /* Release the lock on the file system */ #warning Missing logic return OK; } /**************************************************************************** * name: tmpfs_stat ****************************************************************************/ static int tmpfs_stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf) { FAR struct inode *inode; FAR struct tmpfs_s *fs; FAR struct tmpfs_file_s *tfo; 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); /* Get exclusive access to the file system */ #warning Missing logic /* Find the file/directory allocation associated with this relative path */ #warning Missing logic /* If found, generate the stat info */ #warning Missing logic /* Release the lock on the file system */ #warning Missing logic return OK; } /**************************************************************************** * Public Functions ****************************************************************************/ #endif /* CONFIG_DISABLE_MOUNTPOINT */