* fs/vfs/fs_pathcache.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <errno.h>
#include <nuttx/nuttx.h>
#include <nuttx/lib/lib.h>
#include <nuttx/lru_cache.h>
#include "inode/inode.h"
#include "fs_heap.h"
#include "vfs.h"
* Pre-processor Definitions
****************************************************************************/
* Private Types
****************************************************************************/
* Private Functions Prototypes
****************************************************************************/
static void pathcache_get(FAR void *value);
static void pathcache_put(FAR void *value);
* Private Data
****************************************************************************/
static struct lru_cache_s g_pathcache;
static const struct lru_cache_ops_s g_pathcache_ops =
{
.get_cb = pathcache_get,
.put_cb = pathcache_put,
};
* Private Functions
****************************************************************************/
* Name: pathcache_get
*
* Description:
* Increase reference count of a cached file entry.
*
* Input Parameters:
* value - Pointer to the cached file structure
*
****************************************************************************/
static void pathcache_get(FAR void *value)
{
FAR struct file *filep = value;
atomic_add(&filep->f_refs, 1);
}
* Name: pathcache_put
*
* Description:
* Decrease reference count of a cached file entry.
*
* Input Parameters:
* value - Pointer to the cached file structure
*
* Returned Value:
* Reference count before decrement
*
****************************************************************************/
static void pathcache_put(FAR void *value)
{
FAR struct file *filep = value;
if (atomic_sub(&filep->f_refs, 1) == 1)
{
filep->f_inode->u.i_mops->close(filep);
inode_release(filep->f_inode);
file_deallocate(filep);
}
}
* Name: pathcache_create
*
* Description:
* Create a cache entry from an opened file.
*
* Input Parameters:
* filep - File structure of the opened file
* abspath - Absolute path used as cache key
*
* Returned Value:
* OK on success; a negated errno value on failure
*
****************************************************************************/
static int pathcache_create(FAR struct file *filep, FAR char *abspath)
{
FAR struct file *cached_file;
FAR struct inode *inode;
int ret;
inode = filep->f_inode;
if (inode->u.i_mops->dup == NULL)
{
return -ENOSYS;
}
cached_file = file_allocate();
if (cached_file == NULL)
{
return -ENOMEM;
}
inode_addref(inode);
cached_file->f_oflags = O_RDONLY;
cached_file->f_inode = inode;
ret = inode->u.i_mops->dup(filep, cached_file);
if (ret < 0)
{
goto errout_with_file;
}
ret = lru_cache_insert(&g_pathcache, abspath, cached_file);
if (ret < 0)
{
goto errout_with_cached_file;
}
return OK;
errout_with_cached_file:
inode->u.i_mops->close(cached_file);
errout_with_file:
file_deallocate(cached_file);
return ret;
}
* Name: pathcache_is_match
*
* Description:
* Match callback to check if a cache entry belongs to the unmounting
* mountpoint.
*
* Input Parameters:
* cache - Pointer to the cache structure (unused)
* key - The cache key
* value - Pointer to the cached file structure
* arg - Pointer to the unmounting mountpoint inode
*
* Returned Value:
* 1 if entry should be removed; 0 otherwise
*
****************************************************************************/
static int
pathcache_is_match(FAR struct lru_cache_s *cache,
FAR const char *key, FAR void *value,
FAR void *arg)
{
return ((FAR struct file *)value)->f_inode == arg;
}
* Public Functions
****************************************************************************/
* Name: pathcache_open
*
* Description:
* Open a file with caching support. First open stores a snapshot,
* subsequent opens duplicate from the cached snapshot.
*
* Input Parameters:
* filep - File structure to populate
* relpath - Relative path within the mount point
* oflags - Open flags
* mode - File mode
* path - Path passed to open (used as cache key)
*
* Returned Value:
* OK on success; a negated errno value on failure
*
****************************************************************************/
int pathcache_open(FAR struct file *filep, FAR const char *relpath,
int oflags, mode_t mode, FAR const char *path)
{
FAR struct inode *inode = filep->f_inode;
FAR struct file *cached_file = NULL;
FAR char *pathbuffer;
FAR char *abspath;
int ret = OK;
pathbuffer = lib_get_tempbuffer(PATH_MAX);
if (pathbuffer == NULL)
{
return -ENOMEM;
}
abspath = lib_realpath(path, pathbuffer, true);
if (abspath == NULL)
{
goto errout_with_pathbuffer;
}
cached_file = lru_cache_lookup(&g_pathcache, abspath);
if (cached_file == NULL)
{
ret = inode->u.i_mops->open(filep, relpath, oflags, mode);
if (ret < 0)
{
goto errout_with_pathbuffer;
}
* Only cache files opened in read-only mode here.
* If caching fails for any reason, we still return success because
* the file is already opened. Caching is just an optimization.
*/
if (!(oflags & O_WRONLY))
{
pathcache_create(filep, abspath);
}
goto errout_with_pathbuffer;
}
else
{
* Duplicating the cached file into the provided file pointer.
*/
inode->u.i_mops->dup(cached_file, filep);
pathcache_put(cached_file);
if (oflags & O_WRONLY)
{
lru_cache_remove(&g_pathcache, abspath);
}
if (oflags & O_TRUNC)
{
ret = inode->u.i_mops->truncate(filep, 0);
if (ret < 0)
{
goto errout_with_filep;
}
}
if (oflags & O_APPEND)
{
ret = inode->u.i_mops->seek(filep, 0, SEEK_END);
if (ret < 0)
{
goto errout_with_filep;
}
}
goto errout_with_pathbuffer;
}
errout_with_filep:
inode->u.i_mops->close(filep);
errout_with_pathbuffer:
lib_put_tempbuffer(pathbuffer);
return ret;
}
* Name: pathcache_remove
*
* Description:
* Remove a cache entry by path.
*
* Input Parameters:
* path - Path to remove
*
* Returned Value:
* OK on success; a negated errno value on failure
*
****************************************************************************/
int pathcache_remove(FAR const char *path)
{
FAR char *pathbuffer;
FAR char *abspath;
int ret = OK;
DEBUGASSERT(path != NULL);
pathbuffer = lib_get_tempbuffer(PATH_MAX);
if (pathbuffer == NULL)
{
return -ENOMEM;
}
abspath = lib_realpath(path, pathbuffer, true);
if (abspath == NULL)
{
ret = -EINVAL;
goto errout_with_pathbuffer;
}
lru_cache_remove(&g_pathcache, abspath);
errout_with_pathbuffer:
lib_put_tempbuffer(pathbuffer);
return ret;
}
* Name: pathcache_close
*
* Description:
* Close a file with caching support. If the file was opened with WRONLY
* or TRUNC, update the cache entry.
*
* Input Parameters:
* filep - File structure being closed
* path - Absolute path (used as cache key)
*
* Returned Value:
* OK on success; a negated errno value on failure
*
****************************************************************************/
int pathcache_close(FAR struct file *filep, FAR const char *path)
{
FAR char *pathbuffer;
FAR char *abspath;
int ret;
DEBUGASSERT(filep != NULL && path != NULL);
* If not, no need to update the cache.
*/
if (!(filep->f_oflags & O_WRONLY))
{
return OK;
}
pathbuffer = lib_get_tempbuffer(PATH_MAX);
if (pathbuffer == NULL)
{
return -ENOMEM;
}
abspath = lib_realpath(path, pathbuffer, true);
if (abspath == NULL)
{
ret = -EINVAL;
goto errout_with_pathbuffer;
}
lru_cache_remove(&g_pathcache, abspath);
pathcache_create(filep, abspath);
errout_with_pathbuffer:
lib_put_tempbuffer(pathbuffer);
return ret;
}
* Name: pathcache_umount
*
* Description:
* Invalidate all cache entries belonging to a specific mountpoint
*
* Input Parameters:
* mountpt - Mountpoint inode being unmounted
*
****************************************************************************/
void pathcache_umount(FAR struct inode *mountpt)
{
lru_cache_remove_if(&g_pathcache, pathcache_is_match, mountpt);
}
* Name: pathcache_initialize
*
* Description:
* Initialize the pathcache
*
* Input Parameters:
* None
*
****************************************************************************/
void pathcache_initialize(void)
{
lru_cache_init(&g_pathcache, CONFIG_FS_PATHCACHE_MAX_ENTRIES,
CONFIG_FS_PATHCACHE_HASH_SIZE, &g_pathcache_ops);
}