6a3c2aded6
* Simplify EINTR/ECANCEL error handling 1. Add semaphore uninterruptible wait function 2 .Replace semaphore wait loop with a single uninterruptible wait 3. Replace all sem_xxx to nxsem_xxx * Unify the void cast usage 1. Remove void cast for function because many place ignore the returned value witout cast 2. Replace void cast for variable with UNUSED macro
809 lines
22 KiB
C
809 lines
22 KiB
C
/****************************************************************************
|
|
* net/procfs/fs_procfs_route.c
|
|
*
|
|
* Copyright (C) 2017 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 <nuttx/config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/statfs.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/fs/procfs.h>
|
|
#include <nuttx/fs/dirent.h>
|
|
|
|
#include "route/route.h"
|
|
|
|
#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS)
|
|
#ifndef CONFIG_FS_PROCFS_EXCLUDE_ROUTE
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/* Determines the size of an intermediate buffer that must be large enough
|
|
* to handle the longest line generated by this logic.
|
|
*/
|
|
|
|
#define STATUS_LINELEN 58
|
|
|
|
/* Directory entry indices */
|
|
|
|
#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6)
|
|
# define IPv4_INDEX 0
|
|
# define IPv6_INDEX 1
|
|
#elif defined(CONFIG_NET_IPv4)
|
|
# define IPv4_INDEX 0
|
|
#elif defined(CONFIG_NET_IPv6)
|
|
# define IPv6_INDEX 0
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/* This enumeration identifies all of the nodes that can be accessed via the
|
|
* procfs file system.
|
|
*/
|
|
|
|
enum route_node_e
|
|
{
|
|
PROC_ROUTE = 0 /* The top-level directory */
|
|
#ifdef CONFIG_NET_IPv4
|
|
, PROC_ROUTE_IPv4 /* IPv4 routing table */
|
|
#endif
|
|
#ifdef CONFIG_NET_IPv6
|
|
, PROC_ROUTE_IPv6 /* IPv6 routing table */
|
|
#endif
|
|
};
|
|
|
|
/* This structure describes one open "file" */
|
|
|
|
struct route_file_s
|
|
{
|
|
struct procfs_file_s base; /* Base open file structure */
|
|
FAR const char *name; /* Terminal node segment name */
|
|
uint8_t node; /* Type of node (see enum route_node_e) */
|
|
char line[STATUS_LINELEN]; /* Pre-allocated buffer for formatted lines */
|
|
};
|
|
|
|
/* This structure describes one open "directory" */
|
|
|
|
struct route_dir_s
|
|
{
|
|
struct procfs_dir_priv_s base; /* Base directory private data */
|
|
FAR const char *name; /* Terminal node segment name */
|
|
uint8_t node; /* Type of node (see enum route_node_e) */
|
|
};
|
|
|
|
/* The structure is used when traversing routing tables */
|
|
|
|
struct route_info_s
|
|
{
|
|
FAR char *line; /* Intermediate line buffer pointer */
|
|
FAR char *buffer; /* User buffer */
|
|
size_t linelen; /* Size of the intermediate buffer */
|
|
size_t buflen; /* Size of the user buffer */
|
|
size_t remaining; /* Bytes remaining in user buffer */
|
|
size_t totalsize; /* Accumulated size of the copy */
|
|
off_t offset; /* Skip offset */
|
|
bool header; /* True: header has been generated */
|
|
int16_t index; /* Routing table index */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* Helpers */
|
|
|
|
static void route_sprintf(FAR struct route_info_s *info,
|
|
FAR const char *fmt, ...);
|
|
#ifdef CONFIG_NET_IPv4
|
|
static int route_ipv4_entry(FAR struct net_route_ipv4_s *route,
|
|
FAR void *arg);
|
|
#endif
|
|
#ifdef CONFIG_NET_IPv6
|
|
static int route_ipv6_entry(FAR struct net_route_ipv6_s *route,
|
|
FAR void *arg);
|
|
#endif
|
|
#ifdef CONFIG_NET_IPv4
|
|
static ssize_t route_ipv4_table(FAR struct route_file_s *procfile,
|
|
FAR char *buffer, size_t buflen, off_t offset);
|
|
#endif
|
|
#ifdef CONFIG_NET_IPv6
|
|
static ssize_t route_ipv6_table(FAR struct route_file_s *procfile,
|
|
FAR char *buffer, size_t buflen, off_t offset);
|
|
#endif
|
|
|
|
/* File system methods */
|
|
|
|
static int route_open(FAR struct file *filep, FAR const char *relpath,
|
|
int oflags, mode_t mode);
|
|
static int route_close(FAR struct file *filep);
|
|
static ssize_t route_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen);
|
|
|
|
static int route_dup(FAR const struct file *oldp,
|
|
FAR struct file *newp);
|
|
|
|
static int route_opendir(const char *relpath,
|
|
FAR struct fs_dirent_s *dir);
|
|
static int route_closedir(FAR struct fs_dirent_s *dir);
|
|
static int route_readdir(FAR struct fs_dirent_s *dir);
|
|
static int route_rewinddir(FAR struct fs_dirent_s *dir);
|
|
|
|
static int route_stat(FAR const char *relpath, FAR struct stat *buf);
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
/* See fs_mount.c -- this structure is explicitly externed there.
|
|
* We use the old-fashioned kind of initializers so that this will compile
|
|
* with any compiler.
|
|
*/
|
|
|
|
const struct procfs_operations net_procfs_routeoperations =
|
|
{
|
|
route_open, /* open */
|
|
route_close, /* close */
|
|
route_read, /* read */
|
|
NULL, /* write */
|
|
|
|
route_dup, /* dup */
|
|
|
|
route_opendir, /* opendir */
|
|
route_closedir, /* closedir */
|
|
route_readdir, /* readdir */
|
|
route_rewinddir, /* rewinddir */
|
|
|
|
route_stat /* stat */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
/* Well-known paths */
|
|
|
|
static const char g_route_path[] = "net/route";
|
|
#ifdef CONFIG_NET_IPv4
|
|
static const char g_route_ipv4_path[] = "net/route/ipv4";
|
|
#endif
|
|
#ifdef CONFIG_NET_IPv6
|
|
static const char g_route_ipv6_path[] = "net/route/ipv6";
|
|
#endif
|
|
|
|
/* Subdirectory names */
|
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
static const char g_route_ipv4_subdir[] = "ipv4";
|
|
#endif
|
|
#ifdef CONFIG_NET_IPv6
|
|
static const char g_route_ipv6_subdir[] = "ipv6";
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: route_sprintf
|
|
*
|
|
* Description:
|
|
* Format:
|
|
*
|
|
* 11111111112222222222333
|
|
* 12345678901234567890123456789012
|
|
* SEQ TARGET NETMASK ROUTER
|
|
* nnnn. xxxxxxxx xxxxxxxx xxxxxxxx
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void route_sprintf(FAR struct route_info_s *info,
|
|
FAR const char *fmt, ...)
|
|
{
|
|
size_t linesize;
|
|
size_t copysize;
|
|
va_list ap;
|
|
|
|
/* Print the format and data to a line buffer */
|
|
|
|
va_start(ap, fmt);
|
|
linesize = vsnprintf(info->line, info->linelen, fmt, ap);
|
|
va_end(ap);
|
|
|
|
/* Copy the line buffer to the user buffer */
|
|
|
|
copysize = procfs_memcpy(info->line, linesize,
|
|
info->buffer, info->remaining,
|
|
&info->offset);
|
|
|
|
/* Update counts and pointers */
|
|
|
|
info->totalsize += copysize;
|
|
info->buffer += copysize;
|
|
info->remaining -= copysize;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: route_ipv4_entry
|
|
*
|
|
* Description:
|
|
* Format:
|
|
*
|
|
* 11111111112222222222333333333344444444444555
|
|
* 12345678901234567890123456789012345678901234567890123
|
|
* SEQ TARGET NETMASK ROUTER
|
|
* nnnn. xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
static int route_ipv4_entry(FAR struct net_route_ipv4_s *route, FAR void *arg)
|
|
{
|
|
FAR struct route_info_s *info = (FAR struct route_info_s *)arg;
|
|
char target[INET_ADDRSTRLEN];
|
|
char netmask[INET_ADDRSTRLEN];
|
|
char router[INET_ADDRSTRLEN];
|
|
|
|
DEBUGASSERT(info != NULL);
|
|
|
|
/* Generate the header before the first entry */
|
|
|
|
if (info->index == 0 && !info->header)
|
|
{
|
|
route_sprintf(info, "%-4s %-16s%-16s%-16s\n",
|
|
"SEQ", "TARGET", "NETMASK", "ROUTER");
|
|
|
|
if (info->totalsize >= info->buflen)
|
|
{
|
|
/* Only part of the header was printed. */
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* The whole header was printed. */
|
|
|
|
info->header = true;
|
|
}
|
|
|
|
/* Generate routing table entry on one line */
|
|
|
|
inet_ntop(AF_INET, &route->target, target, INET_ADDRSTRLEN);
|
|
inet_ntop(AF_INET, &route->netmask, netmask, INET_ADDRSTRLEN);
|
|
inet_ntop(AF_INET, &route->router, router, INET_ADDRSTRLEN);
|
|
|
|
info->index++;
|
|
route_sprintf(info, "%4u. %-16s%-16s%-16s\n",
|
|
info->index, target, netmask, router);
|
|
|
|
return (info->totalsize >= info->buflen) ? 1 : 0;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: route_ipv6_entry
|
|
*
|
|
* Description:
|
|
* Format:
|
|
*
|
|
* 11111111112222222222333333333344444444445555
|
|
* 12345678901234567890123456789012345678901234567890123
|
|
* nnnn. target: xxxx:xxxx:xxxx:xxxxxxxx:xxxx:xxxx:xxxx
|
|
* netmask: xxxx:xxxx:xxxx:xxxxxxxx:xxxx:xxxx:xxxx
|
|
* router: xxxx:xxxx:xxxx:xxxxxxxx:xxxx:xxxx:xxxx
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
static int route_ipv6_entry(FAR struct net_route_ipv6_s *route, FAR void *arg)
|
|
{
|
|
FAR struct route_info_s *info = (FAR struct route_info_s *)arg;
|
|
char addr[INET6_ADDRSTRLEN];
|
|
|
|
DEBUGASSERT(info != NULL);
|
|
|
|
/* Generate routing table entry on three lines */
|
|
|
|
info->index++;
|
|
inet_ntop(AF_INET6, route->target, addr, INET6_ADDRSTRLEN);
|
|
|
|
route_sprintf(info, "%4u. TARGET %s\n", info->index, addr);
|
|
if (info->totalsize >= info->buflen)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
inet_ntop(AF_INET6, route->netmask, addr, INET6_ADDRSTRLEN);
|
|
route_sprintf(info, " NETMASK %s\n", addr);
|
|
if (info->totalsize >= info->buflen)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
inet_ntop(AF_INET6, route->router, addr, INET6_ADDRSTRLEN);
|
|
route_sprintf(info, " ROUTER %s\n", addr);
|
|
return (info->totalsize >= info->buflen) ? 1 : 0;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: route_ipv4_table
|
|
*
|
|
* Description:
|
|
* Format:
|
|
*
|
|
* 11111111112222222222333333333344444444444555
|
|
* 12345678901234567890123456789012345678901234567890123
|
|
* SEQ TARGET NETMASK ROUTER
|
|
* nnnn. xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
static ssize_t route_ipv4_table(FAR struct route_file_s *procfile,
|
|
FAR char *buffer, size_t buflen, off_t offset)
|
|
{
|
|
struct route_info_s info;
|
|
|
|
memset(&info, 0, sizeof(struct route_info_s));
|
|
info.line = procfile->line;
|
|
info.buffer = buffer;
|
|
info.linelen = STATUS_LINELEN;
|
|
info.buflen = buflen;
|
|
info.remaining = buflen;
|
|
info.offset = offset;
|
|
|
|
/* Generate each entry in the routing table */
|
|
|
|
net_foreachroute_ipv4(route_ipv4_entry, &info);
|
|
return info.totalsize;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: route_ipv6_table
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
static ssize_t route_ipv6_table(FAR struct route_file_s *procfile,
|
|
FAR char *buffer, size_t buflen, off_t offset)
|
|
{
|
|
struct route_info_s info;
|
|
|
|
memset(&info, 0, sizeof(struct route_info_s));
|
|
info.line = procfile->line;
|
|
info.buffer = buffer;
|
|
info.linelen = STATUS_LINELEN;
|
|
info.buflen = buflen;
|
|
info.remaining = buflen;
|
|
info.offset = offset;
|
|
|
|
/* Generate each entry in the routing table */
|
|
|
|
net_foreachroute_ipv6(route_ipv6_entry, &info);
|
|
return info.totalsize;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: route_open
|
|
****************************************************************************/
|
|
|
|
static int route_open(FAR struct file *filep, FAR const char *relpath,
|
|
int oflags, mode_t mode)
|
|
{
|
|
FAR struct route_file_s *procfile;
|
|
FAR const char *name;
|
|
uint8_t node;
|
|
|
|
finfo("Open '%s'\n", relpath);
|
|
|
|
/* PROCFS is read-only. Any attempt to open with any kind of write
|
|
* access is not permitted.
|
|
*
|
|
* REVISIT: Write-able proc files could be quite useful.
|
|
*/
|
|
|
|
if ((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0)
|
|
{
|
|
ferr("ERROR: Only O_RDONLY supported\n");
|
|
return -EACCES;
|
|
}
|
|
|
|
/* There are only two possibilities */
|
|
|
|
#ifdef CONFIG_NET_IPv4
|
|
if (strcmp(relpath, g_route_ipv4_path) == 0)
|
|
{
|
|
name = g_route_ipv4_subdir;
|
|
node = PROC_ROUTE_IPv4;
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef CONFIG_NET_IPv6
|
|
if (strcmp(relpath, g_route_ipv6_path) == 0)
|
|
{
|
|
name = g_route_ipv6_subdir;
|
|
node = PROC_ROUTE_IPv6;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
ferr("ERROR: Invalid path \"%s\"\n", relpath);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Allocate a container to hold the task and node selection */
|
|
|
|
procfile = (FAR struct route_file_s *)
|
|
kmm_zalloc(sizeof(struct route_file_s));
|
|
if (!procfile)
|
|
{
|
|
ferr("ERROR: Failed to allocate file container\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Initialize the file container */
|
|
|
|
procfile->name = name; /* Terminal node segment name */
|
|
procfile->node = node; /* Type of node (see enum route_node_e) */
|
|
|
|
/* Save the index as the open-specific state in filep->f_priv */
|
|
|
|
filep->f_priv = (FAR void *)procfile;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: route_close
|
|
****************************************************************************/
|
|
|
|
static int route_close(FAR struct file *filep)
|
|
{
|
|
FAR struct route_file_s *procfile;
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
procfile = (FAR struct route_file_s *)filep->f_priv;
|
|
DEBUGASSERT(procfile);
|
|
|
|
/* Release the file container structure */
|
|
|
|
kmm_free(procfile);
|
|
filep->f_priv = NULL;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: route_read
|
|
****************************************************************************/
|
|
|
|
static ssize_t route_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen)
|
|
{
|
|
FAR struct route_file_s *procfile;
|
|
ssize_t ret;
|
|
|
|
finfo("buffer=%p buflen=%d\n", buffer, (int)buflen);
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
procfile = (FAR struct route_file_s *)filep->f_priv;
|
|
DEBUGASSERT(procfile);
|
|
|
|
/* Provide the requested data */
|
|
|
|
switch (procfile->node)
|
|
{
|
|
#ifdef CONFIG_NET_IPv4
|
|
case PROC_ROUTE_IPv4: /* IPv4 routing table */
|
|
ret = route_ipv4_table(procfile, buffer, buflen, filep->f_pos);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
case PROC_ROUTE_IPv6: /* IPv6 routing table */
|
|
ret = route_ipv6_table(procfile, buffer, buflen, filep->f_pos);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
/* Update the file offset */
|
|
|
|
if (ret > 0)
|
|
{
|
|
filep->f_pos += ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: route_dup
|
|
*
|
|
* Description:
|
|
* Duplicate open file data in the new file structure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int route_dup(FAR const struct file *oldp, FAR struct file *newp)
|
|
{
|
|
FAR struct route_file_s *oldfile;
|
|
FAR struct route_file_s *newfile;
|
|
|
|
finfo("Dup %p->%p\n", oldp, newp);
|
|
|
|
/* Recover our private data from the old struct file instance */
|
|
|
|
oldfile = (FAR struct route_file_s *)oldp->f_priv;
|
|
DEBUGASSERT(oldfile);
|
|
|
|
/* Allocate a new container to hold the task and node selection */
|
|
|
|
newfile = (FAR struct route_file_s *)
|
|
kmm_malloc(sizeof(struct route_file_s));
|
|
if (!newfile)
|
|
{
|
|
ferr("ERROR: Failed to allocate file container\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* The copy the file information from the old container to the new */
|
|
|
|
memcpy(newfile, oldfile, sizeof(struct route_file_s));
|
|
|
|
/* Save the new container in the new file structure */
|
|
|
|
newp->f_priv = (FAR void *)newfile;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: route_opendir
|
|
*
|
|
* Description:
|
|
* Open a directory for read access
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int route_opendir(FAR const char *relpath,
|
|
FAR struct fs_dirent_s *dir)
|
|
{
|
|
FAR struct route_dir_s *level2;
|
|
|
|
finfo("relpath: \"%s\"\n", relpath ? relpath : "NULL");
|
|
DEBUGASSERT(relpath && dir && !dir->u.procfs);
|
|
|
|
/* Check the relative path */
|
|
|
|
if (strcmp(relpath, g_route_path) != 0)
|
|
{
|
|
#ifdef CONFIG_NET_IPv4
|
|
if (strcmp(relpath, g_route_ipv4_path) == 0)
|
|
{
|
|
return -ENOTDIR;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
if (strcmp(relpath, g_route_ipv6_path) == 0)
|
|
{
|
|
return -ENOTDIR;
|
|
}
|
|
#endif
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
level2 = (FAR struct route_dir_s *)
|
|
kmm_zalloc(sizeof(struct route_dir_s));
|
|
if (!level2)
|
|
{
|
|
ferr("ERROR: Failed to allocate the directory structure\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* This is a second level directory */
|
|
|
|
level2->base.level = 2;
|
|
level2->base.nentries = 2;
|
|
level2->name = "";
|
|
level2->node = PROC_ROUTE;
|
|
dir->u.procfs = (FAR void *)level2;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: route_closedir
|
|
*
|
|
* Description: Close the directory listing
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int route_closedir(FAR struct fs_dirent_s *dir)
|
|
{
|
|
FAR struct route_dir_s *priv;
|
|
|
|
DEBUGASSERT(dir && dir->u.procfs);
|
|
priv = dir->u.procfs;
|
|
|
|
if (priv != NULL)
|
|
{
|
|
kmm_free(priv);
|
|
}
|
|
|
|
dir->u.procfs = NULL;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: route_readdir
|
|
*
|
|
* Description: Read the next directory entry
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int route_readdir(struct fs_dirent_s *dir)
|
|
{
|
|
FAR struct route_dir_s *level2;
|
|
FAR const char *dname;
|
|
unsigned int index;
|
|
|
|
DEBUGASSERT(dir != NULL && dir->u.procfs != NULL);
|
|
level2 = dir->u.procfs;
|
|
|
|
/* The index determines which entry to return */
|
|
|
|
index = level2->base.index;
|
|
#ifdef CONFIG_NET_IPv4
|
|
if (index == IPv4_INDEX)
|
|
{
|
|
dname = g_route_ipv4_subdir;
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef CONFIG_NET_IPv6
|
|
if (index == IPv6_INDEX)
|
|
{
|
|
dname = g_route_ipv6_subdir;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* We signal the end of the directory by returning the special
|
|
* error -ENOENT
|
|
*/
|
|
|
|
finfo("Entry %d: End of directory\n", index);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Save the filename and file type */
|
|
|
|
dir->fd_dir.d_type = DTYPE_FILE;
|
|
strncpy(dir->fd_dir.d_name, dname, NAME_MAX + 1);
|
|
|
|
/* Set up the next directory entry offset. NOTE that we could use the
|
|
* standard f_pos instead of our own private index.
|
|
*/
|
|
|
|
level2->base.index = index + 1;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: proc_rewindir
|
|
*
|
|
* Description: Reset directory read to the first entry
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int route_rewinddir(struct fs_dirent_s *dir)
|
|
{
|
|
FAR struct route_dir_s *priv;
|
|
|
|
DEBUGASSERT(dir && dir->u.procfs);
|
|
priv = dir->u.procfs;
|
|
|
|
priv->base.index = 0;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: route_stat
|
|
*
|
|
* Description: Return information about a file or directory
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int route_stat(const char *relpath, struct stat *buf)
|
|
{
|
|
memset(buf, 0, sizeof(struct stat));
|
|
|
|
if (strcmp(relpath, g_route_path) == 0)
|
|
{
|
|
buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR;
|
|
}
|
|
else
|
|
#ifdef CONFIG_NET_IPv4
|
|
if (strcmp(relpath, g_route_ipv4_path) == 0)
|
|
{
|
|
buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR;
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef CONFIG_NET_IPv6
|
|
if (strcmp(relpath, g_route_ipv6_path) == 0)
|
|
{
|
|
buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
return -ENOENT;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
#endif /* CONFIG_FS_PROCFS_EXCLUDE_ROUTE */
|
|
#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS */
|