mirror of
https://github.com/openharmony/third_party_NuttX.git
synced 2026-07-01 08:17:34 -04:00
70bdb2590c
Feature or Bugfix:Feature Binary Source:Huawei PrivateCode(Yes/No):Yes Change-Id: I175d2648bc6f9078c34de2c0a5c93fda10b86c47 ChangeID:13306420
2501 lines
63 KiB
C
Executable File
2501 lines
63 KiB
C
Executable File
/****************************************************************************
|
|
* fs/tmpfs/fs_tmpfs.c
|
|
*
|
|
* Copyright (C) 2015, 2017-2018 Gregory Nutt. All rights reserved.
|
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
|
*
|
|
* 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 <string.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#include <debug.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <linux/spinlock.h>
|
|
#include "fs/dirent_fs.h"
|
|
#include "los_tables.h"
|
|
#include "fs_tmpfs.h"
|
|
#include "los_vm_filemap.h"
|
|
#include "user_copy.h"
|
|
|
|
#ifdef LOSCFG_FS_RAMFS
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
/*
|
|
#if CONFIG_FS_TMPFS_DIRECTORY_FREEGUARD <= CONFIG_FS_TMPFS_DIRECTORY_ALLOCGUARD
|
|
# warning CONFIG_FS_TMPFS_DIRECTORY_FREEGUARD needs to be > ALLOCGUARD
|
|
#endif
|
|
|
|
#if CONFIG_FS_TMPFS_FILE_FREEGUARD <= CONFIG_FS_TMPFS_FILE_ALLOCGUARD
|
|
# warning CONFIG_FS_TMPFS_FILE_FREEGUARD needs to be > ALLOCGUARD
|
|
#endif
|
|
*/
|
|
#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_file(tfo) \
|
|
(tmpfs_unlock_object((FAR struct tmpfs_object_s *)tfo))
|
|
#define tmpfs_unlock_directory(tdo) \
|
|
(tmpfs_unlock_object((FAR struct tmpfs_object_s *)tdo))
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* TMPFS helpers */
|
|
|
|
static void tmpfs_lock_reentrant(FAR struct tmpfs_sem_s *sem);
|
|
static void tmpfs_lock(FAR struct tmpfs_s *fs);
|
|
static void tmpfs_unlock_reentrant(FAR struct tmpfs_sem_s *sem);
|
|
static void tmpfs_unlock(FAR struct tmpfs_s *fs);
|
|
static void tmpfs_lock_object(FAR struct tmpfs_object_s *to);
|
|
static void tmpfs_unlock_object(FAR struct tmpfs_object_s *to);
|
|
static void tmpfs_release_lockedobject(FAR struct tmpfs_object_s *to);
|
|
static void tmpfs_release_lockedfile(FAR struct tmpfs_file_s *tfo);
|
|
static struct tmpfs_dirent_s *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 struct tmpfs_object_s *to);
|
|
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_directory_s *parent_input,
|
|
FAR struct tmpfs_file_s **tfo);
|
|
|
|
static FAR struct tmpfs_directory_s *tmpfs_alloc_directory(void);
|
|
static int tmpfs_create_directory(FAR struct tmpfs_s *fs,
|
|
FAR const char *relpath,
|
|
FAR struct tmpfs_directory_s *parent,
|
|
FAR struct tmpfs_directory_s **tdo);
|
|
|
|
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 */
|
|
|
|
int tmpfs_close(struct file *filep);
|
|
off_t tmpfs_seek(FAR struct file *filep, off_t offset, int whence);
|
|
int tmpfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
|
|
int tmpfs_closedir(struct Vnode *node, struct fs_dirent_s *dir);
|
|
int tmpfs_rewinddir(struct Vnode *vp, struct fs_dirent_s *dir);
|
|
int tmpfs_truncate(struct Vnode *vp, off_t len);
|
|
|
|
int tmpfs_mount(struct Mount *mnt, struct Vnode *device, const void *data);
|
|
int tmpfs_unmount(struct Mount *mnt, struct Vnode **blkdriver);
|
|
|
|
int tmpfs_lookup(struct Vnode *parent, const char *name, int len, struct Vnode **vpp);
|
|
ssize_t tmpfs_write(struct file *filep, const char *buffer, size_t buflen);
|
|
ssize_t tmpfs_read(struct file *filep, char *buffer, size_t buflen);
|
|
int tmpfs_stat(struct Vnode *vp, struct stat *st);
|
|
int tmpfs_opendir(struct Vnode *vp, struct fs_dirent_s *dir);
|
|
int tmpfs_readdir(struct Vnode *vp, struct fs_dirent_s *dir);
|
|
int tmpfs_rename(struct Vnode *oldVnode, struct Vnode *newParent, const char *oldname, const char *newname);
|
|
int tmpfs_mkdir(struct Vnode *parent, const char *relpath, mode_t mode, struct Vnode **vpp);
|
|
int tmpfs_create(struct Vnode *dvp, const char *path, int mode, struct Vnode **vpp);
|
|
int tmpfs_unlink(struct Vnode *parent, struct Vnode *node, char *relpath);
|
|
int tmpfs_rmdir(struct Vnode *parent, struct Vnode *target, char *dirname);
|
|
int tmpfs_reclaim(struct Vnode *vp);
|
|
|
|
int tmpfs_statfs(struct Mount *mp, struct statfs *sbp);
|
|
|
|
static void tmpfs_stat_common(FAR struct tmpfs_object_s *to,
|
|
FAR struct stat *buf);
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
const struct MountOps tmpfs_operations = {
|
|
.Mount = tmpfs_mount,
|
|
.Unmount = tmpfs_unmount,
|
|
.Statfs = tmpfs_statfs,
|
|
};
|
|
|
|
struct VnodeOps tmpfs_vops = {
|
|
.Lookup = tmpfs_lookup,
|
|
.Getattr = tmpfs_stat,
|
|
.Opendir = tmpfs_opendir,
|
|
.Readdir = tmpfs_readdir,
|
|
.Rename = tmpfs_rename,
|
|
.Mkdir = tmpfs_mkdir,
|
|
.Create = tmpfs_create,
|
|
.Unlink = tmpfs_unlink,
|
|
.Rmdir = tmpfs_rmdir,
|
|
.Reclaim = tmpfs_reclaim,
|
|
.Closedir = tmpfs_closedir,
|
|
.Close = NULL,
|
|
.Rewinddir = tmpfs_rewinddir,
|
|
.Truncate = tmpfs_truncate,
|
|
};
|
|
|
|
struct file_operations_vfs tmpfs_fops = {
|
|
.seek = tmpfs_seek,
|
|
.write = tmpfs_write,
|
|
.read = tmpfs_read,
|
|
.ioctl = tmpfs_ioctl,
|
|
.mmap = OsVfsFileMmap,
|
|
.close = tmpfs_close,
|
|
.fsync = NULL,
|
|
};
|
|
|
|
static struct tmpfs_s tmpfs_superblock = {0};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
static mode_t type_to_mode(int type, mode_t permission)
|
|
{
|
|
switch (type) {
|
|
case VNODE_TYPE_DIR:
|
|
return permission | S_IFDIR;
|
|
case VNODE_TYPE_REG:
|
|
return permission | S_IFREG;
|
|
case VNODE_TYPE_BLK:
|
|
return permission | S_IFBLK;
|
|
case VNODE_TYPE_CHR:
|
|
return permission | S_IFCHR;
|
|
case VNODE_TYPE_FIFO:
|
|
return permission | S_IFIFO;
|
|
default:
|
|
break;
|
|
}
|
|
PRINTK("mode to type failed, unknwon type = %d\n", type);
|
|
return permission;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_timestamp
|
|
****************************************************************************/
|
|
|
|
static time_t tmpfs_timestamp(void)
|
|
{
|
|
struct timeval tv;
|
|
|
|
(void)gettimeofday(&tv, (struct timezone *)NULL);
|
|
|
|
return (time_t)(tv.tv_sec);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_lock_reentrant
|
|
****************************************************************************/
|
|
|
|
static void tmpfs_lock_reentrant(FAR struct tmpfs_sem_s *sem)
|
|
{
|
|
pid_t me;
|
|
|
|
/* Do we already hold the semaphore? */
|
|
|
|
me = getpid();
|
|
if (me == sem->ts_holder)
|
|
{
|
|
/* Yes... just increment the count */
|
|
|
|
sem->ts_count++;
|
|
DEBUGASSERT(sem->ts_count > 0);
|
|
}
|
|
|
|
/* Take the semaphore (perhaps waiting) */
|
|
|
|
else
|
|
{
|
|
while (sem_wait(&sem->ts_sem) != 0)
|
|
{
|
|
/* The only case that an error should occur here is if the wait
|
|
* was awakened by a signal.
|
|
*/
|
|
|
|
DEBUGASSERT(get_errno() == EINTR);
|
|
}
|
|
|
|
/* No we hold the semaphore */
|
|
|
|
sem->ts_holder = me;
|
|
sem->ts_count = 1;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_lock
|
|
****************************************************************************/
|
|
|
|
static void tmpfs_lock(FAR struct tmpfs_s *fs)
|
|
{
|
|
tmpfs_lock_reentrant(&fs->tfs_exclsem);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_lock_object
|
|
****************************************************************************/
|
|
|
|
static void tmpfs_lock_object(FAR struct tmpfs_object_s *to)
|
|
{
|
|
tmpfs_lock_reentrant(&to->to_exclsem);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_unlock_reentrant
|
|
****************************************************************************/
|
|
|
|
static void tmpfs_unlock_reentrant(FAR struct tmpfs_sem_s *sem)
|
|
{
|
|
DEBUGASSERT(sem->ts_holder == getpid());
|
|
|
|
/* Is this our last count on the semaphore? */
|
|
|
|
if (sem->ts_count > 1)
|
|
{
|
|
/* No.. just decrement the count */
|
|
|
|
sem->ts_count--;
|
|
}
|
|
|
|
/* Yes.. then we can really release the semaphore */
|
|
|
|
else
|
|
{
|
|
sem->ts_holder = TMPFS_NO_HOLDER;
|
|
sem->ts_count = 0;
|
|
sem_post(&sem->ts_sem);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_unlock
|
|
****************************************************************************/
|
|
|
|
static void tmpfs_unlock(FAR struct tmpfs_s *fs)
|
|
{
|
|
tmpfs_unlock_reentrant(&fs->tfs_exclsem);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_unlock_object
|
|
****************************************************************************/
|
|
|
|
static void tmpfs_unlock_object(FAR struct tmpfs_object_s *to)
|
|
{
|
|
tmpfs_unlock_reentrant(&to->to_exclsem);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_release_lockedobject
|
|
****************************************************************************/
|
|
|
|
static void tmpfs_release_lockedobject(FAR struct tmpfs_object_s *to)
|
|
{
|
|
DEBUGASSERT(to && to->to_refs > 0);
|
|
|
|
/* Is this a file object? */
|
|
|
|
if (to->to_type == TMPFS_REGULAR)
|
|
{
|
|
tmpfs_release_lockedfile((FAR struct tmpfs_file_s *)to);
|
|
}
|
|
else
|
|
{
|
|
if(to->to_refs > 0)
|
|
{
|
|
to->to_refs--;
|
|
}
|
|
tmpfs_unlock_object(to);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_release_lockedfile
|
|
****************************************************************************/
|
|
|
|
static void tmpfs_release_lockedfile(FAR struct tmpfs_file_s *tfo)
|
|
{
|
|
DEBUGASSERT(tfo && tfo->tfo_refs > 0);
|
|
|
|
/* If there are no longer any references to the file and the file has been
|
|
* unlinked from its parent directory, then free the file object now.
|
|
*/
|
|
|
|
if (tfo->tfo_refs == 1 && (tfo->tfo_flags & TFO_FLAG_UNLINKED) != 0)
|
|
{
|
|
sem_destroy(&tfo->tfo_exclsem.ts_sem);
|
|
kmm_free(tfo->tfo_data);
|
|
kmm_free(tfo);
|
|
}
|
|
|
|
/* Otherwise, just decrement the reference count on the file object */
|
|
|
|
else
|
|
{
|
|
if(tfo->tfo_refs > 0)
|
|
{
|
|
tfo->tfo_refs--;
|
|
}
|
|
tmpfs_unlock_file(tfo);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_find_dirent
|
|
****************************************************************************/
|
|
|
|
static struct tmpfs_dirent_s *tmpfs_find_dirent(FAR struct tmpfs_directory_s *tdo,
|
|
FAR const char *name)
|
|
{
|
|
LOS_DL_LIST *node;
|
|
struct tmpfs_dirent_s *tde;
|
|
|
|
/* Search the list of directory entries for a match */
|
|
|
|
for (node = tdo->tdo_entry.pstNext; node != &tdo->tdo_entry; node = node->pstNext)
|
|
{
|
|
tde = (struct tmpfs_dirent_s *)node;
|
|
if (tde->tde_inuse == true && strcmp(tde->tde_name, name) == 0)
|
|
{
|
|
return tde;
|
|
}
|
|
}
|
|
|
|
/* Return NULL if not found */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_remove_dirent
|
|
****************************************************************************/
|
|
|
|
static int tmpfs_remove_dirent(FAR struct tmpfs_directory_s *tdo,
|
|
FAR struct tmpfs_object_s *to)
|
|
{
|
|
struct tmpfs_dirent_s *tde;
|
|
|
|
/* Search the list of directory entries for a match */
|
|
|
|
tde = to->to_dirent;
|
|
if (tde == NULL)
|
|
{
|
|
return -ENONET;
|
|
}
|
|
|
|
/* Free the object name */
|
|
|
|
if (tde->tde_name != NULL)
|
|
{
|
|
kmm_free(tde->tde_name);
|
|
tde->tde_name = NULL;
|
|
}
|
|
|
|
if (tdo->tdo_count == 0)
|
|
{
|
|
LOS_ListDelete(&tde->tde_node);
|
|
kmm_free(tde);
|
|
}
|
|
else
|
|
{
|
|
tde->tde_inuse = false;
|
|
tde->tde_object = NULL;
|
|
}
|
|
if(tdo->tdo_nentries > 0)
|
|
{
|
|
tdo->tdo_nentries--;
|
|
}
|
|
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 *parent;
|
|
FAR struct tmpfs_dirent_s *tde;
|
|
FAR char *newname;
|
|
|
|
/* Copy the name string so that it will persist as long as the
|
|
* directory entry.
|
|
*/
|
|
|
|
newname = strdup(name);
|
|
if (newname == NULL)
|
|
{
|
|
return -ENOSPC;
|
|
}
|
|
|
|
tde = (FAR struct tmpfs_dirent_s *)malloc(sizeof(struct tmpfs_dirent_s));
|
|
if (tde == NULL)
|
|
{
|
|
free(newname);
|
|
return -ENOSPC;
|
|
}
|
|
|
|
tde->tde_object = to;
|
|
tde->tde_name = newname;
|
|
tde->tde_inuse = true;
|
|
to->to_dirent = tde;
|
|
|
|
/* Save the new object info in the new directory entry */
|
|
|
|
parent = *tdo;
|
|
LOS_ListTailInsert(&parent->tdo_entry, &tde->tde_node);
|
|
parent->tdo_nentries++;
|
|
|
|
/* Update directory times */
|
|
|
|
parent->tdo_ctime = parent->tdo_mtime = tmpfs_timestamp();
|
|
|
|
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(struct tmpfs_file_s);
|
|
tfo = (FAR struct tmpfs_file_s *)kmm_malloc(allocsize);
|
|
if (tfo == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* Initialize the new file object. NOTE that the initial state is
|
|
* locked with one reference count.
|
|
*/
|
|
|
|
tfo->tfo_atime = tmpfs_timestamp();
|
|
tfo->tfo_mtime = tfo->tfo_atime;
|
|
tfo->tfo_ctime = tfo->tfo_atime;
|
|
tfo->tfo_type = TMPFS_REGULAR;
|
|
tfo->tfo_refs = 1;
|
|
tfo->tfo_flags = 0;
|
|
tfo->tfo_size = 0;
|
|
tfo->tfo_data = NULL;
|
|
|
|
tfo->tfo_exclsem.ts_holder = getpid();
|
|
tfo->tfo_exclsem.ts_count = 1;
|
|
if (sem_init(&tfo->tfo_exclsem.ts_sem, 0, 0) != 0)
|
|
{
|
|
PRINT_ERR("%s %d, sem_init failed!\n", __FUNCTION__, __LINE__);
|
|
kmm_free(tfo);
|
|
return NULL;
|
|
}
|
|
|
|
return tfo;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_create_file
|
|
****************************************************************************/
|
|
|
|
static int tmpfs_create_file(FAR struct tmpfs_s *fs,
|
|
FAR const char *relpath,
|
|
FAR struct tmpfs_directory_s *parent_input,
|
|
FAR struct tmpfs_file_s **tfo)
|
|
{
|
|
FAR struct tmpfs_directory_s *parent;
|
|
FAR struct tmpfs_file_s *newtfo;
|
|
struct tmpfs_dirent_s *tde;
|
|
FAR char *copy;
|
|
int ret;
|
|
|
|
/* Duplicate the path variable so that we can modify it */
|
|
|
|
copy = strdup(relpath);
|
|
if (copy == NULL)
|
|
{
|
|
return -ENOSPC;
|
|
}
|
|
|
|
/* Separate the path into the file name and the path to the parent
|
|
* directory.
|
|
*/
|
|
if (parent_input == NULL)
|
|
{
|
|
/* No subdirectories... use the root directory */
|
|
parent = (FAR struct tmpfs_directory_s *)fs->tfs_root.tde_object;
|
|
|
|
/* Lock the root directory to emulate the behavior of tmpfs_find_directory() */
|
|
|
|
tmpfs_lock_directory(parent);
|
|
parent->tdo_refs++;
|
|
}
|
|
else
|
|
{
|
|
parent = parent_input;
|
|
}
|
|
if (parent == NULL)
|
|
{
|
|
ret = -EEXIST;
|
|
goto errout_with_copy;
|
|
}
|
|
|
|
/* Verify that no object of this name already exists in the directory */
|
|
tde = tmpfs_find_dirent(parent, copy);
|
|
if (tde != NULL)
|
|
{
|
|
/* Something with this name already exists in the directory.
|
|
* OR perhaps some fatal error occurred.
|
|
*/
|
|
|
|
ret = -EEXIST;
|
|
goto errout_with_parent;
|
|
}
|
|
|
|
/* Allocate an empty file. The initial state of the file is locked with one
|
|
* reference count.
|
|
*/
|
|
|
|
newtfo = tmpfs_alloc_file();
|
|
if (newtfo == NULL)
|
|
{
|
|
ret = -ENOSPC;
|
|
goto errout_with_parent;
|
|
}
|
|
|
|
/* Then add the new, empty file to the directory */
|
|
|
|
ret = tmpfs_add_dirent(&parent, (FAR struct tmpfs_object_s *)newtfo, copy);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_file;
|
|
}
|
|
|
|
/* Release the reference and lock on the parent directory */
|
|
if (parent->tdo_refs > 0)
|
|
{
|
|
parent->tdo_refs--;
|
|
}
|
|
tmpfs_unlock_directory(parent);
|
|
|
|
/* Free the copy of the relpath and return success */
|
|
|
|
kmm_free(copy);
|
|
*tfo = newtfo;
|
|
return OK;
|
|
|
|
/* Error exits */
|
|
|
|
errout_with_file:
|
|
sem_destroy(&newtfo->tfo_exclsem.ts_sem);
|
|
kmm_free(newtfo);
|
|
|
|
errout_with_parent:
|
|
if (parent->tdo_refs > 0)
|
|
{
|
|
parent->tdo_refs--;
|
|
}
|
|
tmpfs_unlock_directory(parent);
|
|
|
|
errout_with_copy:
|
|
kmm_free(copy);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_alloc_directory
|
|
****************************************************************************/
|
|
|
|
static FAR struct tmpfs_directory_s *tmpfs_alloc_directory(void)
|
|
{
|
|
FAR struct tmpfs_directory_s *tdo;
|
|
size_t allocsize;
|
|
|
|
allocsize = sizeof(struct tmpfs_directory_s);
|
|
tdo = (FAR struct tmpfs_directory_s *)kmm_malloc(allocsize);
|
|
if (tdo == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* Initialize the new directory object */
|
|
|
|
tdo->tdo_atime = tmpfs_timestamp();
|
|
tdo->tdo_mtime = tdo->tdo_mtime;
|
|
tdo->tdo_ctime = tdo->tdo_mtime;
|
|
tdo->tdo_type = TMPFS_DIRECTORY;
|
|
tdo->tdo_refs = 0;
|
|
tdo->tdo_nentries = 0;
|
|
tdo->tdo_count = 0;
|
|
LOS_ListInit(&tdo->tdo_entry);
|
|
|
|
tdo->tdo_exclsem.ts_holder = TMPFS_NO_HOLDER;
|
|
tdo->tdo_exclsem.ts_count = 0;
|
|
if (sem_init(&tdo->tdo_exclsem.ts_sem, 0, 1) != 0)
|
|
{
|
|
PRINT_ERR("%s %d, sem_init failed!\n", __FUNCTION__, __LINE__);
|
|
kmm_free(tdo);
|
|
return NULL;
|
|
}
|
|
return tdo;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_create_directory
|
|
****************************************************************************/
|
|
|
|
static int tmpfs_create_directory(FAR struct tmpfs_s *fs,
|
|
FAR const char *relpath,
|
|
FAR struct tmpfs_directory_s *parent_input,
|
|
FAR struct tmpfs_directory_s **tdo)
|
|
{
|
|
FAR struct tmpfs_directory_s *parent;
|
|
FAR struct tmpfs_directory_s *newtdo;
|
|
struct tmpfs_dirent_s *tde;
|
|
FAR char *copy;
|
|
int ret;
|
|
|
|
/* Duplicate the path variable so that we can modify it */
|
|
|
|
copy = strdup(relpath);
|
|
if (copy == NULL)
|
|
{
|
|
return -ENOSPC;
|
|
}
|
|
|
|
/* Separate the path into the file name and the path to the parent
|
|
* directory.
|
|
*/
|
|
if (parent_input == NULL)
|
|
{
|
|
/* No subdirectories... use the root directory */
|
|
|
|
parent = (FAR struct tmpfs_directory_s *)fs->tfs_root.tde_object;
|
|
|
|
tmpfs_lock_directory(parent);
|
|
parent->tdo_refs++;
|
|
}
|
|
else
|
|
{
|
|
parent = parent_input;
|
|
}
|
|
|
|
/* Verify that no object of this name already exists in the directory */
|
|
if (parent == NULL)
|
|
{
|
|
ret = -EEXIST;
|
|
goto errout_with_copy;
|
|
}
|
|
tde = tmpfs_find_dirent(parent, copy);
|
|
if (tde != NULL)
|
|
{
|
|
/* Something with this name already exists in the directory.
|
|
* OR perhaps some fatal error occurred.
|
|
*/
|
|
|
|
ret = -EEXIST;
|
|
|
|
goto errout_with_parent;
|
|
}
|
|
|
|
/* Allocate an empty directory object. NOTE that there is no reference on
|
|
* the new directory and the object is not locked.
|
|
*/
|
|
|
|
newtdo = tmpfs_alloc_directory();
|
|
if (newtdo == NULL)
|
|
{
|
|
ret = -ENOSPC;
|
|
goto errout_with_parent;
|
|
}
|
|
|
|
/* Then add the new, empty file to the directory */
|
|
|
|
ret = tmpfs_add_dirent(&parent, (FAR struct tmpfs_object_s *)newtdo, copy);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_directory;
|
|
}
|
|
|
|
/* Free the copy of the relpath, release our reference to the parent directory,
|
|
* and return success
|
|
*/
|
|
if (parent->tdo_refs > 0)
|
|
{
|
|
parent->tdo_refs--;
|
|
}
|
|
tmpfs_unlock_directory(parent);
|
|
kmm_free(copy);
|
|
|
|
/* Return the (unlocked, unreferenced) directory object to the caller */
|
|
|
|
if (tdo != NULL)
|
|
{
|
|
*tdo = newtdo;
|
|
}
|
|
|
|
return OK;
|
|
|
|
/* Error exits */
|
|
|
|
errout_with_directory:
|
|
sem_destroy(&newtdo->tdo_exclsem.ts_sem);
|
|
kmm_free(newtdo);
|
|
|
|
errout_with_parent:
|
|
if (parent->tdo_refs > 0)
|
|
{
|
|
parent->tdo_refs--;
|
|
}
|
|
tmpfs_unlock_directory(parent);
|
|
|
|
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 = NULL;
|
|
FAR struct tmpfs_dirent_s *tde;
|
|
FAR struct tmpfs_directory_s *tdo = NULL;
|
|
FAR struct tmpfs_directory_s *next_tdo;
|
|
FAR char *segment;
|
|
FAR char *next_segment;
|
|
FAR char *tkptr;
|
|
FAR char *copy;
|
|
|
|
/* Make a copy of the path (so that we can modify it via strtok) */
|
|
|
|
copy = strdup(relpath);
|
|
if (copy == NULL)
|
|
{
|
|
return -ENOSPC;
|
|
}
|
|
|
|
/* Traverse the file system for any object with the matching name */
|
|
|
|
to = fs->tfs_root.tde_object;
|
|
next_tdo = (FAR struct tmpfs_directory_s *)fs->tfs_root.tde_object;
|
|
tdo = next_tdo;
|
|
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 next directory. */
|
|
|
|
tdo = next_tdo;
|
|
|
|
/* Find the TMPFS object with the next segment name in the current
|
|
* directory.
|
|
*/
|
|
|
|
tde = tmpfs_find_dirent(tdo, segment);
|
|
if (tde == NULL)
|
|
{
|
|
/* No object with this name exists in the directory. */
|
|
|
|
kmm_free(copy);
|
|
return -ENOENT;
|
|
}
|
|
|
|
to = tde->tde_object;
|
|
|
|
/* 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 intermediate
|
|
* segments do no correspond to directories.
|
|
*/
|
|
|
|
kmm_free(copy);
|
|
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_tdo = (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)
|
|
{
|
|
if (tdo != NULL)
|
|
{
|
|
/* Get exclusive access to the parent and increment the reference
|
|
* count on the object.
|
|
*/
|
|
|
|
tmpfs_lock_directory(tdo);
|
|
tdo->tdo_refs++;
|
|
}
|
|
|
|
*parent = tdo;
|
|
}
|
|
|
|
if (object)
|
|
{
|
|
if (to != NULL)
|
|
{
|
|
/* 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. If successful, tmpfs_find_object() will
|
|
* lock both the object and the parent directory and will increment the
|
|
* reference count on both.
|
|
*/
|
|
|
|
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 the object and its parent and return an error */
|
|
|
|
tmpfs_release_lockedobject(to);
|
|
|
|
if (parent)
|
|
{
|
|
FAR struct tmpfs_directory_s *tdo = *parent;
|
|
|
|
tdo->tdo_refs--;
|
|
tmpfs_unlock_directory(tdo);
|
|
}
|
|
|
|
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 the object and its parent and return an error */
|
|
|
|
tmpfs_release_lockedobject(to);
|
|
|
|
if (parent)
|
|
{
|
|
FAR struct tmpfs_directory_s *tmptdo = *parent;
|
|
|
|
tmptdo->tdo_refs--;
|
|
tmpfs_unlock_directory(tmptdo);
|
|
}
|
|
|
|
ret = -ENOTDIR;
|
|
}
|
|
|
|
/* Return the verified file object */
|
|
|
|
*tdo = (FAR struct tmpfs_directory_s *)to;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_close
|
|
****************************************************************************/
|
|
|
|
int tmpfs_close(struct file *filep)
|
|
{
|
|
FAR struct tmpfs_file_s *tfo;
|
|
|
|
finfo("filep: %p\n", filep);
|
|
DEBUGASSERT(filep != NULL);
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
tfo = (FAR struct tmpfs_file_s *)(filep->f_vnode->data);
|
|
if (tfo == NULL)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
/* Get exclusive access to the file */
|
|
|
|
tmpfs_lock_file(tfo);
|
|
|
|
/* Decrement the reference count on the file */
|
|
|
|
if (tfo->tfo_refs > 0)
|
|
{
|
|
tfo->tfo_refs--;
|
|
}
|
|
|
|
/* 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->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.
|
|
*/
|
|
|
|
(void)sem_destroy(&tfo->tfo_exclsem.ts_sem);
|
|
kmm_free(tfo->tfo_data);
|
|
kmm_free(tfo);
|
|
return OK;
|
|
}
|
|
|
|
/* Release the lock on the file */
|
|
|
|
tmpfs_unlock_file(tfo);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_read
|
|
****************************************************************************/
|
|
|
|
ssize_t tmpfs_read(struct file *filep, char *buffer, size_t buflen)
|
|
{
|
|
FAR struct tmpfs_file_s *tfo;
|
|
ssize_t nread;
|
|
loff_t startpos;
|
|
loff_t endpos;
|
|
|
|
finfo("filep: %p buffer: %p buflen: %lu\n",
|
|
filep, buffer, (unsigned long)buflen);
|
|
DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
tfo = (FAR struct tmpfs_file_s *)(filep->f_vnode->data);
|
|
if (tfo == NULL)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
if (filep->f_pos >= tfo->tfo_size || buflen == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* 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 */
|
|
|
|
if (LOS_CopyFromKernel(buffer, buflen, &tfo->tfo_data[startpos], nread) != 0)
|
|
{
|
|
tmpfs_unlock_file(tfo);
|
|
return -EINVAL;
|
|
}
|
|
filep->f_pos += nread;
|
|
|
|
/* Update the node's access time */
|
|
|
|
tfo->tfo_atime = tmpfs_timestamp();
|
|
|
|
/* Release the lock on the file */
|
|
|
|
tmpfs_unlock_file(tfo);
|
|
return nread;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_create
|
|
****************************************************************************/
|
|
|
|
int tmpfs_create(struct Vnode *dvp, const char *path, int mode, struct Vnode **vpp)
|
|
{
|
|
struct Vnode *vp = NULL;
|
|
FAR struct tmpfs_file_s *tfo;
|
|
FAR struct tmpfs_s *fs;
|
|
int ret = 0;
|
|
FAR struct tmpfs_directory_s *parent_tdo = NULL;
|
|
|
|
if (dvp == NULL)
|
|
{
|
|
return -ENOENT;
|
|
}
|
|
|
|
fs = dvp->originMount->data;
|
|
if (fs == NULL)
|
|
{
|
|
return -ENOENT;
|
|
}
|
|
|
|
tmpfs_lock(fs);
|
|
|
|
if (dvp->data != NULL)
|
|
{
|
|
parent_tdo = (FAR struct tmpfs_directory_s *)(dvp->data);
|
|
}
|
|
|
|
ret = tmpfs_create_file(fs, path, parent_tdo, &tfo);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_fslock;
|
|
}
|
|
|
|
ret = VnodeAlloc(&tmpfs_vops, &vp);
|
|
if (ret != 0)
|
|
{
|
|
tmpfs_unlock_file(tfo);
|
|
goto errout_with_fslock;
|
|
}
|
|
vp->parent = dvp;
|
|
vp->vop = dvp->vop;
|
|
vp->fop = dvp->fop;
|
|
vp->data = tfo;
|
|
vp->originMount = dvp->originMount;
|
|
vp->type = VNODE_TYPE_REG;
|
|
vp->mode = type_to_mode(vp->type, fs->permission);
|
|
vp->gid = fs->gid;
|
|
vp->uid = fs->uid;
|
|
|
|
ret = VfsHashInsert(vp, (uint32_t)tfo);
|
|
|
|
*vpp = vp;
|
|
tmpfs_unlock_file(tfo);
|
|
errout_with_fslock:
|
|
tmpfs_unlock(fs);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* Name: los_set_ramfs_unit
|
|
****************************************************************************/
|
|
|
|
static spinlock_t tmpfs_alloc_unit_lock;
|
|
bool is_tmpfs_lock_init = false;
|
|
unsigned int g_tmpfs_alloc_unit = 0;
|
|
|
|
void los_set_ramfs_unit(off_t size)
|
|
{
|
|
if (is_tmpfs_lock_init && size >= 0)
|
|
{
|
|
spin_lock(&tmpfs_alloc_unit_lock);
|
|
g_tmpfs_alloc_unit = size;
|
|
spin_unlock(&tmpfs_alloc_unit_lock);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_write
|
|
****************************************************************************/
|
|
|
|
ssize_t tmpfs_write(struct file *filep, const char *buffer, size_t buflen)
|
|
{
|
|
FAR struct tmpfs_file_s *tfo;
|
|
ssize_t nwritten;
|
|
loff_t startpos;
|
|
loff_t endpos;
|
|
int ret;
|
|
int alloc;
|
|
char *data;
|
|
|
|
finfo("filep: %p buffer: %p buflen: %lu\n",
|
|
filep, buffer, (unsigned long)buflen);
|
|
DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
|
|
|
|
if (buflen == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
tfo = (FAR struct tmpfs_file_s *)(filep->f_vnode->data);
|
|
if (tfo == NULL)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
/* Get exclusive access to the file */
|
|
|
|
tmpfs_lock_file(tfo);
|
|
|
|
/* Handle attempts to write beyond the end of the file */
|
|
|
|
startpos = filep->f_pos;
|
|
nwritten = buflen;
|
|
endpos = startpos + buflen;
|
|
|
|
if (startpos < 0)
|
|
{
|
|
ret = -EPERM;
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
if (endpos > tfo->tfo_size)
|
|
{
|
|
spin_lock(&tmpfs_alloc_unit_lock);
|
|
alloc = (g_tmpfs_alloc_unit > buflen) ? g_tmpfs_alloc_unit : buflen;
|
|
spin_unlock(&tmpfs_alloc_unit_lock);
|
|
data = (char *)malloc(startpos + alloc);
|
|
if (!data)
|
|
{
|
|
ret = -ENOSPC;
|
|
goto errout_with_lock;
|
|
}
|
|
if (tfo->tfo_size)
|
|
{
|
|
ret = memcpy_s(data, startpos + alloc, tfo->tfo_data, tfo->tfo_size);
|
|
if (ret != EOK)
|
|
{
|
|
ret = -1;
|
|
free(data);
|
|
goto errout_with_lock;
|
|
}
|
|
free(tfo->tfo_data);
|
|
}
|
|
if (startpos > tfo->tfo_size)
|
|
{
|
|
(void)memset_s(data + tfo->tfo_size, startpos + alloc - tfo->tfo_size, 0, startpos - tfo->tfo_size);
|
|
}
|
|
|
|
tfo->tfo_data = data;
|
|
tfo->tfo_size = startpos + alloc;
|
|
}
|
|
|
|
/* Copy data from the memory object to the user buffer */
|
|
if (LOS_CopyToKernel(&tfo->tfo_data[startpos], nwritten, buffer, nwritten) != 0)
|
|
{
|
|
ret = -EINVAL;
|
|
goto errout_with_lock;
|
|
}
|
|
filep->f_pos += nwritten;
|
|
|
|
/* Update the modified and access times of the node */
|
|
|
|
tfo->tfo_ctime = tfo->tfo_mtime = tmpfs_timestamp();
|
|
|
|
/* Release the lock on the file */
|
|
|
|
tmpfs_unlock_file(tfo);
|
|
return nwritten;
|
|
|
|
errout_with_lock:
|
|
tmpfs_unlock_file(tfo);
|
|
return (ssize_t)ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_seek
|
|
****************************************************************************/
|
|
|
|
off_t tmpfs_seek(FAR struct file *filep, off_t offset, int whence)
|
|
{
|
|
FAR struct tmpfs_file_s *tfo;
|
|
off_t position;
|
|
|
|
finfo("filep: %p\n", filep);
|
|
DEBUGASSERT(filep->f_priv != NULL && filep->f_vnode != NULL);
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
tfo = (FAR struct tmpfs_file_s *)(filep->f_vnode->data);
|
|
if (tfo == NULL)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
/* 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 beyond 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 (position < 0)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Save the new file position */
|
|
|
|
filep->f_pos = position;
|
|
return position;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_ioctl
|
|
****************************************************************************/
|
|
|
|
int tmpfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_opendir
|
|
****************************************************************************/
|
|
|
|
int tmpfs_opendir(struct Vnode *vp, struct fs_dirent_s *dir)
|
|
{
|
|
FAR struct tmpfs_s *fs;
|
|
FAR struct tmpfs_directory_s *tdo;
|
|
int ret = 0;
|
|
FAR struct fs_tmpfsdir_s *tmp;
|
|
|
|
finfo("vp: %p dir: %p\n", vp, dir);
|
|
DEBUGASSERT(vp != NULL && dir != NULL);
|
|
|
|
/* Get the mountpoint private data from the inode structure */
|
|
|
|
fs = vp->originMount->data;
|
|
DEBUGASSERT(fs != NULL);
|
|
|
|
tmp = (FAR struct fs_tmpfsdir_s *)malloc(sizeof(struct fs_tmpfsdir_s));
|
|
if (!tmp)
|
|
{
|
|
return -ENOSPC;
|
|
}
|
|
|
|
/* Get exclusive access to the file system */
|
|
|
|
tmpfs_lock(fs);
|
|
|
|
/* 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.
|
|
*/
|
|
|
|
if (vp->data != NULL)
|
|
{
|
|
tdo = (FAR struct tmpfs_directory_s *)vp->data;
|
|
}
|
|
else
|
|
{
|
|
tdo = (FAR struct tmpfs_directory_s *)fs->tfs_root.tde_object;
|
|
}
|
|
|
|
if (tdo == NULL)
|
|
{
|
|
free(tmp);
|
|
tmpfs_unlock(fs);
|
|
return -EINTR;
|
|
}
|
|
tmp->tf_tdo = tdo;
|
|
tmp->tf_index = 0;
|
|
dir->u.fs_dir = (fs_dir_s)tmp;
|
|
tdo->tdo_count++;
|
|
tdo->tdo_refs++;
|
|
tmpfs_unlock_directory(tdo);
|
|
|
|
/* Release the lock on the file system and return the result */
|
|
|
|
tmpfs_unlock(fs);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_closedir
|
|
****************************************************************************/
|
|
|
|
int tmpfs_closedir(struct Vnode *vp, struct fs_dirent_s *dir)
|
|
{
|
|
FAR struct tmpfs_directory_s *tdo;
|
|
struct fs_tmpfsdir_s *tmp;
|
|
|
|
finfo("vp: %p dir: %p\n", vp, dir);
|
|
DEBUGASSERT(vp != NULL && dir != NULL);
|
|
|
|
/* Get the directory structure from the dir argument */
|
|
|
|
tmp = (FAR struct fs_tmpfsdir_s *)dir->u.fs_dir;
|
|
if (tmp == NULL)
|
|
{
|
|
return -ENOENT;
|
|
}
|
|
tdo = tmp->tf_tdo;
|
|
DEBUGASSERT(tdo != NULL);
|
|
|
|
/* Decrement the reference count on the directory object */
|
|
|
|
tmpfs_lock_directory(tdo);
|
|
if (tdo->tdo_count == 1)
|
|
{
|
|
LOS_DL_LIST *node = tdo->tdo_entry.pstNext;
|
|
FAR struct tmpfs_dirent_s *tde;
|
|
while (node != &tdo->tdo_entry)
|
|
{
|
|
tde = (FAR struct tmpfs_dirent_s *)node;
|
|
node = node->pstNext;
|
|
if (tde->tde_inuse == false)
|
|
{
|
|
LOS_ListDelete(&tde->tde_node);
|
|
kmm_free(tde);
|
|
}
|
|
}
|
|
}
|
|
if (tdo->tdo_refs > 0)
|
|
{
|
|
tdo->tdo_refs--;
|
|
}
|
|
if (tdo->tdo_count > 0)
|
|
{
|
|
tdo->tdo_count--;
|
|
}
|
|
tmpfs_unlock_directory(tdo);
|
|
|
|
free(tmp);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_readdir
|
|
****************************************************************************/
|
|
|
|
int tmpfs_readdir(struct Vnode *vp, struct fs_dirent_s *dir)
|
|
{
|
|
FAR struct tmpfs_directory_s *tdo;
|
|
unsigned int index;
|
|
int ret;
|
|
struct fs_tmpfsdir_s *tmp;
|
|
LOS_DL_LIST *node;
|
|
FAR struct tmpfs_dirent_s *tde;
|
|
|
|
finfo("vp: %p dir: %p\n", vp, dir);
|
|
DEBUGASSERT(vp != NULL && dir != NULL);
|
|
|
|
/* Get the directory structure from the dir argument and lock it */
|
|
|
|
tmp = (FAR struct fs_tmpfsdir_s *)dir->u.fs_dir;
|
|
if (tmp == NULL)
|
|
{
|
|
return -ENOENT;
|
|
}
|
|
|
|
tdo = tmp->tf_tdo;
|
|
if (tdo == NULL)
|
|
{
|
|
return -ENOENT;
|
|
}
|
|
|
|
tmpfs_lock_directory(tdo);
|
|
|
|
/* Have we reached the end of the directory? */
|
|
|
|
index = tmp->tf_index;
|
|
node = tdo->tdo_entry.pstNext;
|
|
while (node != &tdo->tdo_entry && index != 0)
|
|
{
|
|
node = node->pstNext;
|
|
index--;
|
|
}
|
|
|
|
while (node != &tdo->tdo_entry)
|
|
{
|
|
tde = (FAR struct tmpfs_dirent_s *)node;
|
|
tmp->tf_index++;
|
|
if (tde->tde_inuse == true)
|
|
{
|
|
break;
|
|
}
|
|
node = node->pstNext;
|
|
}
|
|
|
|
if (node == &tdo->tdo_entry)
|
|
{
|
|
/* We signal the end of the directory by returning the special error:
|
|
* -ENOENT
|
|
*/
|
|
|
|
finfo("End of directory\n");
|
|
ret = -ENOENT;
|
|
}
|
|
else
|
|
{
|
|
FAR struct tmpfs_object_s *to;
|
|
|
|
/* Does this entry refer to a file or a directory object? */
|
|
|
|
to = tde->tde_object;
|
|
DEBUGASSERT(to != NULL);
|
|
|
|
if (to->to_type == TMPFS_DIRECTORY)
|
|
{
|
|
/* A directory */
|
|
|
|
dir->fd_dir[0].d_type = DT_DIR;
|
|
}
|
|
else /* to->to_type == TMPFS_REGULAR) */
|
|
{
|
|
/* A regular file */
|
|
|
|
dir->fd_dir[0].d_type = DT_REG;
|
|
}
|
|
|
|
/* Copy the entry name */
|
|
|
|
(void)strncpy_s(dir->fd_dir[0].d_name, NAME_MAX + 1, tde->tde_name, NAME_MAX);
|
|
|
|
dir->fd_position++;
|
|
dir->fd_dir[0].d_off = dir->fd_position;
|
|
dir->fd_dir[0].d_reclen = (uint16_t)sizeof(struct dirent);
|
|
|
|
ret = 1; // 1 means current file num is 1
|
|
}
|
|
|
|
tmpfs_unlock_directory(tdo);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_rewinddir
|
|
****************************************************************************/
|
|
|
|
int tmpfs_rewinddir(struct Vnode *vp, struct fs_dirent_s *dir)
|
|
{
|
|
struct fs_tmpfsdir_s *tmp;
|
|
fvdbg("vp: %p dir: %p\n", vp, dir);
|
|
DEBUGASSERT(vp != NULL && dir != NULL);
|
|
tmp = (struct fs_tmpfsdir_s *)dir->u.fs_dir;
|
|
|
|
/* Set the readdir index to zero */
|
|
|
|
tmp->tf_index = 0;
|
|
return OK;
|
|
}
|
|
|
|
int tmpfs_truncate(struct Vnode *vp, off_t len)
|
|
{
|
|
FAR struct tmpfs_file_s *tfo = NULL;
|
|
|
|
tfo = vp->data;
|
|
tfo->tfo_size = 0;
|
|
|
|
if (tfo->tfo_data)
|
|
{
|
|
free(tfo->tfo_data);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_mount
|
|
****************************************************************************/
|
|
|
|
int tmpfs_mount(struct Mount *mnt, struct Vnode *device, const void *data)
|
|
{
|
|
FAR struct tmpfs_directory_s *tdo;
|
|
FAR struct tmpfs_s *fs = &tmpfs_superblock;
|
|
struct Vnode *vp = NULL;
|
|
int ret;
|
|
|
|
DEBUGASSERT(device == NULL && data != NULL);
|
|
|
|
if (fs->tfs_root.tde_object != NULL)
|
|
{
|
|
return -EPERM;
|
|
}
|
|
|
|
/* Create a root file system. This is like a single directory entry in
|
|
* the file system structure.
|
|
*/
|
|
|
|
tdo = tmpfs_alloc_directory();
|
|
if (tdo == NULL)
|
|
{
|
|
return -ENOSPC;
|
|
}
|
|
|
|
LOS_ListInit(&fs->tfs_root.tde_node);
|
|
fs->tfs_root.tde_object = (FAR struct tmpfs_object_s *)tdo;
|
|
fs->tfs_root.tde_name = NULL;
|
|
|
|
/* Set up the backward link (to support reallocation) */
|
|
|
|
tdo->tdo_dirent = &fs->tfs_root;
|
|
|
|
/* Initialize the file system state */
|
|
|
|
fs->tfs_exclsem.ts_holder = TMPFS_NO_HOLDER;
|
|
fs->tfs_exclsem.ts_count = 0;
|
|
sem_init(&fs->tfs_exclsem.ts_sem, 0, 1);
|
|
|
|
/* Return the new file system handle */
|
|
|
|
spin_lock_init(&tmpfs_alloc_unit_lock);
|
|
is_tmpfs_lock_init = true;
|
|
|
|
ret = VnodeAlloc(&tmpfs_vops, &vp);
|
|
if (ret != 0)
|
|
{
|
|
ret = ENOMEM;
|
|
goto ERROR_WITH_FSWIN;
|
|
}
|
|
|
|
fs->permission = mnt->vnodeBeCovered->mode & 0777;
|
|
fs->gid = mnt->vnodeBeCovered->gid;
|
|
fs->uid = mnt->vnodeBeCovered->uid;
|
|
vp->originMount = mnt;
|
|
vp->fop = &tmpfs_fops;
|
|
vp->type = VNODE_TYPE_DIR;
|
|
vp->data = NULL;
|
|
vp->mode = type_to_mode(vp->type, fs->permission);
|
|
vp->gid = fs->gid;
|
|
vp->uid = fs->uid;
|
|
mnt->data = fs;
|
|
mnt->vnodeCovered = vp;
|
|
|
|
return OK;
|
|
|
|
ERROR_WITH_FSWIN:
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_unmount
|
|
****************************************************************************/
|
|
|
|
int tmpfs_unmount(struct Mount *mnt, struct Vnode **blkdriver)
|
|
{
|
|
FAR struct tmpfs_s *fs = (FAR struct tmpfs_s *)mnt->data;
|
|
FAR struct tmpfs_directory_s *tdo;
|
|
int ret = 0;
|
|
|
|
finfo("handle: %p blkdriver: %p \n", handle, blkdriver);
|
|
DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
|
|
|
|
/* Lock the file system */
|
|
|
|
tmpfs_lock(fs);
|
|
|
|
tdo = (FAR struct tmpfs_directory_s *)fs->tfs_root.tde_object;
|
|
if (tdo == NULL)
|
|
{
|
|
ret = -EINVAL;
|
|
goto errout_with_objects;
|
|
}
|
|
|
|
if (tdo->tdo_nentries > 0 || tdo->tdo_refs > 1)
|
|
{
|
|
ret = -EBUSY;
|
|
goto errout_with_objects;
|
|
}
|
|
|
|
/* Now we can destroy the root file system and the file system itself. */
|
|
|
|
sem_destroy(&tdo->tdo_exclsem.ts_sem);
|
|
kmm_free(tdo);
|
|
|
|
sem_destroy(&fs->tfs_exclsem.ts_sem);
|
|
fs->tfs_root.tde_object = NULL;
|
|
|
|
tmpfs_unlock(fs);
|
|
is_tmpfs_lock_init = false;
|
|
return ret;
|
|
|
|
errout_with_objects:
|
|
tmpfs_unlock(fs);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int tmpfs_lookup(struct Vnode *parent, const char *relPath, int len, struct Vnode **vpp)
|
|
{
|
|
// 1. when first time create file, lookup fail, then call tmpfs_create.
|
|
// 2. when ls, lookup will success to show.
|
|
// 3. when cd, lookup success.
|
|
FAR struct tmpfs_object_s *to;
|
|
FAR struct tmpfs_s *fs;
|
|
struct tmpfs_dirent_s *tde = NULL;
|
|
FAR struct tmpfs_directory_s *parent_tdo;
|
|
|
|
struct Vnode *vp = NULL;
|
|
int ret = 0;
|
|
char filename[len + 1];
|
|
ret = memcpy_s(filename, (len + 1), relPath, len);
|
|
if (ret != 0)
|
|
{
|
|
ret = -ENOMEM;
|
|
goto errout;
|
|
}
|
|
filename[len] = '\0';
|
|
|
|
fs = parent->originMount->data;
|
|
DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
|
|
|
|
tmpfs_lock(fs);
|
|
|
|
parent_tdo = (FAR struct tmpfs_directory_s *)(parent->data);
|
|
|
|
if (parent_tdo == NULL)
|
|
{
|
|
// if parent_tdo don't exist, find file in root.
|
|
ret = tmpfs_find_object(fs, filename, &to, NULL);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_lock;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PRINTK("tmpfs_lookup parent_tdo->tdo_type = %d\n", parent_tdo->tdo_type);
|
|
if (parent_tdo->tdo_type != TMPFS_DIRECTORY)
|
|
{
|
|
ret = -ENOENT;
|
|
goto errout_with_lock;
|
|
}
|
|
// if parent_tdo exist£¬search for the relationship between parent_tdo and the current dirname.
|
|
tde = tmpfs_find_dirent(parent_tdo, filename);
|
|
if (tde == NULL)
|
|
{
|
|
ret = -ENOENT;
|
|
goto errout_with_lock;
|
|
}
|
|
to = tde->tde_object;
|
|
}
|
|
|
|
if (to == NULL)
|
|
{
|
|
ret = -ENOENT;
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
ret = VfsHashGet(parent->originMount, (uint32_t)to, &vp, NULL, NULL);
|
|
if (ret != 0)
|
|
{
|
|
ret = -ENOENT;
|
|
goto errout_with_objects;
|
|
}
|
|
|
|
if (vp == NULL)
|
|
{
|
|
ret = VnodeAlloc(&tmpfs_vops, &vp);
|
|
if (ret != 0)
|
|
{
|
|
PRINTK("%s-%d \n", __FUNCTION__, __LINE__);
|
|
goto errout_with_objects;
|
|
}
|
|
|
|
vp->parent = parent;
|
|
vp->vop = parent->vop;
|
|
vp->fop = parent->fop;
|
|
vp->data = to;
|
|
vp->originMount = parent->originMount;
|
|
vp->type = to->to_type == TMPFS_REGULAR ? VNODE_TYPE_REG : VNODE_TYPE_DIR;
|
|
vp->mode = type_to_mode(vp->type, fs->permission);
|
|
vp->gid = fs->gid;
|
|
vp->uid = fs->uid;
|
|
|
|
ret = VfsHashInsert(vp, (uint32_t)to);
|
|
}
|
|
|
|
*vpp = vp;
|
|
|
|
tmpfs_release_lockedobject(to);
|
|
tmpfs_unlock(fs);
|
|
|
|
return 0;
|
|
|
|
errout_with_objects:
|
|
tmpfs_release_lockedobject(to);
|
|
errout_with_lock:
|
|
tmpfs_unlock(fs);
|
|
errout:
|
|
return ret;
|
|
}
|
|
|
|
int tmpfs_reclaim(struct Vnode *vp)
|
|
{
|
|
vp->data = NULL;
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_statfs
|
|
****************************************************************************/
|
|
|
|
int tmpfs_statfs(struct Mount *mp, struct statfs *sbp)
|
|
{
|
|
(void)memset_s(sbp, sizeof(struct statfs), 0, sizeof(struct statfs));
|
|
|
|
sbp->f_type = TMPFS_MAGIC;
|
|
sbp->f_flags = mp->mountFlags;
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_unlink
|
|
****************************************************************************/
|
|
|
|
int tmpfs_unlink(struct Vnode *parent, struct Vnode *node, char *relpath)
|
|
{
|
|
FAR struct tmpfs_s *fs;
|
|
FAR struct tmpfs_directory_s *parent_dir;
|
|
FAR struct tmpfs_file_s *tfo = NULL;
|
|
int ret;
|
|
|
|
finfo("mountpt: %p node: %p relpath: %s\n", parent, node, relpath);
|
|
DEBUGASSERT(parent != NULL && node != NULL && relpath != NULL);
|
|
|
|
if (strlen(relpath) == 0)
|
|
{
|
|
return -EISDIR;
|
|
}
|
|
|
|
/* Get the file system structure from the inode reference. */
|
|
if (node->originMount == NULL)
|
|
{
|
|
return -EISDIR;
|
|
}
|
|
|
|
fs = node->originMount->data;
|
|
if (fs == NULL)
|
|
{
|
|
return -EISDIR;
|
|
}
|
|
|
|
DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
|
|
|
|
/* Get exclusive access to the file system */
|
|
|
|
tmpfs_lock(fs);
|
|
|
|
/* Find the file object and parent directory associated with this relative
|
|
* path. If successful, tmpfs_find_file will lock both the file object
|
|
* and the parent directory and take one reference count on each.
|
|
*/
|
|
|
|
parent_dir = (FAR struct tmpfs_directory_s *)(parent->data);
|
|
if (parent_dir == NULL)
|
|
{
|
|
ret = tmpfs_find_file(fs, relpath, &tfo, &parent_dir);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_lock;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tfo = (FAR struct tmpfs_file_s *)node->data;
|
|
}
|
|
|
|
if (tfo == NULL || parent_dir == NULL)
|
|
{
|
|
ret = -EISDIR;
|
|
goto errout_with_lock;
|
|
}
|
|
DEBUGASSERT(tfo != NULL);
|
|
|
|
/* Remove the file from parent directory */
|
|
ret = tmpfs_remove_dirent(parent_dir, (struct tmpfs_object_s *)tfo);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_objects;
|
|
}
|
|
|
|
/* If the reference count is not one, then just mark the file as
|
|
* unlinked
|
|
*/
|
|
|
|
if (tfo->tfo_refs > 1)
|
|
{
|
|
/* Make the file object as unlinked */
|
|
|
|
tfo->tfo_flags |= TFO_FLAG_UNLINKED;
|
|
|
|
/* Release the reference count on the file object */
|
|
|
|
tfo->tfo_refs--;
|
|
tmpfs_unlock_file(tfo);
|
|
}
|
|
|
|
/* Otherwise we can free the object now */
|
|
|
|
else
|
|
{
|
|
sem_destroy(&tfo->tfo_exclsem.ts_sem);
|
|
kmm_free(tfo->tfo_data);
|
|
kmm_free(tfo);
|
|
node->data = NULL;
|
|
}
|
|
|
|
/* Release the reference and lock on the parent directory */
|
|
|
|
if (parent_dir->tdo_refs > 0)
|
|
{
|
|
parent_dir->tdo_refs--;
|
|
}
|
|
tmpfs_unlock_directory(parent_dir);
|
|
tmpfs_unlock(fs);
|
|
|
|
return OK;
|
|
|
|
errout_with_objects:
|
|
tmpfs_release_lockedfile(tfo);
|
|
|
|
if (parent_dir->tdo_refs > 0)
|
|
{
|
|
parent_dir->tdo_refs--;
|
|
}
|
|
|
|
tmpfs_unlock_directory(parent_dir);
|
|
|
|
errout_with_lock:
|
|
tmpfs_unlock(fs);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_mkdir
|
|
****************************************************************************/
|
|
|
|
int tmpfs_mkdir(struct Vnode *parent, const char *relpath, mode_t mode, struct Vnode **vpp)
|
|
{
|
|
FAR struct tmpfs_s *fs;
|
|
struct Vnode *vp = NULL;
|
|
FAR struct tmpfs_directory_s *tdo;
|
|
FAR struct tmpfs_directory_s *parent_tdo = NULL;
|
|
int ret;
|
|
|
|
finfo("parent: %p relpath: %s mode: %04x\n", parent, relpath, mode);
|
|
DEBUGASSERT(parent != NULL && relpath != NULL);
|
|
|
|
if (strlen(relpath) == 0)
|
|
{
|
|
return -EEXIST;
|
|
}
|
|
|
|
/* Get the file system structure from the inode reference. */
|
|
|
|
fs = parent->originMount->data;
|
|
DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
|
|
|
|
/* Get exclusive access to the file system */
|
|
|
|
tmpfs_lock(fs);
|
|
|
|
if (parent->data != NULL)
|
|
{
|
|
parent_tdo = (FAR struct tmpfs_directory_s *)(parent->data);
|
|
}
|
|
/* Create the directory. */
|
|
ret = tmpfs_create_directory(fs, relpath, parent_tdo, &tdo);
|
|
if (ret != OK)
|
|
{
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
ret = VnodeAlloc(&tmpfs_vops, &vp);
|
|
if (ret != 0)
|
|
{
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
vp->parent = parent;
|
|
vp->vop = parent->vop;
|
|
vp->fop = parent->fop;
|
|
vp->data = tdo;
|
|
vp->originMount = parent->originMount;
|
|
vp->type = VNODE_TYPE_DIR;
|
|
vp->mode = type_to_mode(vp->type, fs->permission);
|
|
vp->gid = fs->gid;
|
|
vp->uid = fs->uid;
|
|
|
|
ret = VfsHashInsert(vp, (uint32_t)tdo);
|
|
*vpp = vp;
|
|
|
|
errout_with_lock:
|
|
tmpfs_unlock(fs);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_rmdir
|
|
****************************************************************************/
|
|
|
|
int tmpfs_rmdir(struct Vnode *parent, struct Vnode *target, char *dirname)
|
|
{
|
|
FAR struct tmpfs_s *fs;
|
|
FAR struct tmpfs_directory_s *parent_dir;
|
|
FAR struct tmpfs_directory_s *tdo;
|
|
int ret = 0;
|
|
|
|
finfo("parent: %p relpath: %s\n", target, relpath);
|
|
DEBUGASSERT(parent != NULL && target != NULL && relpath != NULL);
|
|
|
|
/* Get the file system structure from the inode reference. */
|
|
fs = parent->originMount->data;
|
|
if (fs == NULL)
|
|
{
|
|
return -EISDIR;
|
|
}
|
|
DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
|
|
|
|
/* Get exclusive access to the file system */
|
|
|
|
tmpfs_lock(fs);
|
|
|
|
/* Find the directory object and parent directory associated with this
|
|
* relative path. If successful, tmpfs_find_file will lock both the
|
|
* directory object and the parent directory and take one reference count
|
|
* on each.
|
|
*/
|
|
parent_dir = (FAR struct tmpfs_directory_s *)(parent->data);
|
|
if (parent_dir == NULL)
|
|
{
|
|
ret = tmpfs_find_directory(fs, dirname, &tdo, &parent_dir);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_lock;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tdo = (FAR struct tmpfs_directory_s *)target->data;
|
|
}
|
|
|
|
if (tdo == NULL || tdo->tdo_type != TMPFS_DIRECTORY)
|
|
{
|
|
ret = -EISDIR;
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
/* Is the directory empty? We cannot remove directories that still
|
|
* contain references to file system objects. No can we remove the
|
|
* directory if there are outstanding references on it (other than
|
|
* our reference).
|
|
*/
|
|
|
|
if (tdo->tdo_nentries > 0 || tdo->tdo_refs > 1)
|
|
{
|
|
ret = -EBUSY;
|
|
goto errout_with_objects;
|
|
}
|
|
/* Remove the directory from parent directory */
|
|
ret = tmpfs_remove_dirent(parent_dir, (struct tmpfs_object_s *)tdo);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_objects;
|
|
}
|
|
|
|
/* Free the directory object */
|
|
|
|
sem_destroy(&tdo->tdo_exclsem.ts_sem);
|
|
kmm_free(tdo);
|
|
target->data = NULL;
|
|
|
|
/* Release the reference and lock on the parent directory */
|
|
|
|
if (parent_dir->tdo_refs > 0)
|
|
{
|
|
parent_dir->tdo_refs--;
|
|
}
|
|
|
|
tmpfs_unlock_directory(parent_dir);
|
|
tmpfs_unlock(fs);
|
|
|
|
return OK;
|
|
|
|
errout_with_objects:
|
|
if (tdo->tdo_refs > 0)
|
|
{
|
|
tdo->tdo_refs--;
|
|
}
|
|
tmpfs_unlock_directory(tdo);
|
|
|
|
if (parent_dir->tdo_refs > 0)
|
|
{
|
|
parent_dir->tdo_refs--;
|
|
}
|
|
tmpfs_unlock_directory(parent_dir);
|
|
|
|
errout_with_lock:
|
|
tmpfs_unlock(fs);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_rename
|
|
****************************************************************************/
|
|
|
|
int tmpfs_rename(struct Vnode *oldVnode, struct Vnode *newParent, const char *oldname, const char *newname)
|
|
{
|
|
FAR struct tmpfs_directory_s *oldparent;
|
|
FAR struct tmpfs_directory_s *newparent_tdo;
|
|
FAR struct tmpfs_object_s *old_to;
|
|
FAR struct tmpfs_dirent_s *tde;
|
|
FAR struct tmpfs_directory_s *tdo;
|
|
FAR struct tmpfs_file_s *tfo;
|
|
FAR struct tmpfs_s *fs;
|
|
FAR struct tmpfs_s *new_fs;
|
|
FAR struct Vnode *old_parent_vnode;
|
|
|
|
FAR char *copy;
|
|
int ret = 0;
|
|
unsigned int oldrelpath_len, newrelpath_len, cmp_namelen;
|
|
|
|
finfo("oldParent: %p newParent: %p oldname: %s newname: %s\n",
|
|
oldParent, newParent, oldname, newname);
|
|
|
|
oldrelpath_len = strlen(oldname);
|
|
newrelpath_len = strlen(newname);
|
|
|
|
cmp_namelen = (oldrelpath_len <= newrelpath_len) ? oldrelpath_len : newrelpath_len;
|
|
if (!cmp_namelen || ((!strncmp(oldname, newname, cmp_namelen)) &&
|
|
(oldname[cmp_namelen] == '/' ||
|
|
newname[cmp_namelen] == '/')))
|
|
{
|
|
return -EPERM;
|
|
}
|
|
|
|
/* Get the file system structure from the inode reference. */
|
|
if (oldVnode->parent == NULL)
|
|
{
|
|
return -EPERM;
|
|
}
|
|
|
|
fs = oldVnode->parent->originMount->data;
|
|
if (fs == NULL)
|
|
{
|
|
return -EPERM;
|
|
}
|
|
|
|
DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
|
|
|
|
/* Duplicate the newpath variable so that we can modify it */
|
|
|
|
copy = strdup(newname);
|
|
if (copy == NULL)
|
|
{
|
|
return -ENOSPC;
|
|
}
|
|
|
|
/* Get exclusive access to the file system */
|
|
|
|
tmpfs_lock(fs);
|
|
|
|
/* Separate the new path into the new file name and the path to the new
|
|
* parent directory.
|
|
*/
|
|
newparent_tdo = (FAR struct tmpfs_directory_s *)(newParent->data);
|
|
if (newparent_tdo == NULL)
|
|
{
|
|
new_fs = newParent->originMount->data;
|
|
newparent_tdo = (FAR struct tmpfs_directory_s *)new_fs->tfs_root.tde_object;
|
|
if (newparent_tdo == NULL)
|
|
{
|
|
ret = -ENOTEMPTY;
|
|
goto errout_with_lock;
|
|
}
|
|
tmpfs_lock_directory(newparent_tdo);
|
|
newparent_tdo->tdo_refs++;
|
|
}
|
|
|
|
/* Find the old object at oldpath. If successful, tmpfs_find_object()
|
|
* will lock both the object and the parent directory and will increment
|
|
* the reference count on both.
|
|
*/
|
|
old_parent_vnode = oldVnode->parent;
|
|
oldparent = (FAR struct tmpfs_directory_s *)(old_parent_vnode->data);
|
|
old_to = (FAR struct tmpfs_object_s *)oldVnode->data;
|
|
|
|
if (oldparent == NULL)
|
|
{
|
|
oldparent = (FAR struct tmpfs_directory_s *)fs->tfs_root.tde_object;
|
|
if (oldparent == NULL || old_to == NULL)
|
|
{
|
|
ret = -ENOTEMPTY;
|
|
goto errout_with_newparent;
|
|
}
|
|
}
|
|
|
|
tmpfs_lock_directory(oldparent);
|
|
tde = tmpfs_find_dirent(newparent_tdo, copy);
|
|
if (tde != NULL)
|
|
{
|
|
FAR struct tmpfs_object_s *new_to = tde->tde_object;
|
|
|
|
/* Cannot rename a directory to a noempty directory */
|
|
|
|
if (tde->tde_object->to_type == TMPFS_DIRECTORY)
|
|
{
|
|
tdo = (struct tmpfs_directory_s *)new_to;
|
|
if (tdo->tdo_nentries != 0)
|
|
{
|
|
ret = -ENOTEMPTY;
|
|
goto errout_with_oldparent;
|
|
}
|
|
}
|
|
|
|
/* Null rename, just return */
|
|
|
|
if (old_to == new_to)
|
|
{
|
|
ret = ENOERR;
|
|
goto errout_with_oldparent;
|
|
}
|
|
|
|
/* Check that we are renaming like-for-like */
|
|
|
|
if (old_to->to_type == TMPFS_REGULAR && new_to->to_type == TMPFS_DIRECTORY)
|
|
{
|
|
ret = -EISDIR;
|
|
goto errout_with_oldparent;
|
|
}
|
|
|
|
if (old_to->to_type == TMPFS_DIRECTORY && new_to->to_type == TMPFS_REGULAR)
|
|
{
|
|
ret = -ENOTDIR;
|
|
goto errout_with_oldparent;
|
|
}
|
|
|
|
/* Now delete the destination directory entry */
|
|
|
|
ret = tmpfs_remove_dirent(newparent_tdo, new_to);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_oldparent;
|
|
}
|
|
|
|
if (new_to->to_type == TMPFS_DIRECTORY)
|
|
{
|
|
(void)sem_destroy(&new_to->to_exclsem.ts_sem);
|
|
kmm_free(new_to);
|
|
}
|
|
else
|
|
{
|
|
tfo = (struct tmpfs_file_s *)new_to;
|
|
if (new_to->to_refs > 0)
|
|
{
|
|
/* Make the file object as unlinked */
|
|
|
|
tfo->tfo_flags |= TFO_FLAG_UNLINKED;
|
|
}
|
|
|
|
/* Otherwise we can free the object now */
|
|
|
|
else
|
|
{
|
|
(void)sem_destroy(&tfo->tfo_exclsem.ts_sem);
|
|
kmm_free(tfo->tfo_data);
|
|
kmm_free(tfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Remove the entry from the parent directory */
|
|
|
|
ret = tmpfs_remove_dirent(oldparent, old_to);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_oldparent;
|
|
}
|
|
|
|
/* Add an entry to the new parent directory. */
|
|
|
|
ret = tmpfs_add_dirent(&newparent_tdo, old_to, copy);
|
|
oldVnode->parent = newParent;
|
|
|
|
errout_with_oldparent:
|
|
if (oldparent == NULL)
|
|
{
|
|
tmpfs_unlock(fs);
|
|
kmm_free(copy);
|
|
return ret;
|
|
}
|
|
if (oldparent->tdo_refs > 0)
|
|
{
|
|
oldparent->tdo_refs--;
|
|
}
|
|
tmpfs_unlock_directory(oldparent);
|
|
tmpfs_release_lockedobject(old_to);
|
|
|
|
errout_with_newparent:
|
|
if (newparent_tdo == NULL)
|
|
{
|
|
tmpfs_unlock(fs);
|
|
kmm_free(copy);
|
|
return ret;
|
|
}
|
|
if (newparent_tdo->tdo_refs > 0)
|
|
{
|
|
newparent_tdo->tdo_refs--;
|
|
}
|
|
tmpfs_unlock_directory(newparent_tdo);
|
|
|
|
errout_with_lock:
|
|
tmpfs_unlock(fs);
|
|
kmm_free(copy);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_stat_common
|
|
****************************************************************************/
|
|
|
|
static void tmpfs_stat_common(FAR struct tmpfs_object_s *to,
|
|
FAR struct stat *buf)
|
|
{
|
|
size_t objsize;
|
|
|
|
/* Is the tmpfs object a regular file? */
|
|
|
|
(VOID)memset_s(buf, sizeof(struct stat), 0, sizeof(struct stat));
|
|
|
|
if (to->to_type == TMPFS_REGULAR)
|
|
{
|
|
FAR struct tmpfs_file_s *tfo =
|
|
(FAR struct tmpfs_file_s *)to;
|
|
|
|
/* -rwxrwxrwx */
|
|
|
|
buf->st_mode = S_IRWXO | S_IRWXG | S_IRWXU | S_IFREG;
|
|
buf->st_nlink = tfo->tfo_refs - 1;
|
|
|
|
/* Get the size of the object */
|
|
|
|
objsize = tfo->tfo_size;
|
|
}
|
|
else /* if (to->to_type == TMPFS_DIRECTORY) */
|
|
{
|
|
FAR struct tmpfs_directory_s *tdo =
|
|
(FAR struct tmpfs_directory_s *)to;
|
|
|
|
/* drwxrwxrwx */
|
|
|
|
buf->st_mode = S_IRWXO | S_IRWXG | S_IRWXU | S_IFDIR;
|
|
buf->st_nlink = tdo->tdo_nentries + tdo->tdo_refs;
|
|
|
|
/* Get the size of the object */
|
|
|
|
objsize = sizeof(struct tmpfs_directory_s);
|
|
}
|
|
|
|
/* Fake the rest of the information */
|
|
|
|
buf->st_size = objsize;
|
|
buf->st_blksize = 0;
|
|
buf->st_blocks = 0;
|
|
buf->st_atime = to->to_atime;
|
|
buf->st_mtime = to->to_mtime;
|
|
buf->st_ctime = to->to_ctime;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: tmpfs_stat
|
|
****************************************************************************/
|
|
|
|
int tmpfs_stat(struct Vnode *vp, struct stat *st)
|
|
{
|
|
FAR struct tmpfs_s *fs;
|
|
FAR struct tmpfs_object_s *to;
|
|
int ret;
|
|
|
|
finfo("vp=%p st=%p\n", vp, st);
|
|
DEBUGASSERT(vp != NULL && st != NULL);
|
|
/* Get the file system structure from the inode reference. */
|
|
|
|
fs = vp->originMount->data;
|
|
DEBUGASSERT(fs != NULL && fs->tfs_root.tde_object != NULL);
|
|
|
|
/* Get exclusive access to the file system */
|
|
|
|
tmpfs_lock(fs);
|
|
|
|
/* Find the tmpfs object at the relpath. If successful,
|
|
* tmpfs_find_object() will lock the object and increment the
|
|
* reference count on the object.
|
|
*/
|
|
if (vp->data != NULL)
|
|
{
|
|
to = (FAR struct tmpfs_object_s *)vp->data;
|
|
}
|
|
else
|
|
{
|
|
to = fs->tfs_root.tde_object;
|
|
}
|
|
to->to_refs++;
|
|
|
|
/* We found it... Return information about the file object in the stat
|
|
* buffer.
|
|
*/
|
|
if(to == NULL)
|
|
{
|
|
ret = -ENOENT;
|
|
goto errout_with_fslock;
|
|
}
|
|
DEBUGASSERT(to != NULL);
|
|
tmpfs_stat_common(to, st);
|
|
|
|
/* Unlock the object and return success */
|
|
|
|
tmpfs_release_lockedobject(to);
|
|
ret = OK;
|
|
|
|
errout_with_fslock:
|
|
tmpfs_unlock(fs);
|
|
return ret;
|
|
}
|
|
|
|
FSMAP_ENTRY(ramfs_fsmap, "ramfs", tmpfs_operations, FALSE, FALSE);
|
|
|
|
#endif
|
|
|