/**************************************************************************** * fs/procfs/fs_procfs.c * * Copyright (C) 2013-2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mount/mount.h" #if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS) /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define PROCFS_NATTRS 2 /**************************************************************************** * External Definitons ****************************************************************************/ extern const struct procfs_operations proc_operations; extern const struct procfs_operations irq_operations; extern const struct procfs_operations cpuload_operations; extern const struct procfs_operations meminfo_operations; extern const struct procfs_operations module_operations; extern const struct procfs_operations uptime_operations; /* This is not good. These are implemented in other sub-systems. Having to * deal with them here is not a good coupling. What is really needed is a * run-time procfs registration system vs. a build time, fixed procfs * configuration. */ extern const struct procfs_operations net_procfsoperations; extern const struct procfs_operations net_procfs_routeoperations; extern const struct procfs_operations mtd_procfsoperations; extern const struct procfs_operations part_procfsoperations; extern const struct procfs_operations mount_procfsoperations; extern const struct procfs_operations smartfs_procfsoperations; /* And even worse, this one is specific to the STM32. The solution to * this nasty couple would be to replace this hard-coded, ROM-able * operations table with a RAM-base registration table. */ #if defined(CONFIG_STM32_CCM_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_CCM) extern const struct procfs_operations ccm_procfsoperations; #endif /**************************************************************************** * Private Types ****************************************************************************/ /* Table of all known / pre-registered procfs handlers / participants. */ #ifdef CONFIG_FS_PROCFS_REGISTER static const struct procfs_entry_s g_base_entries[] = #else static const struct procfs_entry_s g_procfs_entries[] = #endif { #ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS { "[0-9]*/**", &proc_operations, PROCFS_UNKOWN_TYPE }, { "[0-9]*", &proc_operations, PROCFS_DIR_TYPE }, #endif #if defined(CONFIG_SCHED_CPULOAD) && !defined(CONFIG_FS_PROCFS_EXCLUDE_CPULOAD) { "cpuload", &cpuload_operations, PROCFS_FILE_TYPE }, #endif #ifdef CONFIG_SCHED_IRQMONITOR { "irqs", &irq_operations, PROCFS_FILE_TYPE }, #endif #ifndef CONFIG_FS_PROCFS_EXCLUDE_MEMINFO { "meminfo", &meminfo_operations, PROCFS_FILE_TYPE }, #endif #if defined(CONFIG_MODULE) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MODULE) { "modules", &module_operations, PROCFS_FILE_TYPE }, #endif #ifndef CONFIG_FS_PROCFS_EXCLUDE_BLOCKS { "fs/blocks", &mount_procfsoperations, PROCFS_FILE_TYPE }, #endif #ifndef CONFIG_FS_PROCFS_EXCLUDE_MOUNT { "fs/mount", &mount_procfsoperations, PROCFS_FILE_TYPE }, #endif #ifndef CONFIG_FS_PROCFS_EXCLUDE_USAGE { "fs/usage", &mount_procfsoperations, PROCFS_FILE_TYPE }, #endif #if defined(CONFIG_FS_SMARTFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS) { "fs/smartfs**", &smartfs_procfsoperations, PROCFS_UNKOWN_TYPE }, #endif #if defined(CONFIG_MTD) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MTD) { "mtd", &mtd_procfsoperations, PROCFS_FILE_TYPE }, #endif #if defined(CONFIG_NET) && !defined(CONFIG_FS_PROCFS_EXCLUDE_NET) { "net", &net_procfsoperations, PROCFS_DIR_TYPE }, #if defined(CONFIG_NET_ROUTE) && !defined(CONFIG_FS_PROCFS_EXCLUDE_ROUTE) { "net/route", &net_procfs_routeoperations, PROCFS_DIR_TYPE }, { "net/route/**", &net_procfs_routeoperations, PROCFS_UNKOWN_TYPE }, #endif { "net/**", &net_procfsoperations, PROCFS_UNKOWN_TYPE }, #endif #if defined(CONFIG_MTD_PARTITION) && !defined(CONFIG_FS_PROCFS_EXCLUDE_PARTITIONS) { "partitions", &part_procfsoperations, PROCFS_FILE_TYPE }, #endif #if !defined(CONFIG_FS_PROCFS_EXCLUDE_UPTIME) { "uptime", &uptime_operations, PROCFS_FILE_TYPE }, #endif }; #ifdef CONFIG_FS_PROCFS_REGISTER static const uint8_t g_base_entrycount = sizeof(g_base_entries) / sizeof(struct procfs_entry_s); static FAR struct procfs_entry_s *g_procfs_entries; static uint8_t g_procfs_entrycount; #else static const uint8_t g_procfs_entrycount = sizeof(g_procfs_entries) / sizeof(struct procfs_entry_s); #endif /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* Helpers */ static void procfs_enum(FAR struct tcb_s *tcb, FAR void *arg); /* File system methods */ static int procfs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode); static int procfs_close(FAR struct file *filep); static ssize_t procfs_read(FAR struct file *filep, FAR char *buffer, size_t buflen); static ssize_t procfs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); static int procfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg); static int procfs_dup(FAR const struct file *oldp, FAR struct file *newp); static int procfs_fstat(FAR const struct file *filep, FAR struct stat *buf); static int procfs_opendir(FAR struct inode *mountpt, const char *relpath, FAR struct fs_dirent_s *dir); static int procfs_closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int procfs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int procfs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); static int procfs_bind(FAR struct inode *blkdriver, FAR const void *data, FAR void **handle); static int procfs_unbind(FAR void *handle, FAR struct inode **blkdriver, unsigned int flags); static int procfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf); static int procfs_stat(FAR struct inode *mountpt, FAR const char *relpath, FAR struct stat *buf); /* Initialization */ #ifdef CONFIG_FS_PROCFS_REGISTER int procfs_initialize(void); #else # define procfs_initialize() #endif /**************************************************************************** * Private Data ****************************************************************************/ /**************************************************************************** * 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 mountpt_operations procfs_operations = { procfs_open, /* open */ procfs_close, /* close */ procfs_read, /* read */ procfs_write, /* write */ NULL, /* seek */ procfs_ioctl, /* ioctl */ NULL, /* sync */ procfs_dup, /* dup */ procfs_fstat, /* fstat */ NULL, /* truncate */ procfs_opendir, /* opendir */ procfs_closedir, /* closedir */ procfs_readdir, /* readdir */ procfs_rewinddir, /* rewinddir */ procfs_bind, /* bind */ procfs_unbind, /* unbind */ procfs_statfs, /* statfs */ NULL, /* unlink */ NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* rename */ procfs_stat /* stat */ }; /* Level 0 contains the directory of active tasks in addition to other * statically registered entries with custom handlers. This strcture * contains a snapshot of the active tasks when the directory is first * opened. */ struct procfs_level0_s { struct procfs_dir_priv_s base; /* Base struct for ProcFS dir */ /* Our private data */ uint8_t lastlen; /* length of last reported static dir */ pid_t pid[CONFIG_MAX_TASKS]; /* Snapshot of all active task IDs */ FAR const char *lastread; /* Pointer to last static dir read */ }; /* Level 1 is an internal virtual directory (such as /proc/fs) which * will contain one or more additional static entries based on the * configuration. */ struct procfs_level1_s { struct procfs_dir_priv_s base; /* Base struct for ProcFS dir */ /* Our private data */ uint8_t lastlen; /* length of last reported static dir */ uint8_t subdirlen; /* Length of the subdir search */ uint16_t firstindex; /* Index of 1st entry matching this subdir */ FAR const char *lastread; /* Pointer to last static dir read */ }; /**************************************************************************** * Private Functions ****************************************************************************/ #ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS /**************************************************************************** * Name: procfs_enum ****************************************************************************/ static void procfs_enum(FAR struct tcb_s *tcb, FAR void *arg) { FAR struct procfs_level0_s *dir = (FAR struct procfs_level0_s *)arg; int index; DEBUGASSERT(dir); /* Add the PID to the list */ index = dir->base.nentries; DEBUGASSERT(index < CONFIG_MAX_TASKS); dir->pid[index] = tcb->pid; dir->base.nentries = index + 1; } #endif /**************************************************************************** * Name: procfs_open ****************************************************************************/ static int procfs_open(FAR struct file *filep, FAR const char *relpath, int oflags, mode_t mode) { int x, ret = -ENOENT; finfo("Open '%s'\n", relpath); /* Perform the stat based on the procfs_entry operations */ for (x = 0; x < g_procfs_entrycount; x++) { /* Test if the path matches this entry's specification */ if (match(g_procfs_entries[x].pathpattern, relpath)) { /* Match found! Stat using this procfs entry */ DEBUGASSERT(g_procfs_entries[x].ops && g_procfs_entries[x].ops->open); ret = g_procfs_entries[x].ops->open(filep, relpath, oflags, mode); if (ret == OK) { DEBUGASSERT(filep->f_priv); ((struct procfs_file_s *) filep->f_priv)->procfsentry = &g_procfs_entries[x]; break; } } } return ret; } /**************************************************************************** * Name: procfs_close ****************************************************************************/ static int procfs_close(FAR struct file *filep) { FAR struct procfs_file_s *attr; /* Recover our private data from the struct file instance */ attr = (FAR struct procfs_file_s *)filep->f_priv; DEBUGASSERT(attr); /* Release the file attributes structure */ kmm_free(attr); filep->f_priv = NULL; return OK; } /**************************************************************************** * Name: procfs_read ****************************************************************************/ static ssize_t procfs_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { FAR struct procfs_file_s *handler; ssize_t ret = 0; finfo("buffer=%p buflen=%d\n", buffer, (int)buflen); /* Recover our private data from the struct file instance */ handler = (FAR struct procfs_file_s *)filep->f_priv; DEBUGASSERT(handler); /* Call the handler's read routine */ ret = handler->procfsentry->ops->read(filep, buffer, buflen); return ret; } /**************************************************************************** * Name: procfs_write ****************************************************************************/ static ssize_t procfs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { FAR struct procfs_file_s *handler; ssize_t ret = 0; finfo("buffer=%p buflen=%d\n", buffer, (int)buflen); /* Recover our private data from the struct file instance */ handler = (FAR struct procfs_file_s *)filep->f_priv; DEBUGASSERT(handler); /* Call the handler's read routine */ if (handler->procfsentry->ops->write) { ret = handler->procfsentry->ops->write(filep, buffer, buflen); } return ret; } /**************************************************************************** * Name: procfs_ioctl ****************************************************************************/ static int procfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { finfo("cmd: %d arg: %08lx\n", cmd, arg); /* No IOCTL commands supported */ return -ENOTTY; } /**************************************************************************** * Name: procfs_dup * * Description: * Duplicate open file data in the new file structure. * ****************************************************************************/ static int procfs_dup(FAR const struct file *oldp, FAR struct file *newp) { FAR struct procfs_file_s *oldattr; finfo("Dup %p->%p\n", oldp, newp); /* Recover our private data from the old struct file instance */ oldattr = (FAR struct procfs_file_s *)oldp->f_priv; DEBUGASSERT(oldattr); /* Allow lower-level handler do the dup to get it's extra data */ return oldattr->procfsentry->ops->dup(oldp, newp); } /**************************************************************************** * Name: procfs_fstat * * Description: * Obtain information about an open file associated with the file * descriptor 'fd', and will write it to the area pointed to by 'buf'. * ****************************************************************************/ static int procfs_fstat(FAR const struct file *filep, FAR struct stat *buf) { FAR struct procfs_file_s *handler; finfo("buf=%p\n", buf); /* Recover our private data from the struct file instance */ handler = (FAR struct procfs_file_s *)filep->f_priv; DEBUGASSERT(handler); /* The procfs file system contains only directory and data file entries. * Since the file has been opened, we know that this is a data file and, * at a minimum, readable. */ memset(buf, 0, sizeof(struct stat)); buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR; /* If the write method is provided, then let's also claim that the file is * writable. */ if (handler->procfsentry->ops->write != NULL) { buf->st_mode |= S_IWOTH | S_IWGRP | S_IWUSR; } return OK; } /**************************************************************************** * Name: procfs_opendir * * Description: * Open a directory for read access * ****************************************************************************/ static int procfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, FAR struct fs_dirent_s *dir) { FAR struct procfs_level0_s *level0; FAR struct procfs_dir_priv_s *dirpriv; FAR void *priv = NULL; irqstate_t flags; finfo("relpath: \"%s\"\n", relpath ? relpath : "NULL"); DEBUGASSERT(mountpt && relpath && dir && !dir->u.procfs); /* The relative must be either: * * "" - The top level directory of task/thread IDs * "" - The sub-directory of task/thread attributes */ if (!relpath || relpath[0] == '\0') { /* The path refers to the top level directory. Allocate the level0 * dirent structure. */ level0 = (FAR struct procfs_level0_s *) kmm_zalloc(sizeof(struct procfs_level0_s)); if (!level0) { ferr("ERROR: Failed to allocate the level0 directory structure\n"); return -ENOMEM; } /* Take a snapshot of all currently active tasks. Any new tasks * added between the opendir() and closedir() call will not be * visible. * * NOTE that interrupts must be disabled throughout the traversal. */ #ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS flags = enter_critical_section(); sched_foreach(procfs_enum, level0); leave_critical_section(flags); #else level0->base.index = 0; level0->base.nentries = 0; #endif /* Initialize lastread entries */ level0->lastread = ""; level0->lastlen = 0; level0->base.procfsentry = NULL; priv = (FAR void *)level0; } else { int x, ret; int len = strlen(relpath); /* Search the static array of procfs_entries */ for (x = 0; x < g_procfs_entrycount; x++) { /* Test if the path matches this entry's specification */ if (match(g_procfs_entries[x].pathpattern, relpath)) { /* Match found! Call the handler's opendir routine. If successful, * this opendir routine will create an entry derived from struct * procfs_dir_priv_s as dir->u.procfs. */ DEBUGASSERT(g_procfs_entries[x].ops && g_procfs_entries[x].ops->opendir); ret = g_procfs_entries[x].ops->opendir(relpath, dir); if (ret == OK) { DEBUGASSERT(dir->u.procfs); /* Set the procfs_entry handler */ dirpriv = (FAR struct procfs_dir_priv_s *)dir->u.procfs; dirpriv->procfsentry = &g_procfs_entries[x]; } return ret; } /* Test for a sub-string match (e.g. "ls /proc/fs") */ else if (strncmp(g_procfs_entries[x].pathpattern, relpath, len) == 0) { FAR struct procfs_level1_s *level1; /* Doing an intermediate directory search */ /* The path refers to the top level directory. Allocate the level1 * dirent structure. */ level1 = (FAR struct procfs_level1_s *) kmm_zalloc(sizeof(struct procfs_level1_s)); if (!level1) { ferr("ERROR: Failed to allocate the level0 directory structure\n"); return -ENOMEM; } level1->base.level = 1; level1->base.index = x; level1->firstindex = x; level1->subdirlen = len; level1->lastread = ""; level1->lastlen = 0; level1->base.procfsentry = NULL; priv = (FAR void *)level1; break; } } } dir->u.procfs = priv; return OK; } /**************************************************************************** * Name: procfs_closedir * * Description: Close the directory listing * ****************************************************************************/ static int procfs_closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir) { FAR struct procfs_dir_priv_s *priv; DEBUGASSERT(mountpt && dir && dir->u.procfs); priv = dir->u.procfs; if (priv) { kmm_free(priv); } dir->u.procfs = NULL; return OK; } /**************************************************************************** * Name: procfs_readdir * * Description: Read the next directory entry * ****************************************************************************/ static int procfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir) { FAR const struct procfs_entry_s *entry = NULL; FAR struct procfs_dir_priv_s *priv; FAR struct procfs_level0_s *level0; FAR struct tcb_s *tcb; FAR const char *name = NULL; unsigned int index; irqstate_t flags; pid_t pid; int ret = -ENOENT; DEBUGASSERT(mountpt && dir && dir->u.procfs); priv = dir->u.procfs; /* Are we reading the 1st directory level with dynamic PID and static * entries? */ if (priv->level == 0) { level0 = (FAR struct procfs_level0_s *)priv; /* Have we reached the end of the PID information */ index = priv->index; if (index >= priv->nentries) { /* We must report the next static entry ... no more PID entries. * skip any entries with wildcards in the first segment of the * directory name. */ while (index < priv->nentries + g_procfs_entrycount) { entry = &g_procfs_entries[index - priv->nentries]; name = entry->pathpattern; while (*name != '/' && *name != '\0') { if (*name == '*' || *name == '[' || *name == '?') { /* Wildcard found. Skip this entry */ index++; name = NULL; break; } name++; } /* Test if we skipped this entry */ if (name != NULL) { /* This entry is okay to report. Test if it has a duplicate * first level name as the one we just reported. This could * happen in the event of procfs_entry_s such as: * * fs/smartfs * fs/nfs * fs/nxffs */ name = g_procfs_entries[index - priv->nentries].pathpattern; if (!level0->lastlen || (strncmp(name, level0->lastread, level0->lastlen) != 0)) { /* Not a duplicate, return the first segment of this * entry */ break; } else { /* Skip this entry ... duplicate 1st level name found */ index++; } } } /* Test if we are at the end of the directory */ if (index >= priv->nentries + g_procfs_entrycount) { /* We signal the end of the directory by returning the special * error -ENOENT */ finfo("Entry %d: End of directory\n", index); ret = -ENOENT; } else { /* Report the next static entry */ level0->lastlen = strcspn(name, "/"); level0->lastread = name; strncpy(dir->fd_dir.d_name, name, level0->lastlen); dir->fd_dir.d_name[level0->lastlen] = '\0'; /* If the entry is a directory type OR if the reported name is * only a sub-string of the entry (meaning that it contains * '/'), then report this entry as a directory. */ if (entry->type == PROCFS_DIR_TYPE || level0->lastlen != strlen(name)) { dir->fd_dir.d_type = DTYPE_DIRECTORY; } else { dir->fd_dir.d_type = DTYPE_FILE; } /* Advance to next entry for the next read */ priv->index = index; ret = OK; } } #ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS else { /* Verify that the pid still refers to an active task/thread */ pid = level0->pid[index]; flags = enter_critical_section(); tcb = sched_gettcb(pid); leave_critical_section(flags); if (!tcb) { ferr("ERROR: PID %d is no longer valid\n", (int)pid); return -ENOENT; } /* Save the filename=pid and file type=directory */ dir->fd_dir.d_type = DTYPE_DIRECTORY; snprintf(dir->fd_dir.d_name, NAME_MAX+1, "%d", (int)pid); /* Set up the next directory entry offset. NOTE that we could use the * standard f_pos instead of our own private index. */ level0->base.index = index + 1; ret = OK; } #endif /* CONFIG_FS_PROCFS_EXCLUDE_PROCESS */ } /* Are we reading an intermediate subdirectory? */ else if (priv->level > 0 && priv->procfsentry == NULL) { FAR struct procfs_level1_s *level1; level1 = (FAR struct procfs_level1_s *) priv; /* Test if this entry matches. We assume all entries of the same * subdirectory are listed in order in the procfs_entry array. */ if (strncmp(g_procfs_entries[level1->base.index].pathpattern, g_procfs_entries[level1->firstindex].pathpattern, level1->subdirlen) == 0) { /* This entry matches. Report the subdir entry */ name = &g_procfs_entries[level1->base.index].pathpattern[ level1->subdirlen + 1]; level1->lastlen = strcspn(name, "/"); level1->lastread = name; strncpy(dir->fd_dir.d_name, name, level1->lastlen); /* Some of the search entries contain '**' wildcards. When we * report the entry name, we must remove this wildcard search * specifier. */ while (dir->fd_dir.d_name[level1->lastlen - 1] == '*') { level1->lastlen--; } dir->fd_dir.d_name[level1->lastlen] = '\0'; if (name[level1->lastlen] == '/') { dir->fd_dir.d_type = DTYPE_DIRECTORY; } else { dir->fd_dir.d_type = DTYPE_FILE; } level1->base.index++; ret = OK; } else { /* No more entries in the subdirectory */ ret = -ENOENT; } } else { /* We are performing a directory search of one of the subdirectories * and we must let the handler perform the read. */ DEBUGASSERT(priv->procfsentry && priv->procfsentry->ops->readdir); ret = priv->procfsentry->ops->readdir(dir); } return ret; } /**************************************************************************** * Name: procfs_rewindir * * Description: Reset directory read to the first entry * ****************************************************************************/ static int procfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir) { FAR struct procfs_dir_priv_s *priv; DEBUGASSERT(mountpt && dir && dir->u.procfs); priv = dir->u.procfs; if (priv->level > 0 && priv->procfsentry == NULL) { priv->index = ((struct procfs_level1_s *) priv)->firstindex; } else { priv->index = 0; } return OK; } /**************************************************************************** * Name: procfs_bind * * Description: This implements a portion of the mount operation. This * function allocates and initializes the mountpoint private data and * binds the blockdriver inode to the filesystem private data. The final * binding of the private data (containing the blockdriver) to the * mountpoint is performed by mount(). * ****************************************************************************/ static int procfs_bind(FAR struct inode *blkdriver, const void *data, void **handle) { /* Make sure that we are properly initialized */ procfs_initialize(); return OK; } /**************************************************************************** * Name: procfs_unbind * * Description: This implements the filesystem portion of the umount * operation. * ****************************************************************************/ static int procfs_unbind(void *handle, FAR struct inode **blkdriver, unsigned int flags) { return OK; } /**************************************************************************** * Name: procfs_statfs * * Description: Return filesystem statistics * ****************************************************************************/ static int procfs_statfs(struct inode *mountpt, struct statfs *buf) { /* Fill in the statfs info */ memset(buf, 0, sizeof(struct statfs)); buf->f_type = PROCFS_MAGIC; buf->f_bsize = 0; buf->f_blocks = 0; buf->f_bfree = 0; buf->f_bavail = 0; buf->f_namelen = NAME_MAX; return OK; } /**************************************************************************** * Name: procfs_stat * * Description: Return information about a file or directory * ****************************************************************************/ static int procfs_stat(struct inode *mountpt, const char *relpath, struct stat *buf) { int ret = -ENOSYS; /* Three path forms are accepted: * * "" - The relative path refers to the top level directory * "" - If refers to a currently active task/thread, then it * is a directory * "/" - If is a recognized attribute then, then it * is a file. */ memset(buf, 0, sizeof(struct stat)); if (!relpath || relpath[0] == '\0') { /* The path refers to the top level directory */ /* It's a read-only directory */ buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR; ret = OK; } else { int x; int len = strlen(relpath); /* Perform the stat based on the procfs_entry operations */ for (x = 0; x < g_procfs_entrycount; x++) { /* Test if the path matches this entry's specification */ if (match(g_procfs_entries[x].pathpattern, relpath)) { /* Match found! Stat using this procfs entry */ DEBUGASSERT(g_procfs_entries[x].ops && g_procfs_entries[x].ops->stat); return g_procfs_entries[x].ops->stat(relpath, buf); } /* Test for an internal subdirectory stat */ else if (strncmp(g_procfs_entries[x].pathpattern, relpath, len) == 0) { /* It's an internal subdirectory */ buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR; ret = OK; break; } } } return ret; } /**************************************************************************** * Name: procfs_initialize * * Description: * Configure the initial set of entries in the procfs file system. * * Input Parameters: * None * * Returned Value: * Zero (OK) on success; a negated errno value on failure * ****************************************************************************/ #ifdef CONFIG_FS_PROCFS_REGISTER int procfs_initialize(void) { /* Are we already initialized? */ if (g_procfs_entries == NULL) { /* No.. allocate a modifyable list of entries */ g_procfs_entries = (FAR struct procfs_entry_s *) kmm_malloc(sizeof(g_base_entries)); if (g_procfs_entries == NULL) { return -ENOMEM; } /* And copy the fixed entries into the allocated array */ memcpy(g_procfs_entries, g_base_entries, sizeof(g_base_entries)); g_procfs_entrycount = g_base_entrycount; } return OK; } #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: procfs_register * * Description: * Add a new entry to the procfs file system. * * NOTE: This function should be called *prior* to mounting the procfs * file system to prevent concurrency problems with the modification of * the procfs data set while it is in use. * * Input Parameters: * entry - Describes the entry to be registered. * * Returned Value: * Zero (OK) on success; a negated errno value on failure * ****************************************************************************/ #ifdef CONFIG_FS_PROCFS_REGISTER int procfs_register(FAR const struct procfs_entry_s *entry) { FAR struct procfs_entry_s *newtable; unsigned int newcount; size_t newsize; int ret; /* Make sure that we are properly initialized */ procfs_initialize(); /* realloc the table of procfs entries. * * REVISIT: This reallocation may free memory previously used for the * procfs entry table. If that table were actively in use, then that * could cause procfs logic to use a stale memory pointer! We avoid that * problem by requiring that the procfs file be unmounted when the new * entry is added. That requirment, however, is not enforced explicitly. * * Locking the scheduler as done below is insufficient. As would be just * marking the entries as volatile. */ newcount = g_procfs_entrycount + 1; newsize = newcount * sizeof(struct procfs_entry_s); sched_lock(); newtable = (FAR struct procfs_entry_s *)kmm_realloc(g_procfs_entries, newsize); if (newtable == NULL) { /* Reallocation failed! */ ret = -ENOMEM; } else { /* Copy the new entry at the end of the reallocated table */ memcpy(&newtable[g_procfs_entrycount], entry, sizeof(struct procfs_entry_s)); /* Instantiate the reallocated table */ g_procfs_entries = newtable; g_procfs_entrycount = newcount; ret = OK; } sched_unlock(); return ret; } #endif #endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS */