1c002e1049
Block and MTD drivers may be opened and managed as though they were character drivers. But this is really sleight of hand; there is a hidden character driver proxy that mediates the interface to the block and MTD drivers in this case. fstat(), however, did not account for this. It would report the characteristics of the proxy character driver, not of the underlying block or MTD driver. This change corrects that. fstat now checks if the character driver is such a proxy and, if so, reports the characteristics of the underlying block or MTD driver, not the proxy character driver.
438 lines
12 KiB
C
438 lines
12 KiB
C
/****************************************************************************
|
|
* fs/vfs/fs_stat.c
|
|
*
|
|
* 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 <sys/stat.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <sched.h>
|
|
#include <errno.h>
|
|
|
|
#include "inode/inode.h"
|
|
#include <nuttx/mtd/mtd.h>
|
|
#include <nuttx/fs/ioctl.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_PSEUDOFS_SOFTLINKS
|
|
/* Reset, preserving the number of symbolic links encountered so far */
|
|
|
|
# define RESET_BUF(b) \
|
|
{ \
|
|
uint16_t save = (b)->st_count; \
|
|
memset((b), 0, sizeof(struct stat)); \
|
|
(b)->st_count = save; \
|
|
}
|
|
#else
|
|
/* Reset everything */
|
|
|
|
# define RESET_BUF(b) memset((b), 0, sizeof(struct stat));
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static inline int statroot(FAR struct stat *buf);
|
|
int stat_recursive(FAR const char *path, FAR struct stat *buf);
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: statroot
|
|
****************************************************************************/
|
|
|
|
static inline int statroot(FAR struct stat *buf)
|
|
{
|
|
/* There is no inode associated with the fake root directory */
|
|
|
|
RESET_BUF(buf);
|
|
buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stat_recursive
|
|
*
|
|
* Returned Value:
|
|
* Zero on success; < 0 on failure:
|
|
*
|
|
* EACCES Search permission is denied for one of the directories in the
|
|
* path prefix of path.
|
|
* EFAULT Bad address.
|
|
* ENOENT A component of the path path does not exist, or the path is an
|
|
* empty string.
|
|
* ENOMEM Out of memory
|
|
* ENOTDIR A component of the path is not a directory.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int stat_recursive(FAR const char *path, FAR struct stat *buf)
|
|
{
|
|
struct inode_search_s desc;
|
|
FAR struct inode *inode;
|
|
int ret;
|
|
|
|
/* Get an inode for this path */
|
|
|
|
SETUP_SEARCH(&desc, path, true);
|
|
|
|
ret = inode_find(&desc);
|
|
if (ret < 0)
|
|
{
|
|
/* This name does not refer to an inode in the pseudo file system and
|
|
* there is no mountpoint that includes in this path.
|
|
*/
|
|
|
|
goto errout_with_search;
|
|
}
|
|
|
|
/* Get the search results */
|
|
|
|
inode = desc.node;
|
|
DEBUGASSERT(inode != NULL);
|
|
|
|
/* The way we handle the stat depends on the type of inode that we
|
|
* are dealing with.
|
|
*/
|
|
|
|
#ifndef CONFIG_DISABLE_MOUNTPOINT
|
|
if (INODE_IS_MOUNTPT(inode))
|
|
{
|
|
/* The node is a file system mointpoint. Verify that the mountpoint
|
|
* supports the stat() method
|
|
*/
|
|
|
|
if (inode->u.i_mops && inode->u.i_mops->stat)
|
|
{
|
|
/* Perform the stat() operation */
|
|
|
|
ret = inode->u.i_mops->stat(inode, desc.relpath, buf);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* The node is part of the root pseudo file system. This path may
|
|
* recurse if soft links are supported in the pseudo file system.
|
|
*/
|
|
|
|
ret = inode_stat(inode, buf);
|
|
}
|
|
|
|
inode_release(inode);
|
|
errout_with_search:
|
|
RELEASE_SEARCH(&desc);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: nx_stat
|
|
*
|
|
* Description:
|
|
* nx_stat() is similar to the standard 'stat' interface except that is
|
|
* not a cancellation point and it does not modify the errno variable.
|
|
*
|
|
* nx_stat() is an internal NuttX interface and should not be called from
|
|
* applications.
|
|
*
|
|
* Returned Value:
|
|
* Zero is returned on success; a negated value is returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int nx_stat(FAR const char *path, FAR struct stat *buf)
|
|
{
|
|
/* Sanity checks */
|
|
|
|
if (path == NULL || buf == NULL)
|
|
{
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (*path == '\0')
|
|
{
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Check for the fake root directory (which has no inode) */
|
|
|
|
if (strcmp(path, "/") == 0)
|
|
{
|
|
return statroot(buf);
|
|
}
|
|
|
|
/* The perform the stat() operation on the path. This is potentially
|
|
* recursive if soft link support is enabled.
|
|
*/
|
|
|
|
#ifdef CONFIG_PSEUDOFS_SOFTLINKS
|
|
buf->st_count = 0;
|
|
#endif
|
|
return stat_recursive(path, buf);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: stat
|
|
*
|
|
* Returned Value:
|
|
* Zero on success; -1 on failure with errno set:
|
|
*
|
|
* EACCES Search permission is denied for one of the directories in the
|
|
* path prefix of path.
|
|
* EFAULT Bad address.
|
|
* ENOENT A component of the path path does not exist, or the path is an
|
|
* empty string.
|
|
* ENOMEM Out of memory
|
|
* ENOTDIR A component of the path is not a directory.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int stat(FAR const char *path, FAR struct stat *buf)
|
|
{
|
|
int ret;
|
|
|
|
ret = nx_stat(path, buf);
|
|
if (ret < 0)
|
|
{
|
|
set_errno(-ret);
|
|
ret = ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inode_stat
|
|
*
|
|
* Description:
|
|
* The inode_stat() function will obtain information about an 'inode' in
|
|
* the pseudo file system and write it to the area pointed to by 'buf'.
|
|
*
|
|
* The 'buf' argument is a pointer to a stat structure, as defined in
|
|
* <sys/stat.h>, into which information is placed concerning the file.
|
|
*
|
|
* Input Parameters:
|
|
* inode - The indoe of interest
|
|
* buf - The caller provide location in which to return information about
|
|
* the inode.
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) returned on success. Otherwise, a negated errno value is
|
|
* returned to indicate the nature of the failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int inode_stat(FAR struct inode *inode, FAR struct stat *buf)
|
|
{
|
|
DEBUGASSERT(inode != NULL && buf != NULL);
|
|
|
|
/* Most of the stat entries just do not apply */
|
|
|
|
RESET_BUF(buf);
|
|
|
|
/* Handle "special" nodes */
|
|
|
|
if (INODE_IS_SPECIAL(inode))
|
|
{
|
|
#if defined(CONFIG_FS_NAMED_SEMAPHORES)
|
|
/* Check for a named semaphore */
|
|
|
|
if (INODE_IS_NAMEDSEM(inode))
|
|
{
|
|
buf->st_mode = S_IFSEM;
|
|
}
|
|
else
|
|
#endif
|
|
#if !defined(CONFIG_DISABLE_MQUEUE)
|
|
/* Check for a message queue */
|
|
|
|
if (INODE_IS_MQUEUE(inode))
|
|
{
|
|
buf->st_mode = S_IFMQ;
|
|
}
|
|
else
|
|
#endif
|
|
#if defined(CONFIG_FS_SHM)
|
|
/* Check for shared memory */
|
|
|
|
if (INODE_IS_SHM(inode))
|
|
{
|
|
buf->st_mode = S_IFSHM;
|
|
}
|
|
else
|
|
#endif
|
|
#if defined(CONFIG_MTD)
|
|
/* Check for an MTD driver */
|
|
|
|
if (INODE_IS_MTD(inode))
|
|
{
|
|
struct mtd_geometry_s mtdgeo;
|
|
|
|
buf->st_mode = S_IFMTD;
|
|
buf->st_mode |= S_IROTH | S_IRGRP | S_IRUSR;
|
|
buf->st_mode |= S_IWOTH | S_IWGRP | S_IWUSR;
|
|
|
|
if (inode->u.i_mtd != NULL &&
|
|
MTD_IOCTL(inode->u.i_mtd, MTDIOC_GEOMETRY,
|
|
(unsigned long)((uintptr_t)&mtdgeo)) >= 0)
|
|
{
|
|
buf->st_size = mtdgeo.neraseblocks * mtdgeo.erasesize;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef CONFIG_PSEUDOFS_SOFTLINKS
|
|
/* Handle softlinks differently. Just call stat() recursively on the
|
|
* target of the softlink.
|
|
*
|
|
* REVISIT: This has the possibility of an infinite loop!
|
|
*/
|
|
|
|
if (INODE_IS_SOFTLINK(inode))
|
|
{
|
|
int ret;
|
|
|
|
/* Increment the link counter. This is necessary to avoid
|
|
* infinite recursion if loops are encountered in the traversal.
|
|
* If we encounter more SYMLOOP_MAX symbolic links at any time
|
|
* during the traversal, error out.
|
|
*
|
|
* NOTE: That inode_search() will automatically skip over
|
|
* consecutive, intermediate symbolic links. Those numbers will
|
|
* not be included in the total.
|
|
*/
|
|
|
|
if (++buf->st_count > SYMLOOP_MAX)
|
|
{
|
|
return -ELOOP;
|
|
}
|
|
|
|
DEBUGASSERT(buf->st_count > 0); /* Check for unsigned integer overflow */
|
|
|
|
/* stat() the target of the soft link. */
|
|
|
|
ret = stat_recursive((FAR const char *)inode->u.i_link, buf);
|
|
|
|
/* If stat() fails, then there is a problem with the target of the
|
|
* symbolic link, but not with the symbolic link itself. We should
|
|
* still report success, just with less information.
|
|
*/
|
|
|
|
if (ret < 0)
|
|
{
|
|
RESET_BUF(buf);
|
|
}
|
|
|
|
/* Make sure the caller knows that this really a symbolic link. */
|
|
|
|
buf->st_mode |= S_IFLNK;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
}
|
|
}
|
|
|
|
/* Handle "normal inodes */
|
|
|
|
else if (inode->u.i_ops != NULL)
|
|
{
|
|
/* Determine read/write privileges based on the existence of read
|
|
* and write methods.
|
|
*/
|
|
|
|
if (inode->u.i_ops->read)
|
|
{
|
|
buf->st_mode = S_IROTH | S_IRGRP | S_IRUSR;
|
|
}
|
|
|
|
if (inode->u.i_ops->write)
|
|
{
|
|
buf->st_mode |= S_IWOTH | S_IWGRP | S_IWUSR;
|
|
}
|
|
|
|
/* Determine the type of the inode */
|
|
|
|
/* Check for a mountpoint */
|
|
|
|
if (INODE_IS_MOUNTPT(inode))
|
|
{
|
|
buf->st_mode |= S_IFDIR;
|
|
}
|
|
|
|
/* Check for a block driver */
|
|
|
|
else if (INODE_IS_BLOCK(inode))
|
|
{
|
|
/* What is if also has child inodes? */
|
|
|
|
buf->st_mode |= S_IFBLK;
|
|
|
|
#ifndef CONFIG_DISABLE_MOUNTPOINT
|
|
if ((inode->u.i_bops != NULL) &&
|
|
(inode->u.i_bops->geometry != NULL))
|
|
{
|
|
struct geometry geo;
|
|
if (inode->u.i_bops->geometry(inode, &geo) >= 0 &&
|
|
geo.geo_available)
|
|
{
|
|
buf->st_size = geo.geo_nsectors * geo.geo_sectorsize;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Otherwise, the node must refer to a character driver */
|
|
|
|
else /* if (INODE_IS_DRIVER(inode)) */
|
|
{
|
|
/* What is it if it also has child inodes? */
|
|
|
|
buf->st_mode |= S_IFCHR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* If it has no operations, then it must just be a intermediate
|
|
* node in the inode tree. It is something like a directory.
|
|
* We'll say that all pseudo-directories are read-able but not
|
|
* write-able.
|
|
*/
|
|
|
|
buf->st_mode |= S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR;
|
|
}
|
|
|
|
return OK;
|
|
}
|