summaryrefslogtreecommitdiff
path: root/nuttx/fs
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-12-12 09:21:55 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-12-12 09:21:55 -0600
commit147415a647658c5d472338d745c5b68942eb0b2c (patch)
tree9a94ea4d00f35742480ecac85db9ef399bf3be0c /nuttx/fs
parent2977ad09c854e056b26f4ae1d86e751a7acead60 (diff)
downloadnuttx-147415a647658c5d472338d745c5b68942eb0b2c.tar.gz
nuttx-147415a647658c5d472338d745c5b68942eb0b2c.tar.bz2
nuttx-147415a647658c5d472338d745c5b68942eb0b2c.zip
procfs/: Extenstive architectural changes and enhancements by Ken Pettit
Diffstat (limited to 'nuttx/fs')
-rw-r--r--nuttx/fs/procfs/Kconfig46
-rw-r--r--nuttx/fs/procfs/Make.defs2
-rw-r--r--nuttx/fs/procfs/fs_procfs.c991
-rw-r--r--nuttx/fs/procfs/fs_procfsproc.c1003
-rw-r--r--nuttx/fs/procfs/fs_skeleton.c460
-rw-r--r--nuttx/fs/smartfs/Make.defs2
-rw-r--r--nuttx/fs/smartfs/smartfs_procfs.c460
7 files changed, 2308 insertions, 656 deletions
diff --git a/nuttx/fs/procfs/Kconfig b/nuttx/fs/procfs/Kconfig
index 9f20aa672..6c1c34bf0 100644
--- a/nuttx/fs/procfs/Kconfig
+++ b/nuttx/fs/procfs/Kconfig
@@ -6,9 +6,45 @@
config FS_PROCFS
bool "PROCFS File System"
default n
- select FS_WRITABLE
+ select FS_READABLE
---help---
- The PROCFS file system is provides access to task status through the
- NuttX file system. The PROCFS may, for example, be mount at /proc.
- Then information about all of the currently active tasks and threads
- will be available in proc/.
+ The PROCFS file system provides access to task status and other driver
+ status through the NuttX file system. The PROCFS may, for example, be
+ mount at /proc. Then information about all of the currently active
+ tasks and threads will be available in /proc.
+
+if FS_PROCFS
+
+menu "Exclude individual procfs entries"
+
+config FS_PROCFS_EXCLUDE_MOUNTS
+ bool "Exclude mounts"
+ depends on !DISABLE_MOUNTPOINT
+ default n
+
+config FS_PROCFS_EXCLUDE_MTD
+ bool "Exclude mtd"
+ depends on MTD
+ default n
+
+config FS_PROCFS_EXCLUDE_PROCESS
+ bool "Exclude process information"
+ default n
+ ---help---
+ Causes the process information to be excluded from the procfs system.
+ This will reduce code space, but then giving access to process info
+ was kinda the whole point of procfs, but hey, whatever.
+
+config FS_PROCFS_EXCLUDE_PARTITIONS
+ bool "Exclude partitions"
+ depends on MTD_PARTITION
+ default n
+
+config FS_PROCFS_EXCLUDE_SMARTFS
+ bool "Exclude fs/smartfs"
+ depends on FS_SMARTFS
+ default n
+
+endmenu
+
+endif
diff --git a/nuttx/fs/procfs/Make.defs b/nuttx/fs/procfs/Make.defs
index 8eae9027c..b39fa782d 100644
--- a/nuttx/fs/procfs/Make.defs
+++ b/nuttx/fs/procfs/Make.defs
@@ -37,7 +37,7 @@ ifeq ($(CONFIG_FS_PROCFS),y)
# Files required for procfs file system support
ASRCS +=
-CSRCS += fs_procfs.c
+CSRCS += fs_procfs.c fs_procfsproc.c
# Include procfs build support
diff --git a/nuttx/fs/procfs/fs_procfs.c b/nuttx/fs/procfs/fs_procfs.c
index 599052603..2bd091bb6 100644
--- a/nuttx/fs/procfs/fs_procfs.c
+++ b/nuttx/fs/procfs/fs_procfs.c
@@ -57,7 +57,9 @@
#include <nuttx/sched.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/fs.h>
+#include <nuttx/fs/procfs.h>
#include <nuttx/fs/dirent.h>
+#include <nuttx/regex.h>
#include <arch/irq.h>
@@ -67,65 +69,42 @@
* Pre-processor Definitions
****************************************************************************/
-#define STATUS_LINELEN 32
-
-#ifndef MIN
-# define MIN(a,b) ((a < b) ? a : b)
-#endif
+#define PROCFS_NATTRS 2
/****************************************************************************
- * Private Types
+ * External Definitons
****************************************************************************/
-/* This enumeration identifies all of the thread attributes that can be
- * accessed via the procfs file system.
- */
-
-enum procfs_attr_e
-{
- PROCFS_STATUS = 0, /* Task/thread status */
- PROCFS_CMDLINE, /* Command line */
-};
-#define PROCFS_NATTRS 2
-
-/* This structure describes one open "file" */
-
-struct procfs_file_s
-{
- pid_t pid; /* Task/thread ID */
- uint8_t attr; /* See enum procfs_attr_e */
- char line[STATUS_LINELEN]; /* Pre-allocated buffer for formatted lines */
-};
-/* The generic proc/ pseudo directory structure */
+extern const struct procfs_operations process_operations;
+extern const struct procfs_operations mtd_procfsoperations;
+extern const struct procfs_operations part_procfsoperations;
+extern const struct procfs_operations smartfs_procfsoperations;
-struct procfs_level_s
-{
- uint8_t level; /* Directory level. Currently 0 or 1 */
- uint16_t index; /* Index to the next directory entry */
- uint16_t nentries; /* Number of directory entries */
-};
-
-/* Level 0 is the directory of active tasks */
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+/* Table of all known / pre-registered procfs handlers / participants. */
-struct procfs_level0_s
+static const struct procfs_entry_s g_procfsentries[] =
{
- uint8_t level; /* Directory level. Currently 0 or 1 */
- uint16_t index; /* Index to the next directory entry */
- uint16_t nentries; /* Number of directory entries */
-
- pid_t pid[CONFIG_MAX_TASKS]; /* Snapshot of all active task IDs */
+#ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS
+ { "[0-9]*/*", &process_operations },
+ { "[0-9]*", &process_operations },
+#endif
+#if defined(CONFIG_FS_SMARTFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
+//{ "fs/smartfs", &smartfs_procfsoperations },
+ { "fs/smartfs**", &smartfs_procfsoperations },
+#endif
+#if defined(CONFIG_MTD) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MTD)
+ { "mtd", &mtd_procfsoperations },
+#endif
+#if defined(CONFIG_MTD_PARTITION) && !defined(CONFIG_FS_PROCFS_EXCLUDE_PARTITON)
+ { "partitions", &part_procfsoperations },
+#endif
};
-/* Level 1 is the directory of task attributes */
-
-struct procfs_level1_s
-{
- uint8_t level; /* Directory level. Currently 0 or 1 */
- uint16_t index; /* Index to the next directory entry */
- uint16_t nentries; /* Number of directory entries */
-
- pid_t pid; /* ID of task for attributes */
-};
+static const uint8_t g_procfsentrycount = sizeof(g_procfsentries) /
+ sizeof(struct procfs_entry_s);
/****************************************************************************
* Private Function Prototypes
@@ -133,16 +112,6 @@ struct procfs_level1_s
/* Helpers */
static void procfs_enum(FAR struct tcb_s *tcb, FAR void *arg);
-static int procfs_findattr(FAR const char *attr);
-static size_t procfs_addline(FAR struct procfs_file_s *attr,
- FAR char *buffer, size_t buflen, size_t linesize,
- off_t *offset);
-static ssize_t procfs_status(FAR struct procfs_file_s *attr,
- FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen,
- off_t offset);
-static ssize_t procfs_cmdline(FAR struct procfs_file_s *attr,
- FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen,
- off_t offset);
/* File system methods */
@@ -216,45 +185,52 @@ const struct mountpt_operations procfs_operations =
procfs_stat /* stat */
};
-/* This is the list of all attribute strings. Indexing is with the same
- * values as enum procfs_attr_e.
+/* 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.
*/
-static const char *g_attrstrings[PROCFS_NATTRS] =
+struct procfs_level0_s
{
- "status",
- "cmdline"
-};
+ uint8_t level; /* Directory level. Currently 0 or 1 */
+ uint8_t lastlen; /* length of last reported static dir */
+ uint16_t index; /* Index to the next directory entry */
+ uint16_t nentries; /* Number of directory entries */
+ pid_t pid[CONFIG_MAX_TASKS]; /* Snapshot of all active task IDs */
+ FAR const char *lastread; /* Pointer to last static dir read */
-static const char *g_statenames[] =
-{
- "Invalid",
- "Pending unlock",
- "Ready",
- "Running",
- "Inactive",
- "Semaphore wait",
-#ifndef CONFIG_DISABLE_MQUEUE
- "Signal wait",
-#endif
-#ifndef CONFIG_DISABLE_MQUEUE
- "MQ not empty wait",
- "MQ no full wait"
-#endif
+ /* Pointer to procfs handler entry */
+
+ FAR const struct procfs_entry_s *procfsentry;
};
-static const char *g_ttypenames[4] =
+/* 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
{
- "Task",
- "pthread",
- "Kernel thread",
- "--?--"
+ uint8_t level; /* Directory level. Currently 0 or 1 */
+ uint8_t lastlen; /* length of last reported static dir */
+ uint8_t subdirlen; /* Length of the subdir search */
+ uint16_t index; /* Index to the next directory entry */
+ uint16_t nentries; /* Number of directory entries */
+ uint16_t firstindex; /* Index of 1st entry matching this subdir */
+ FAR const char *lastread; /* Pointer to last static dir read */
+
+ /* Pointer to procfs handler entry */
+
+ FAR const struct procfs_entry_s *procfsentry;
};
/****************************************************************************
* Private Functions
****************************************************************************/
+#ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS
+
/****************************************************************************
* Name: procfs_enum
****************************************************************************/
@@ -274,264 +250,8 @@ static void procfs_enum(FAR struct tcb_s *tcb, FAR void *arg)
dir->pid[index] = tcb->pid;
dir->nentries = index + 1;
}
-
-/****************************************************************************
- * Name: procfs_findattr
- ****************************************************************************/
-
-static int procfs_findattr(FAR const char *attr)
-{
- int i;
-
- /* Search every string in g_attrstrings or until a match is found */
-
- for (i = 0; i < PROCFS_NATTRS; i++)
- {
- if (strcmp(g_attrstrings[i], attr) == 0)
- {
- return i;
- }
- }
-
- /* Not found */
-
- return -ENOENT;
-}
-
-/****************************************************************************
- * Name: procfs_addline
- ****************************************************************************/
-
-static size_t procfs_addline(FAR struct procfs_file_s *attr,
- FAR char *buffer, size_t buflen,
- size_t linesize, off_t *offset)
-{
- size_t copysize;
- size_t lnoffset;
-
- /* Will this line take us past the offset? */
-
- lnoffset = *offset;
- if (linesize < lnoffset)
- {
- /* No... decrement the offset and return without doing anything */
-
- *offset -= linesize;
- return 0;
- }
-
- /* Handle the remaining offset */
-
- linesize -= lnoffset;
- buffer += lnoffset;
- *offset = 0;
-
- /* Copy the line into the user buffer */
-
- copysize = MIN(linesize, buflen);
- memcpy(buffer, &attr->line[lnoffset], copysize);
- return copysize;
-}
-
-/****************************************************************************
- * Name: procfs_status
- ****************************************************************************/
-
-static ssize_t procfs_status(FAR struct procfs_file_s *attr,
- FAR struct tcb_s *tcb, FAR char *buffer,
- size_t buflen, off_t offset)
-{
- FAR const char *name;
- size_t remaining;
- size_t linesize;
- size_t copysize;
- size_t totalsize;
-
- remaining = buflen;
- totalsize = 0;
-
- /* Show the task name */
-
-#if CONFIG_TASK_NAME_SIZE > 0
- name = tcb->name;
-#else
- name = "<noname>";
-#endif
- linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n",
- "Name:", name);
- copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
-
- totalsize += copysize;
- buffer += copysize;
- remaining -= copysize;
-
- if (totalsize >= buflen)
- {
- return totalsize;
- }
-
- /* Show the thread type */
-
- linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", "Type:",
- g_ttypenames[(tcb->flags & TCB_FLAG_TTYPE_MASK) >>
- TCB_FLAG_TTYPE_SHIFT]);
- copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
-
- totalsize += copysize;
- buffer += copysize;
- remaining -= copysize;
-
- if (totalsize >= buflen)
- {
- return totalsize;
- }
-
- /* Show the thread state */
-
- linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", "State:",
- g_statenames[tcb->task_state]);
- copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
-
- totalsize += copysize;
- buffer += copysize;
- remaining -= copysize;
-
- if (totalsize >= buflen)
- {
- return totalsize;
- }
-
- /* Show the thread priority */
-
-#ifdef CONFIG_PRIORITY_INHERITANCE
- linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%d (%d)\n", "Priority:",
- tcb->sched_priority, tcb->base_priority);
-#else
- linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%d\n", "Priority:",
- tcb->sched_priority);
-#endif
- copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
-
- totalsize += copysize;
- buffer += copysize;
- remaining -= copysize;
-
- if (totalsize >= buflen)
- {
- return totalsize;
- }
-
- /* Show the scheduler */
-
- linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", "Scheduler:",
- tcb->flags & TCB_FLAG_ROUND_ROBIN ? "SCHED_RR" : "SCHED_FIFO");
- copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
-
- totalsize += copysize;
- buffer += copysize;
- remaining -= copysize;
-
- if (totalsize >= buflen)
- {
- return totalsize;
- }
-
- /* Show the signal mast */
-
-#ifndef CONFIG_DISABLE_SIGNALS
- linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%08x\n", "SigMask:",
- tcb->sigprocmask);
- copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
-
- totalsize += copysize;
-#endif
-
- return totalsize;
-}
-
-/****************************************************************************
- * Name: procfs_cmdline
- ****************************************************************************/
-
-static ssize_t procfs_cmdline(FAR struct procfs_file_s *attr,
- FAR struct tcb_s *tcb, FAR char *buffer,
- size_t buflen, off_t offset)
-{
- FAR struct task_tcb_s *ttcb;
- FAR const char *name;
- FAR char **argv;
- size_t remaining;
- size_t linesize;
- size_t copysize;
- size_t totalsize;
-
- remaining = buflen;
- totalsize = 0;
-
- /* Show the task name */
-
-#if CONFIG_TASK_NAME_SIZE > 0
- name = tcb->name;
-#else
- name = "<noname>";
-#endif
- linesize = strlen(name);
- memcpy(attr->line, name, linesize);
- copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
-
- totalsize += copysize;
- buffer += copysize;
- remaining -= copysize;
-
- if (totalsize >= buflen)
- {
- return totalsize;
- }
-
-#ifndef CONFIG_DISABLE_PTHREAD
- /* Show the pthread argument */
-
- if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD)
- {
- FAR struct pthread_tcb_s *ptcb = (FAR struct pthread_tcb_s *)tcb;
-
- linesize = snprintf(attr->line, STATUS_LINELEN, " 0x%p\n", ptcb->arg);
- copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
-
- totalsize += copysize;
- buffer += copysize;
- remaining -= copysize;
-
- return totalsize;
- }
#endif
- /* Show the task argument list (skipping over the name) */
-
- ttcb = (FAR struct task_tcb_s *)tcb;
-
- for (argv = ttcb->argv + 1; *argv; argv++)
- {
- linesize = snprintf(attr->line, STATUS_LINELEN, " %s", *argv);
- copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
-
- totalsize += copysize;
- buffer += copysize;
- remaining -= copysize;
-
- if (totalsize >= buflen)
- {
- return totalsize;
- }
- }
-
- linesize = snprintf(attr->line, STATUS_LINELEN, "\n");
- copysize = procfs_addline(attr, buffer, remaining, linesize, &offset);
-
- totalsize += copysize;
- return totalsize;
-}
-
/****************************************************************************
* Name: procfs_open
****************************************************************************/
@@ -539,96 +259,36 @@ static ssize_t procfs_cmdline(FAR struct procfs_file_s *attr,
static int procfs_open(FAR struct file *filep, FAR const char *relpath,
int oflags, mode_t mode)
{
- FAR struct procfs_file_s *attr;
- FAR struct tcb_s *tcb;
- FAR char *ptr;
- irqstate_t flags;
- unsigned long tmp;
- pid_t pid;
- int attrndx;
+ int x, ret = -ENOENT;
fvdbg("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.
- */
+ /* Perform the stat based on the procfs_entry operations */
- if ((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0)
+ for (x = 0; x < g_procfsentrycount; x++)
{
- fdbg("ERROR: Only O_RDONLY supported\n");
- return -EACCES;
- }
-
- /* The first segment of the relative path should be a task/thread ID */
+ /* Test if the path matches this entry's specification */
- ptr = NULL;
- tmp = strtoul(relpath, &ptr, 10);
-
- if (!ptr || *ptr != '/')
- {
- fdbg("ERROR: Invalid path \"%s\"\n", relpath);
- return -ENOENT;
- }
-
- /* Skip over the slash */
-
- ptr++;
-
- /* A valid PID would be in the range of 0-32767 (0 is reserved for the
- * IDLE thread).
- */
-
- if (tmp >= 32768)
- {
- fdbg("ERROR: Invalid PID %ld\n", tmp);
- return -ENOENT;
- }
-
- /* Now verify that a task with this task/thread ID exists */
-
- pid = (pid_t)tmp;
-
- flags = irqsave();
- tcb = sched_gettcb(pid);
- irqrestore(flags);
-
- if (!tcb)
- {
- fdbg("ERROR: PID %d is no longer valid\n", (int)pid);
- return -ENOENT;
- }
+ if (match(g_procfsentries[x].pathpattern, relpath))
+ {
+ /* Match found! Stat using this procfs entry */
- /* The second segment of the relpath should be a well known attribute of
- * the task/thread.
- */
+ DEBUGASSERT(g_procfsentries[x].ops &&
+ g_procfsentries[x].ops->open);
- attrndx = procfs_findattr(ptr);
- if (attrndx < 0)
- {
- fdbg("ERROR: Invalid attribute %s\n", ptr);
- return -ENOENT;
- }
+ ret = g_procfsentries[x].ops->open(filep, relpath, oflags, mode);
- /* Allocate a container to hold the task and attribute selection */
+ if (ret == OK)
+ {
+ DEBUGASSERT(filep->f_priv);
- attr = (FAR struct procfs_file_s *)kzalloc(sizeof(struct procfs_file_s));
- if (!attr)
- {
- fdbg("ERROR: Failed to allocate file attributes\n");
- return -ENOMEM;
+ ((struct procfs_file_s *) filep->f_priv)->procfsentry =
+ &g_procfsentries[x];
+ }
+ }
}
- /* Initialize the file attributes */
-
- attr->pid = pid;
- attr->attr = attrndx;
-
- /* Save the index as the open-specific state in filep->f_priv */
-
- filep->f_priv = (FAR void *)attr;
- return OK;
+ return ret;
}
/****************************************************************************
@@ -658,52 +318,19 @@ static int procfs_close(FAR struct file *filep)
static ssize_t procfs_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
- FAR struct procfs_file_s *attr;
- FAR struct tcb_s *tcb;
- irqstate_t flags;
- ssize_t ret;
+ FAR struct procfs_file_s *handler;
+ ssize_t ret = 0;
fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen);
/* Recover our private data from the struct file instance */
- attr = (FAR struct procfs_file_s *)filep->f_priv;
- DEBUGASSERT(attr);
-
- /* Verify that the thread is still valid */
-
- flags = irqsave();
- tcb = sched_gettcb(attr->pid);
-
- if (!tcb)
- {
- fdbg("ERROR: PID %d is not valid\n", (int)attr->pid);
- irqrestore(flags);
- return -ENODEV;
- }
-
- /* Provide the requested data */
-
- switch (attr->attr)
- {
- default:
- case PROCFS_STATUS: /* Task/thread status */
- ret = procfs_status(attr, tcb, buffer, buflen, filep->f_pos);
- break;
-
- case PROCFS_CMDLINE: /* Command line */
- ret = procfs_cmdline(attr, tcb, buffer, buflen, filep->f_pos);
- break;
- }
-
- irqrestore(flags);
+ handler = (FAR struct procfs_file_s *)filep->f_priv;
+ DEBUGASSERT(handler);
- /* Update the file offset */
+ /* Call the handler's read routine */
- if (ret > 0)
- {
- filep->f_pos += ret;
- }
+ ret = handler->procfsentry->ops->read(filep, buffer, buflen);
return ret;
}
@@ -732,7 +359,6 @@ 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)
{
FAR struct procfs_file_s *oldattr;
- FAR struct procfs_file_s *newattr;
fvdbg("Dup %p->%p\n", oldp, newp);
@@ -741,23 +367,9 @@ static int procfs_dup(FAR const struct file *oldp, FAR struct file *newp)
oldattr = (FAR struct procfs_file_s *)oldp->f_priv;
DEBUGASSERT(oldattr);
- /* Allocate a new container to hold the task and attribute selection */
-
- newattr = (FAR struct procfs_file_s *)kzalloc(sizeof(struct procfs_file_s));
- if (!newattr)
- {
- fdbg("ERROR: Failed to allocate file attributes\n");
- return -ENOMEM;
- }
+ /* Allow lower-level handler do the dup to get it's extra data */
- /* The copy the file attribtes from the old attributes to the new */
-
- memcpy(newattr, oldattr, sizeof(struct procfs_file_s));
-
- /* Save the new attributes in the new file structure */
-
- newp->f_priv = (FAR void *)newattr;
- return OK;
+ return oldattr->procfsentry->ops->dup(oldp, newp);
}
/****************************************************************************
@@ -771,7 +383,7 @@ static int procfs_dup(FAR const struct file *oldp, FAR struct file *newp)
static int procfs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct fs_dirent_s *dir)
{
- FAR struct tcb_s *tcb;
+ FAR struct procfs_level0_s *level0;
FAR void *priv = NULL;
irqstate_t flags;
@@ -786,8 +398,6 @@ static int procfs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
if (!relpath || relpath[0] == '\0')
{
- FAR struct procfs_level0_s *level0;
-
/* The path refers to the top level directory. Allocate the level0
* dirent structure.
*/
@@ -808,84 +418,87 @@ static int procfs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
* NOTE that interrupts must be disabled throughout the traversal.
*/
+#ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS
flags = irqsave();
sched_foreach(procfs_enum, level0);
irqrestore(flags);
+#else
+ level0->index = 0;
+ level0->nentries = 0;
+#endif
+
+ /* Initialze lastread entries */
+
+ level0->lastread = "";
+ level0->lastlen = 0;
+ level0->procfsentry = NULL;
priv = (FAR void *)level0;
}
else
{
- FAR struct procfs_level1_s *level1;
- unsigned long tmp;
- FAR char *ptr;
- pid_t pid;
-
- /* Otherwise, the relative path should be a valid task/thread ID */
+ int x, ret;
+ int len = strlen(relpath);
- ptr = NULL;
- tmp = strtoul(relpath, &ptr, 10);
+ /* Search the static array of procfs_entries */
- if (!ptr || (*ptr != '\0' && strcmp(ptr, "/") != 0))
+ for (x = 0; x < g_procfsentrycount; x++)
{
- /* strtoul failed or there is something in the path after the pid */
-
- fdbg("ERROR: Invalid path \"%s\"\n", relpath);
- return -ENOENT;
- }
+ /* Test if the path matches this entry's specification */
- /* A valid PID would be in the range of 0-32767 (0 is reserved for the
- * IDLE thread).
- */
+ if (match(g_procfsentries[x].pathpattern, relpath))
+ {
+ /* Match found! Call the handler's opendir routine */
- if (tmp >= 32768)
- {
- fdbg("ERROR: Invalid PID %ld\n", tmp);
- return -ENOENT;
- }
+ DEBUGASSERT(g_procfsentries[x].ops && g_procfsentries[x].ops->opendir);
+ ret = g_procfsentries[x].ops->opendir(relpath, dir);
- /* Now verify that a task with this task/thread ID exists */
+ if (ret == OK)
+ {
+ DEBUGASSERT(dir->u.procfs);
- pid = (pid_t)tmp;
+ /* Set the procfs_entry handler */
- flags = irqsave();
- tcb = sched_gettcb(pid);
- irqrestore(flags);
+ level0 = (FAR struct procfs_level0_s *) dir->u.procfs;
+ level0->procfsentry = &g_procfsentries[x];
+ }
- if (!tcb)
- {
- fdbg("ERROR: PID %d is not valid\n", (int)pid);
- return -ENOENT;
- }
+ return ret;
+ }
- /* Was the <pid> the final element of the path? */
+ /* Test for a sub-string match (e.g. "ls /proc/fs") */
- if (*ptr != '\0' && strcmp(ptr, "/") != 0)
- {
- /* There is something in the path after the pid */
+ else if (strncmp(g_procfsentries[x].pathpattern, relpath, len) == 0)
+ {
+ FAR struct procfs_level1_s *level1;
- fdbg("ERROR: Invalid path \"%s\"\n", relpath);
- return -ENOENT;
- }
+ /* Doing an intermediate directory search */
- /* The path refers to the 1st level sbdirectory. Allocate the level1
- * dirent structure.
- */
+ /* The path refers to the top level directory. Allocate the level0
+ * dirent structure.
+ */
- level1 = (FAR struct procfs_level1_s *)
- kzalloc(sizeof(struct procfs_level1_s));
+ level1 = (FAR struct procfs_level1_s *)
+ kzalloc(sizeof(struct procfs_level1_s));
- if (!level1)
- {
- fdbg("ERROR: Failed to allocate the level1 directory structure\n");
- return -ENOMEM;
- }
+ if (!level1)
+ {
+ fdbg("ERROR: Failed to allocate the level0 directory structure\n");
+ return -ENOMEM;
+ }
- level1->level = 1;
- level1->nentries = PROCFS_NATTRS;
- level1->pid = pid;
+ level1->level = 1;
+ level1->index = x;
+ level1->firstindex = x;
+ level1->subdirlen = len;
+ level1->lastread = "";
+ level1->lastlen = 0;
+ level1->procfsentry = NULL;
- priv = (FAR void *)level1;
+ priv = (FAR void *)level1;
+ break;
+ }
+ }
}
dir->u.procfs = priv;
@@ -902,7 +515,7 @@ static int procfs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
static int procfs_closedir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir)
{
- FAR struct procfs_level_s *priv;
+ FAR struct procfs_dir_priv_s *priv;
DEBUGASSERT(mountpt && dir && dir->u.procfs);
priv = dir->u.procfs;
@@ -925,95 +538,204 @@ static int procfs_closedir(FAR struct inode *mountpt,
static int procfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
{
- FAR struct procfs_level_s *priv;
+ FAR struct procfs_dir_priv_s *priv;
+ FAR struct procfs_level0_s *level0;
FAR struct tcb_s *tcb;
+ FAR const char *name;
unsigned int index;
irqstate_t flags;
pid_t pid;
- int ret;
+ int ret = -ENOENT;
DEBUGASSERT(mountpt && dir && dir->u.procfs);
priv = dir->u.procfs;
- /* Have we reached the end of the directory */
+ /* Are we reading the 1st directory level with dynamic PID and static
+ * entries?
+ */
- index = priv->index;
- if (index >= priv->nentries)
+ if (priv->level == 0)
{
- /* We signal the end of the directory by returning the special
- * error -ENOENT
- */
+ level0 = (FAR struct procfs_level0_s *)priv;
- fvdbg("Entry %d: End of directory\n", index);
- ret = -ENOENT;
- }
+ /* Have we reached the end of the PID information */
- /* Are tranversing a first level directory of task IDs */
-
- else if (priv->level == 0)
- {
- FAR struct procfs_level0_s *level0 = (FAR struct procfs_level0_s *)priv;
+ 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.
+ */
- /* Verify that the pid still refers to an active task/thread */
+ while (index < priv->nentries + g_procfsentrycount)
+ {
+ name = g_procfsentries[index - priv->nentries].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_procfsentries[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++;
+ }
+ }
+ }
- pid = level0->pid[index];
+ /* Test if we are at the end of the directory */
- flags = irqsave();
- tcb = sched_gettcb(pid);
- irqrestore(flags);
+ if (index >= priv->nentries + g_procfsentrycount)
+ {
+ /* We signal the end of the directory by returning the special
+ * error -ENOENT
+ */
- if (!tcb)
- {
- fdbg("ERROR: PID %d is no longer valid\n", (int)pid);
- return -ENOENT;
+ fvdbg("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 (name[level0->lastlen] == '/')
+ {
+ 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 */
- /* Save the filename=pid and file type=directory */
+ pid = level0->pid[index];
- dir->fd_dir.d_type = DTYPE_DIRECTORY;
- snprintf(dir->fd_dir.d_name, NAME_MAX+1, "%d", (int)pid);
+ flags = irqsave();
+ tcb = sched_gettcb(pid);
+ irqrestore(flags);
- /* Set up the next directory entry offset. NOTE that we could use the
- * standard f_pos instead of our own private index.
- */
+ if (!tcb)
+ {
+ fdbg("ERROR: PID %d is no longer valid\n", (int)pid);
+ return -ENOENT;
+ }
- level0->index = index + 1;
- ret = OK;
- }
+ /* Save the filename=pid and file type=directory */
- /* No.. We must be tranversing a subdirectory of task attributes */
+ dir->fd_dir.d_type = DTYPE_DIRECTORY;
+ snprintf(dir->fd_dir.d_name, NAME_MAX+1, "%d", (int)pid);
- else
- {
- FAR struct procfs_level1_s *level1 = (FAR struct procfs_level1_s *)priv;
+ /* Set up the next directory entry offset. NOTE that we could use the
+ * standard f_pos instead of our own private index.
+ */
- DEBUGASSERT(priv->level == 1);
+ level0->index = index + 1;
+ ret = OK;
+ }
+#endif /* CONFIG_FS_PROCFS_EXCLUDE_PROCESS */
+ }
- /* Verify that the pid still refers to an active task/thread */
+ /* Are we reading in intermediate subdirectory? */
- pid = level1->pid;
+ else if (priv->level == 1 && priv->procfsentry == NULL)
+ {
+ FAR struct procfs_level1_s *level1;
- flags = irqsave();
- tcb = sched_gettcb(pid);
- irqrestore(flags);
+ 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 (!tcb)
+ if (strncmp(g_procfsentries[level1->index].pathpattern,
+ g_procfsentries[level1->firstindex].pathpattern,
+ level1->subdirlen) == 0)
{
- fdbg("ERROR: PID %d is no longer valid\n", (int)pid);
- return -ENOENT;
- }
+ /* This entry matches. Report the subdir entry */
+
+ name = &g_procfsentries[level1->index].pathpattern[
+ level1->subdirlen + 1];
+ level1->lastlen = strcspn(name, "/");
+ level1->lastread = name;
+ strncpy(dir->fd_dir.d_name, name, level1->lastlen);
+ dir->fd_dir.d_name[level1->lastlen] = '\0';
- /* Save the filename=pid and file type=directory */
+ if (name[level1->lastlen] == '/')
+ {
+ dir->fd_dir.d_type = DTYPE_DIRECTORY;
+ }
+ else
+ {
+ dir->fd_dir.d_type = DTYPE_FILE;
+ }
- dir->fd_dir.d_type = DTYPE_FILE;
- strncpy(dir->fd_dir.d_name, g_attrstrings[index], NAME_MAX+1);
+ level1->index++;
+ ret = OK;
+ }
+ else
+ {
+ /* No more entries in the subdirectory */
- /* Set up the next directory entry offset. NOTE that we could use the
- * standard f_pos instead of our own private index.
+ ret = -ENOENT;
+ }
+ }
+ else
+ {
+ /* We are performing a directory search of one of the subdirectories
+ * and we must let the handler perform the read.
*/
- level1->index = index + 1;
- ret = OK;
+ DEBUGASSERT(priv->procfsentry && priv->procfsentry->ops->readdir);
+ ret = priv->procfsentry->ops->readdir(dir);
}
return ret;
@@ -1028,12 +750,20 @@ static int procfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
static int procfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir)
{
- FAR struct procfs_level_s *priv;
+ FAR struct procfs_dir_priv_s *priv;
DEBUGASSERT(mountpt && dir && dir->u.procfs);
priv = dir->u.procfs;
- priv->index = 0;
+ if (priv->level == 1 && priv->procfsentry == NULL)
+ {
+ priv->index = ((struct procfs_level1_s *) priv)->firstindex;
+ }
+ else
+ {
+ priv->index = 0;
+ }
+
return OK;
}
@@ -1098,12 +828,7 @@ static int procfs_statfs(struct inode *mountpt, struct statfs *buf)
static int procfs_stat(struct inode *mountpt, const char *relpath,
struct stat *buf)
{
- FAR struct tcb_s *tcb;
- unsigned long tmp;
- FAR char *ptr;
- irqstate_t flags;
- pid_t pid;
- int ret;
+ int ret = -ENOSYS;
/* Three path forms are accepted:
*
@@ -1120,71 +845,39 @@ static int procfs_stat(struct inode *mountpt, const char *relpath,
/* It's a read-only directory */
buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR;
-
+ ret = OK;
}
else
{
- /* Otherwise, the first segment of the relative path should be a valid
- * task/thread ID
- */
+ int x;
+ int len = strlen(relpath);
- ptr = NULL;
- tmp = strtoul(relpath, &ptr, 10);
+ /* Perform the stat based on the procfs_entry operations */
- if (!ptr)
+ for (x = 0; x < g_procfsentrycount; x++)
{
- fdbg("ERROR: Invalid path \"%s\"\n", relpath);
- return -ENOENT;
- }
-
- /* A valid PID would be in the range of 0-32767 (0 is reserved for the
- * IDLE thread).
- */
-
- if (tmp >= 32768)
- {
- fdbg("ERROR: Invalid PID %ld\n", tmp);
- return -ENOENT;
- }
+ /* Test if the path matches this entry's specification */
- /* Now verify that a task with this task/thread ID exists */
-
- pid = (pid_t)tmp;
-
- flags = irqsave();
- tcb = sched_gettcb(pid);
- irqrestore(flags);
-
- if (!tcb)
- {
- fdbg("ERROR: PID %d is no longer valid\n", (int)pid);
- return -ENOENT;
- }
+ if (match(g_procfsentries[x].pathpattern, relpath))
+ {
+ /* Match found! Stat using this procfs entry */
- /* Was the <pid> the final element of the path? */
+ DEBUGASSERT(g_procfsentries[x].ops &&
+ g_procfsentries[x].ops->stat);
- if (*ptr == '\0' || strcmp(ptr, "/") == 0)
- {
- /* Yes ... It's a read-only directory */
+ return g_procfsentries[x].ops->stat(relpath, buf);
+ }
- buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR;
- }
- else
- {
- /* Otherwise, the second segment of the relpath should be a well
- * known attribute of the task/thread.
- */
+ /* Test for an internal subdirectory stat */
- ret = procfs_findattr(ptr);
- if (ret < 0)
+ else if (strncmp(g_procfsentries[x].pathpattern, relpath, len) == 0)
{
- fdbg("ERROR: Invalid attribute %s\n", ptr);
- return -ENOENT;
- }
-
- /* It's a read-only file name */
+ /* It's an internal subdirectory */
- buf->st_mode = S_IFREG|S_IROTH|S_IRGRP|S_IRUSR;
+ buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR;
+ ret = OK;
+ break;
+ }
}
}
@@ -1193,7 +886,7 @@ static int procfs_stat(struct inode *mountpt, const char *relpath,
buf->st_size = 0;
buf->st_blksize = 0;
buf->st_blocks = 0;
- return OK;
+ return ret;
}
/****************************************************************************
diff --git a/nuttx/fs/procfs/fs_procfsproc.c b/nuttx/fs/procfs/fs_procfsproc.c
new file mode 100644
index 000000000..d646c6758
--- /dev/null
+++ b/nuttx/fs/procfs/fs_procfsproc.c
@@ -0,0 +1,1003 @@
+/****************************************************************************
+ * fs/procfs/fs_procfsproc.c
+ *
+ * Copyright (C) 2013 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/sched.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/procfs.h>
+#include <nuttx/fs/dirent.h>
+
+#include <arch/irq.h>
+
+#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS)
+#ifndef CONFIG_FS_PROCFS_EXCLUDE_PROCESS
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define STATUS_LINELEN 32
+
+#ifndef MIN
+# define MIN(a,b) ((a < b) ? a : b)
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+/* This enumeration identifies all of the thread attributes that can be
+ * accessed via the procfs file system.
+ */
+
+enum process_attr_e
+{
+ PROCFS_STATUS = 0, /* Task/thread status */
+ PROCFS_CMDLINE, /* Command line */
+};
+#define PROCFS_NATTRS 2
+
+/* This structure describes one open "file" */
+
+struct process_file_s
+{
+ struct procfs_file_s base; /* Base open file structure */
+ uint8_t type; /* See enum process_type_e */
+ pid_t pid; /* Task/thread ID */
+ uint8_t attr; /* See enum process_attr_e */
+ char line[STATUS_LINELEN]; /* Pre-allocated buffer for formatted lines */
+};
+
+/* Level 0 is the directory of active tasks */
+
+struct process_level0_s
+{
+ uint8_t level; /* Directory level. Currently 0 or 1 */
+ uint16_t index; /* Index to the next directory entry */
+ uint16_t nentries; /* Number of directory entries */
+
+ pid_t pid[CONFIG_MAX_TASKS]; /* Snapshot of all active task IDs */
+};
+
+/* Level 1 is the directory of task attributes */
+
+struct process_level1_s
+{
+ struct procfs_dir_priv_s base; /* Base directory private data */
+
+ /* Our specific data for context control */
+
+ pid_t pid; /* ID of task for attributes */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* Helpers */
+
+static int process_findattr(FAR const char *attr);
+static size_t process_addline(FAR struct process_file_s *attr,
+ FAR char *buffer, size_t buflen, size_t linesize,
+ off_t *offset);
+static ssize_t process_status(FAR struct process_file_s *attr,
+ FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen,
+ off_t offset);
+static ssize_t process_cmdline(FAR struct process_file_s *attr,
+ FAR struct tcb_s *tcb, FAR char *buffer, size_t buflen,
+ off_t offset);
+
+/* File system methods */
+
+static int process_open(FAR struct file *filep, FAR const char *relpath,
+ int oflags, mode_t mode);
+static int process_close(FAR struct file *filep);
+static ssize_t process_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen);
+
+static int process_dup(FAR const struct file *oldp,
+ FAR struct file *newp);
+
+static int process_opendir(const char *relpath, FAR struct fs_dirent_s *dir);
+static int process_closedir(FAR struct fs_dirent_s *dir);
+static int process_readdir(FAR struct fs_dirent_s *dir);
+static int process_rewinddir(FAR struct fs_dirent_s *dir);
+
+static int process_stat(FAR const char *relpath, FAR struct stat *buf);
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/* 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 process_operations =
+{
+ process_open, /* open */
+ process_close, /* close */
+ process_read, /* read */
+ NULL, /* write */
+
+ process_dup, /* dup */
+
+ process_opendir, /* opendir */
+ process_closedir, /* closedir */
+ process_readdir, /* readdir */
+ process_rewinddir, /* rewinddir */
+
+ process_stat /* stat */
+};
+
+/* This is the list of all attribute strings. Indexing is with the same
+ * values as enum process_attr_e.
+ */
+
+static const char *g_attrstrings[PROCFS_NATTRS] =
+{
+ "status",
+ "cmdline"
+};
+
+static const char *g_statenames[] =
+{
+ "Invalid",
+ "Pending unlock",
+ "Ready",
+ "Running",
+ "Inactive",
+ "Semaphore wait",
+#ifndef CONFIG_DISABLE_MQUEUE
+ "Signal wait",
+#endif
+#ifndef CONFIG_DISABLE_MQUEUE
+ "MQ not empty wait",
+ "MQ no full wait"
+#endif
+};
+
+static const char *g_ttypenames[4] =
+{
+ "Task",
+ "pthread",
+ "Kernel thread",
+ "--?--"
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: process_findattr
+ ****************************************************************************/
+
+static int process_findattr(FAR const char *attr)
+{
+ int i;
+
+ /* Search every string in g_attrstrings or until a match is found */
+
+ for (i = 0; i < PROCFS_NATTRS; i++)
+ {
+ if (strcmp(g_attrstrings[i], attr) == 0)
+ {
+ return i;
+ }
+ }
+
+ /* Not found */
+
+ return -ENOENT;
+}
+
+/****************************************************************************
+ * Name: process_addline
+ ****************************************************************************/
+
+static size_t process_addline(FAR struct process_file_s *attr,
+ FAR char *buffer, size_t buflen,
+ size_t linesize, off_t *offset)
+{
+ size_t copysize;
+ size_t lnoffset;
+
+ /* Will this line take us past the offset? */
+
+ lnoffset = *offset;
+ if (linesize < lnoffset)
+ {
+ /* No... decrement the offset and return without doing anything */
+
+ *offset -= linesize;
+ return 0;
+ }
+
+ /* Handle the remaining offset */
+
+ linesize -= lnoffset;
+ buffer += lnoffset;
+ *offset = 0;
+
+ /* Copy the line into the user buffer */
+
+ copysize = MIN(linesize, buflen);
+ memcpy(buffer, &attr->line[lnoffset], copysize);
+ return copysize;
+}
+
+/****************************************************************************
+ * Name: process_status
+ ****************************************************************************/
+
+static ssize_t process_status(FAR struct process_file_s *attr,
+ FAR struct tcb_s *tcb, FAR char *buffer,
+ size_t buflen, off_t offset)
+{
+ FAR const char *name;
+ size_t remaining;
+ size_t linesize;
+ size_t copysize;
+ size_t totalsize;
+
+ remaining = buflen;
+ totalsize = 0;
+
+ /* Show the task name */
+
+#if CONFIG_TASK_NAME_SIZE > 0
+ name = tcb->name;
+#else
+ name = "<noname>";
+#endif
+ linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n",
+ "Name:", name);
+ copysize = process_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ if (totalsize >= buflen)
+ {
+ return totalsize;
+ }
+
+ /* Show the thread type */
+
+ linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", "Type:",
+ g_ttypenames[(tcb->flags & TCB_FLAG_TTYPE_MASK) >>
+ TCB_FLAG_TTYPE_SHIFT]);
+ copysize = process_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ if (totalsize >= buflen)
+ {
+ return totalsize;
+ }
+
+ /* Show the thread state */
+
+ linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", "State:",
+ g_statenames[tcb->task_state]);
+ copysize = process_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ if (totalsize >= buflen)
+ {
+ return totalsize;
+ }
+
+ /* Show the thread priority */
+
+#ifdef CONFIG_PRIORITY_INHERITANCE
+ linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%d (%d)\n", "Priority:",
+ tcb->sched_priority, tcb->base_priority);
+#else
+ linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%d\n", "Priority:",
+ tcb->sched_priority);
+#endif
+ copysize = process_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ if (totalsize >= buflen)
+ {
+ return totalsize;
+ }
+
+ /* Show the scheduler */
+
+ linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%s\n", "Scheduler:",
+ tcb->flags & TCB_FLAG_ROUND_ROBIN ? "SCHED_RR" : "SCHED_FIFO");
+ copysize = process_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ if (totalsize >= buflen)
+ {
+ return totalsize;
+ }
+
+ /* Show the signal mast */
+
+#ifndef CONFIG_DISABLE_SIGNALS
+ linesize = snprintf(attr->line, STATUS_LINELEN, "%-12s%08x\n", "SigMask:",
+ tcb->sigprocmask);
+ copysize = process_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+#endif
+
+ return totalsize;
+}
+
+/****************************************************************************
+ * Name: process_cmdline
+ ****************************************************************************/
+
+static ssize_t process_cmdline(FAR struct process_file_s *attr,
+ FAR struct tcb_s *tcb, FAR char *buffer,
+ size_t buflen, off_t offset)
+{
+ FAR struct task_tcb_s *ttcb;
+ FAR const char *name;
+ FAR char **argv;
+ size_t remaining;
+ size_t linesize;
+ size_t copysize;
+ size_t totalsize;
+
+ remaining = buflen;
+ totalsize = 0;
+
+ /* Show the task name */
+
+#if CONFIG_TASK_NAME_SIZE > 0
+ name = tcb->name;
+#else
+ name = "<noname>";
+#endif
+ linesize = strlen(name);
+ memcpy(attr->line, name, linesize);
+ copysize = process_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ if (totalsize >= buflen)
+ {
+ return totalsize;
+ }
+
+#ifndef CONFIG_DISABLE_PTHREAD
+ /* Show the pthread argument */
+
+ if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD)
+ {
+ FAR struct pthread_tcb_s *ptcb = (FAR struct pthread_tcb_s *)tcb;
+
+ linesize = snprintf(attr->line, STATUS_LINELEN, " 0x%p\n", ptcb->arg);
+ copysize = process_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ return totalsize;
+ }
+#endif
+
+ /* Show the task argument list (skipping over the name) */
+
+ ttcb = (FAR struct task_tcb_s *)tcb;
+
+ for (argv = ttcb->argv + 1; *argv; argv++)
+ {
+ linesize = snprintf(attr->line, STATUS_LINELEN, " %s", *argv);
+ copysize = process_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ buffer += copysize;
+ remaining -= copysize;
+
+ if (totalsize >= buflen)
+ {
+ return totalsize;
+ }
+ }
+
+ linesize = snprintf(attr->line, STATUS_LINELEN, "\n");
+ copysize = process_addline(attr, buffer, remaining, linesize, &offset);
+
+ totalsize += copysize;
+ return totalsize;
+}
+
+/****************************************************************************
+ * Name: process_open
+ ****************************************************************************/
+
+static int process_open(FAR struct file *filep, FAR const char *relpath,
+ int oflags, mode_t mode)
+{
+ FAR struct process_file_s *attr;
+ FAR struct tcb_s *tcb;
+ FAR char *ptr;
+ irqstate_t flags;
+ unsigned long tmp;
+ pid_t pid;
+ int attrndx;
+
+ fvdbg("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)
+ {
+ fdbg("ERROR: Only O_RDONLY supported\n");
+ return -EACCES;
+ }
+
+ /* The first segment of the relative path should be a task/thread ID */
+
+ ptr = NULL;
+ tmp = strtoul(relpath, &ptr, 10);
+
+ if (!ptr || *ptr != '/')
+ {
+ fdbg("ERROR: Invalid path \"%s\"\n", relpath);
+ return -ENOENT;
+ }
+
+ /* Skip over the slash */
+
+ ptr++;
+
+ /* A valid PID would be in the range of 0-32767 (0 is reserved for the
+ * IDLE thread).
+ */
+
+ if (tmp >= 32768)
+ {
+ fdbg("ERROR: Invalid PID %ld\n", tmp);
+ return -ENOENT;
+ }
+
+ /* Now verify that a task with this task/thread ID exists */
+
+ pid = (pid_t)tmp;
+
+ flags = irqsave();
+ tcb = sched_gettcb(pid);
+ irqrestore(flags);
+
+ if (!tcb)
+ {
+ fdbg("ERROR: PID %d is no longer valid\n", (int)pid);
+ return -ENOENT;
+ }
+
+ /* The second segment of the relpath should be a well known attribute of
+ * the task/thread.
+ */
+
+ attrndx = process_findattr(ptr);
+ if (attrndx < 0)
+ {
+ fdbg("ERROR: Invalid attribute %s\n", ptr);
+ return -ENOENT;
+ }
+
+ /* Allocate a container to hold the task and attribute selection */
+
+ attr = (FAR struct process_file_s *)kzalloc(sizeof(struct process_file_s));
+ if (!attr)
+ {
+ fdbg("ERROR: Failed to allocate file attributes\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize the file attributes */
+
+ attr->pid = pid;
+ attr->attr = attrndx;
+
+ /* Save the index as the open-specific state in filep->f_priv */
+
+ filep->f_priv = (FAR void *)attr;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: process_close
+ ****************************************************************************/
+
+static int process_close(FAR struct file *filep)
+{
+ FAR struct process_file_s *attr;
+
+ /* Recover our private data from the struct file instance */
+
+ attr = (FAR struct process_file_s *)filep->f_priv;
+ DEBUGASSERT(attr);
+
+ /* Release the file attributes structure */
+
+ kfree(attr);
+ filep->f_priv = NULL;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: process_read
+ ****************************************************************************/
+
+static ssize_t process_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen)
+{
+ FAR struct process_file_s *attr;
+ FAR struct tcb_s *tcb;
+ irqstate_t flags;
+ ssize_t ret;
+
+ fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen);
+
+ /* Recover our private data from the struct file instance */
+
+ attr = (FAR struct process_file_s *)filep->f_priv;
+ DEBUGASSERT(attr);
+
+ /* Verify that the thread is still valid */
+
+ flags = irqsave();
+ tcb = sched_gettcb(attr->pid);
+
+ if (!tcb)
+ {
+ fdbg("ERROR: PID %d is not valid\n", (int)attr->pid);
+ irqrestore(flags);
+ return -ENODEV;
+ }
+
+ /* Provide the requested data */
+
+ switch (attr->attr)
+ {
+ default:
+ case PROCFS_STATUS: /* Task/thread status */
+ ret = process_status(attr, tcb, buffer, buflen, filep->f_pos);
+ break;
+
+ case PROCFS_CMDLINE: /* Command line */
+ ret = process_cmdline(attr, tcb, buffer, buflen, filep->f_pos);
+ break;
+ }
+
+ irqrestore(flags);
+
+ /* Update the file offset */
+
+ if (ret > 0)
+ {
+ filep->f_pos += ret;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: process_dup
+ *
+ * Description:
+ * Duplicate open file data in the new file structure.
+ *
+ ****************************************************************************/
+
+static int process_dup(FAR const struct file *oldp, FAR struct file *newp)
+{
+ FAR struct process_file_s *oldattr;
+ FAR struct process_file_s *newattr;
+
+ fvdbg("Dup %p->%p\n", oldp, newp);
+
+ /* Recover our private data from the old struct file instance */
+
+ oldattr = (FAR struct process_file_s *)oldp->f_priv;
+ DEBUGASSERT(oldattr);
+
+ /* Allocate a new container to hold the task and attribute selection */
+
+ newattr = (FAR struct process_file_s *)kzalloc(sizeof(struct process_file_s));
+ if (!newattr)
+ {
+ fdbg("ERROR: Failed to allocate file attributes\n");
+ return -ENOMEM;
+ }
+
+ /* The copy the file attribtes from the old attributes to the new */
+
+ memcpy(newattr, oldattr, sizeof(struct process_file_s));
+
+ /* Save the new attributes in the new file structure */
+
+ newp->f_priv = (FAR void *)newattr;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: process_opendir
+ *
+ * Description:
+ * Open a directory for read access
+ *
+ ****************************************************************************/
+
+static int process_opendir(FAR const char *relpath, FAR struct fs_dirent_s *dir)
+{
+ FAR struct tcb_s *tcb;
+ FAR void *priv = NULL;
+ irqstate_t flags;
+
+ fvdbg("relpath: \"%s\"\n", relpath ? relpath : "NULL");
+ DEBUGASSERT(relpath && dir && !dir->u.procfs);
+
+ /* The relative must be:
+ *
+ * "<pid>" - The sub-directory of task/thread attributes
+ */
+
+ FAR struct process_level1_s *level1;
+ unsigned long tmp;
+ FAR char *ptr;
+ pid_t pid;
+
+ /* Otherwise, the relative path should be a valid task/thread ID */
+
+ ptr = NULL;
+ tmp = strtoul(relpath, &ptr, 10);
+
+ if (!ptr || (*ptr != '\0' && strcmp(ptr, "/") != 0))
+ {
+ /* strtoul failed or there is something in the path after the pid */
+
+ fdbg("ERROR: Invalid path \"%s\"\n", relpath);
+ return -ENOENT;
+ }
+
+ /* A valid PID would be in the range of 0-32767 (0 is reserved for the
+ * IDLE thread).
+ */
+
+ if (tmp >= 32768)
+ {
+ fdbg("ERROR: Invalid PID %ld\n", tmp);
+ return -ENOENT;
+ }
+
+ /* Now verify that a task with this task/thread ID exists */
+
+ pid = (pid_t)tmp;
+
+ flags = irqsave();
+ tcb = sched_gettcb(pid);
+ irqrestore(flags);
+
+ if (!tcb)
+ {
+ fdbg("ERROR: PID %d is not valid\n", (int)pid);
+ return -ENOENT;
+ }
+
+ /* Was the <pid> the final element of the path? */
+
+ if (*ptr != '\0' && strcmp(ptr, "/") != 0)
+ {
+ /* There is something in the path after the pid */
+
+ fdbg("ERROR: Invalid path \"%s\"\n", relpath);
+ return -ENOENT;
+ }
+
+ /* The path refers to the 1st level sbdirectory. Allocate the level1
+ * dirent structure.
+ */
+
+ level1 = (FAR struct process_level1_s *)
+ kzalloc(sizeof(struct process_level1_s));
+
+ if (!level1)
+ {
+ fdbg("ERROR: Failed to allocate the level1 directory structure\n");
+ return -ENOMEM;
+ }
+
+ level1->base.level = 1;
+ level1->base.nentries = PROCFS_NATTRS;
+ level1->base.index = 0;
+ level1->pid = pid;
+
+ priv = (FAR void *)level1;
+
+ dir->u.procfs = priv;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: process_closedir
+ *
+ * Description: Close the directory listing
+ *
+ ****************************************************************************/
+
+static int process_closedir(FAR struct fs_dirent_s *dir)
+{
+ FAR struct process_level0_s *priv;
+
+ DEBUGASSERT(dir && dir->u.procfs);
+ priv = dir->u.procfs;
+
+ if (priv)
+ {
+ kfree(priv);
+ }
+
+ dir->u.procfs = NULL;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: process_readdir
+ *
+ * Description: Read the next directory entry
+ *
+ ****************************************************************************/
+
+static int process_readdir(struct fs_dirent_s *dir)
+{
+ FAR struct process_level1_s *level1;
+ FAR struct tcb_s *tcb;
+ unsigned int index;
+ irqstate_t flags;
+ pid_t pid;
+ int ret;
+
+ DEBUGASSERT(dir && dir->u.procfs);
+ level1 = dir->u.procfs;
+
+ /* Have we reached the end of the directory */
+
+ index = level1->base.index;
+ if (index >= level1->base.nentries)
+ {
+ /* We signal the end of the directory by returning the special
+ * error -ENOENT
+ */
+
+ fvdbg("Entry %d: End of directory\n", index);
+ ret = -ENOENT;
+ }
+
+ /* We are tranversing a subdirectory of task attributes */
+
+ else
+ {
+ DEBUGASSERT(level1->base.level == 1);
+
+ /* Verify that the pid still refers to an active task/thread */
+
+ pid = level1->pid;
+
+ flags = irqsave();
+ tcb = sched_gettcb(pid);
+ irqrestore(flags);
+
+ if (!tcb)
+ {
+ fdbg("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_FILE;
+ strncpy(dir->fd_dir.d_name, g_attrstrings[index], 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.
+ */
+
+ level1->base.index = index + 1;
+ ret = OK;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: process_rewindir
+ *
+ * Description: Reset directory read to the first entry
+ *
+ ****************************************************************************/
+
+static int process_rewinddir(struct fs_dirent_s *dir)
+{
+ FAR struct process_level0_s *priv;
+
+ DEBUGASSERT(dir && dir->u.procfs);
+ priv = dir->u.procfs;
+
+ priv->index = 0;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: process_stat
+ *
+ * Description: Return information about a file or directory
+ *
+ ****************************************************************************/
+
+static int process_stat(const char *relpath, struct stat *buf)
+{
+ FAR struct tcb_s *tcb;
+ unsigned long tmp;
+ FAR char *ptr;
+ irqstate_t flags;
+ pid_t pid;
+ int ret;
+
+ /* Two path forms are accepted:
+ *
+ * "<pid>" - If <pid> refers to a currently active task/thread, then it
+ * is a directory
+ * "<pid>/<attr>" - If <attr> is a recognized attribute then, then it
+ * is a file.
+ */
+
+ ptr = NULL;
+ tmp = strtoul(relpath, &ptr, 10);
+
+ if (!ptr)
+ {
+ fdbg("ERROR: Invalid path \"%s\"\n", relpath);
+ return -ENOENT;
+ }
+
+ /* A valid PID would be in the range of 0-32767 (0 is reserved for the
+ * IDLE thread).
+ */
+
+ if (tmp >= 32768)
+ {
+ fdbg("ERROR: Invalid PID %ld\n", tmp);
+ return -ENOENT;
+ }
+
+ /* Now verify that a task with this task/thread ID exists */
+
+ pid = (pid_t)tmp;
+
+ flags = irqsave();
+ tcb = sched_gettcb(pid);
+ irqrestore(flags);
+
+ if (!tcb)
+ {
+ fdbg("ERROR: PID %d is no longer valid\n", (int)pid);
+ return -ENOENT;
+ }
+
+ /* Was the <pid> the final element of the path? */
+
+ if (*ptr == '\0' || strcmp(ptr, "/") == 0)
+ {
+ /* Yes ... It's a read-only directory */
+
+ buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR;
+ }
+ else
+ {
+ /* Otherwise, the second segment of the relpath should be a well
+ * known attribute of the task/thread.
+ */
+
+ ret = process_findattr(ptr);
+ if (ret < 0)
+ {
+ fdbg("ERROR: Invalid attribute %s\n", ptr);
+ return -ENOENT;
+ }
+
+ /* It's a read-only file name */
+
+ buf->st_mode = S_IFREG|S_IROTH|S_IRGRP|S_IRUSR;
+ }
+
+ /* File/directory size, access block size */
+
+ buf->st_size = 0;
+ buf->st_blksize = 0;
+ buf->st_blocks = 0;
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+#endif /* CONFIG_FS_PROCFS_EXCLUDE_PROCESS */
+#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS */
diff --git a/nuttx/fs/procfs/fs_skeleton.c b/nuttx/fs/procfs/fs_skeleton.c
new file mode 100644
index 000000000..e9b206941
--- /dev/null
+++ b/nuttx/fs/procfs/fs_skeleton.c
@@ -0,0 +1,460 @@
+/****************************************************************************
+ * fs/procfs/fs_skeleton.c
+ *
+ * Copyright (C) 2013 Ken Pettit. All rights reserved.
+ * Author: Ken Pettit <pettitkd@gmail.com>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/sched.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/procfs.h>
+#include <nuttx/fs/dirent.h>
+
+#include <arch/irq.h>
+
+#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+/* This enumeration identifies all of the thread attributes that can be
+ * accessed via the procfs file system.
+ */
+
+/* This structure describes one open "file" */
+
+struct skel_file_s
+{
+ struct procfs_file_s base; /* Base open file structure */
+
+ /* Add context specific data types for managing an open file here */
+};
+
+/* Level 1 is the directory of attributes */
+
+struct skel_level1_s
+{
+ struct procfs_dir_priv_s base; /* Base directory private data */
+
+ /* Add context specific data types here for managing the directory
+ * open / read / stat, etc.
+ */
+
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* File system methods */
+
+static int skel_open(FAR struct file *filep, FAR const char *relpath,
+ int oflags, mode_t mode);
+static int skel_close(FAR struct file *filep);
+static ssize_t skel_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen);
+
+static int skel_dup(FAR const struct file *oldp,
+ FAR struct file *newp);
+
+static int skel_opendir(const char *relpath, FAR struct fs_dirent_s *dir);
+static int skel_closedir(FAR struct fs_dirent_s *dir);
+static int skel_readdir(FAR struct fs_dirent_s *dir);
+static int skel_rewinddir(FAR struct fs_dirent_s *dir);
+
+static int skel_stat(FAR const char *relpath, FAR struct stat *buf);
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/* See include/nutts/fs/procfs.h
+ * We use the old-fashioned kind of initializers so that this will compile
+ * with any compiler.
+ */
+
+const struct procfs_operations skel_procfsoperations =
+{
+ skel_open, /* open */
+ skel_close, /* close */
+ skel_read, /* read */
+
+ /* TODO: Decide if this driver supports write */
+ NULL, /* write */
+
+ skel_dup, /* dup */
+
+ skel_opendir, /* opendir */
+ skel_closedir, /* closedir */
+ skel_readdir, /* readdir */
+ skel_rewinddir, /* rewinddir */
+
+ skel_stat /* stat */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: skel_open
+ ****************************************************************************/
+
+static int skel_open(FAR struct file *filep, FAR const char *relpath,
+ int oflags, mode_t mode)
+{
+ FAR struct skel_file_s *priv;
+
+ fvdbg("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) &&
+ (skel_procfsoperations.write == NULL))
+ {
+ fdbg("ERROR: Only O_RDONLY supported\n");
+ return -EACCES;
+ }
+
+ /* Allocate a container to hold the task and attribute selection */
+
+ priv = (FAR struct skel_file_s *)kzalloc(sizeof(struct skel_file_s));
+ if (!priv)
+ {
+ fdbg("ERROR: Failed to allocate file attributes\n");
+ return -ENOMEM;
+ }
+
+ /* TODO: Initialize the context specific data here */
+
+
+ /* Save the index as the open-specific state in filep->f_priv */
+
+ filep->f_priv = (FAR void *)priv;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: skel_close
+ ****************************************************************************/
+
+static int skel_close(FAR struct file *filep)
+{
+ FAR struct skel_file_s *priv;
+
+ /* Recover our private data from the struct file instance */
+
+ priv = (FAR struct skel_file_s *)filep->f_priv;
+ DEBUGASSERT(priv);
+
+ /* Release the file attributes structure */
+
+ kfree(priv);
+ filep->f_priv = NULL;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: skel_read
+ ****************************************************************************/
+
+static ssize_t skel_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen)
+{
+ FAR struct skel_file_s *priv;
+ ssize_t ret;
+
+ fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen);
+
+ /* Recover our private data from the struct file instance */
+
+ priv = (FAR struct skel_file_s *)filep->f_priv;
+ DEBUGASSERT(priv);
+
+ /* TODO: Provide the requested data */
+
+ ret = 0;
+
+ /* Update the file offset */
+
+ if (ret > 0)
+ {
+ filep->f_pos += ret;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: skel_dup
+ *
+ * Description:
+ * Duplicate open file data in the new file structure.
+ *
+ ****************************************************************************/
+
+static int skel_dup(FAR const struct file *oldp, FAR struct file *newp)
+{
+ FAR struct skel_file_s *oldpriv;
+ FAR struct skel_file_s *newpriv;
+
+ fvdbg("Dup %p->%p\n", oldp, newp);
+
+ /* Recover our private data from the old struct file instance */
+
+ oldpriv = (FAR struct skel_file_s *)oldp->f_priv;
+ DEBUGASSERT(oldpriv);
+
+ /* Allocate a new container to hold the task and attribute selection */
+
+ newpriv = (FAR struct skel_file_s *)kzalloc(sizeof(struct skel_file_s));
+ if (!newpriv)
+ {
+ fdbg("ERROR: Failed to allocate file attributes\n");
+ return -ENOMEM;
+ }
+
+ /* The copy the file attribtes from the old attributes to the new */
+
+ memcpy(newpriv, oldpriv, sizeof(struct skel_file_s));
+
+ /* Save the new attributes in the new file structure */
+
+ newp->f_priv = (FAR void *)newpriv;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: skel_opendir
+ *
+ * Description:
+ * Open a directory for read access
+ *
+ ****************************************************************************/
+
+static int skel_opendir(FAR const char *relpath, FAR struct fs_dirent_s *dir)
+{
+ FAR struct skel_level1_s *level1;
+
+ fvdbg("relpath: \"%s\"\n", relpath ? relpath : "NULL");
+ DEBUGASSERT(relpath && dir && !dir->u.procfs);
+
+ /* The path refers to the 1st level sbdirectory. Allocate the level1
+ * dirent structure.
+ */
+
+ level1 = (FAR struct skel_level1_s *)
+ kzalloc(sizeof(struct skel_level1_s));
+
+ if (!level1)
+ {
+ fdbg("ERROR: Failed to allocate the level1 directory structure\n");
+ return -ENOMEM;
+ }
+
+ /* TODO: Initialze context specific data */
+
+
+ /* Initialze base structure components */
+
+ level1->base.level = 1;
+ level1->base.nentries = 0;
+ level1->base.index = 0;
+
+ dir->u.procfs = (FAR void *) level1;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: skel_closedir
+ *
+ * Description: Close the directory listing
+ *
+ ****************************************************************************/
+
+static int skel_closedir(FAR struct fs_dirent_s *dir)
+{
+ FAR struct skel_level1_s *priv;
+
+ DEBUGASSERT(dir && dir->u.procfs);
+ priv = dir->u.procfs;
+
+ if (priv)
+ {
+ kfree(priv);
+ }
+
+ dir->u.procfs = NULL;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: skel_readdir
+ *
+ * Description: Read the next directory entry
+ *
+ ****************************************************************************/
+
+static int skel_readdir(struct fs_dirent_s *dir)
+{
+ FAR struct skel_level1_s *level1;
+ char filename[16];
+ int ret, index;
+
+ DEBUGASSERT(dir && dir->u.procfs);
+ level1 = dir->u.procfs;
+
+ /* TODO: Perform device specific readdir function here. This may
+ * or may not involve validating the nentries variable
+ * in the base depending on the implementation.
+ */
+
+ /* Have we reached the end of the directory */
+
+ index = level1->base.index;
+ if (index >= level1->base.nentries)
+ {
+ /* We signal the end of the directory by returning the special
+ * error -ENOENT
+ */
+
+ fvdbg("Entry %d: End of directory\n", index);
+ ret = -ENOENT;
+ }
+
+ /* We are tranversing a subdirectory of task attributes */
+
+ else
+ {
+ DEBUGASSERT(level1->base.level == 1);
+
+ /* TODO: Add device specific entries */
+
+ strcpy(filename, "dummy");
+
+ /* TODO: Specify the type of entry */
+
+ dir->fd_dir.d_type = DTYPE_FILE;
+ strncpy(dir->fd_dir.d_name, filename, 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.
+ */
+
+ ret = OK;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: skel_rewindir
+ *
+ * Description: Reset directory read to the first entry
+ *
+ ****************************************************************************/
+
+static int skel_rewinddir(struct fs_dirent_s *dir)
+{
+ FAR struct skel_level1_s *priv;
+
+ DEBUGASSERT(dir && dir->u.procfs);
+ priv = dir->u.procfs;
+
+ priv->base.index = 0;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: skel_stat
+ *
+ * Description: Return information about a file or directory
+ *
+ ****************************************************************************/
+
+static int skel_stat(const char *relpath, struct stat *buf)
+{
+ int ret = -ENOENT;
+
+ /* TODO: Decide if the relpath is valid and if it is a file
+ * or a directory and set it's permissions.
+ */
+
+ buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR;
+ ret = OK;
+
+ /* File/directory size, access block size */
+
+ buf->st_size = 0;
+ buf->st_blksize = 0;
+ buf->st_blocks = 0;
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS */
diff --git a/nuttx/fs/smartfs/Make.defs b/nuttx/fs/smartfs/Make.defs
index 9604d546f..b77e1cfd3 100644
--- a/nuttx/fs/smartfs/Make.defs
+++ b/nuttx/fs/smartfs/Make.defs
@@ -38,7 +38,7 @@ ifeq ($(CONFIG_FS_SMARTFS),y)
# Files required for SmartFS file system support
ASRCS +=
-CSRCS += smartfs_smart.c smartfs_utils.c
+CSRCS += smartfs_smart.c smartfs_utils.c smartfs_procfs.c
# Files required for mksmartfs utility function
diff --git a/nuttx/fs/smartfs/smartfs_procfs.c b/nuttx/fs/smartfs/smartfs_procfs.c
new file mode 100644
index 000000000..64f7101d4
--- /dev/null
+++ b/nuttx/fs/smartfs/smartfs_procfs.c
@@ -0,0 +1,460 @@
+/****************************************************************************
+ * fs/smartfs/smartfs_procfs.c
+ *
+ * Copyright (C) 2013 Ken Pettit. All rights reserved.
+ * Author: Ken Pettit <pettitkd@gmail.com>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/sched.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/procfs.h>
+#include <nuttx/fs/dirent.h>
+
+#include <arch/irq.h>
+
+#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_EXCLUDE_SMARTFS)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+/* This enumeration identifies all of the thread attributes that can be
+ * accessed via the procfs file system.
+ */
+
+/* This structure describes one open "file" */
+
+struct smartfs_file_s
+{
+ struct procfs_file_s base; /* Base open file structure */
+
+ /* Add context specific data types for managing an open file here */
+};
+
+/* Level 1 is the directory of attributes */
+
+struct smartfs_level1_s
+{
+ struct procfs_dir_priv_s base; /* Base directory private data */
+
+ /* Add context specific data types here for managing the directory
+ * open / read / stat, etc.
+ */
+
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* File system methods */
+
+static int smartfs_open(FAR struct file *filep, FAR const char *relpath,
+ int oflags, mode_t mode);
+static int smartfs_close(FAR struct file *filep);
+static ssize_t smartfs_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen);
+
+static int smartfs_dup(FAR const struct file *oldp,
+ FAR struct file *newp);
+
+static int smartfs_opendir(const char *relpath, FAR struct fs_dirent_s *dir);
+static int smartfs_closedir(FAR struct fs_dirent_s *dir);
+static int smartfs_readdir(FAR struct fs_dirent_s *dir);
+static int smartfs_rewinddir(FAR struct fs_dirent_s *dir);
+
+static int smartfs_stat(FAR const char *relpath, FAR struct stat *buf);
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/* See include/nutts/fs/procfs.h
+ * We use the old-fashioned kind of initializers so that this will compile
+ * with any compiler.
+ */
+
+const struct procfs_operations smartfs_procfsoperations =
+{
+ smartfs_open, /* open */
+ smartfs_close, /* close */
+ smartfs_read, /* read */
+
+ /* TODO: Decide if this deiver supports write */
+ NULL, /* write */
+
+ smartfs_dup, /* dup */
+
+ smartfs_opendir, /* opendir */
+ smartfs_closedir, /* closedir */
+ smartfs_readdir, /* readdir */
+ smartfs_rewinddir, /* rewinddir */
+
+ smartfs_stat /* stat */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: smartfs_open
+ ****************************************************************************/
+
+static int smartfs_open(FAR struct file *filep, FAR const char *relpath,
+ int oflags, mode_t mode)
+{
+ FAR struct smartfs_file_s *priv;
+
+ fvdbg("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) &&
+ (smartfs_procfsoperations.write == NULL))
+ {
+ fdbg("ERROR: Only O_RDONLY supported\n");
+ return -EACCES;
+ }
+
+ /* Allocate a container to hold the task and attribute selection */
+
+ priv = (FAR struct smartfs_file_s *)kzalloc(sizeof(struct smartfs_file_s));
+ if (!priv)
+ {
+ fdbg("ERROR: Failed to allocate file attributes\n");
+ return -ENOMEM;
+ }
+
+ /* TODO: Initialize the context specific data here */
+
+
+ /* Save the index as the open-specific state in filep->f_priv */
+
+ filep->f_priv = (FAR void *)priv;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: smartfs_close
+ ****************************************************************************/
+
+static int smartfs_close(FAR struct file *filep)
+{
+ FAR struct smartfs_file_s *priv;
+
+ /* Recover our private data from the struct file instance */
+
+ priv = (FAR struct smartfs_file_s *)filep->f_priv;
+ DEBUGASSERT(priv);
+
+ /* Release the file attributes structure */
+
+ kfree(priv);
+ filep->f_priv = NULL;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: smartfs_read
+ ****************************************************************************/
+
+static ssize_t smartfs_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen)
+{
+ FAR struct smartfs_file_s *priv;
+ ssize_t ret;
+
+ fvdbg("buffer=%p buflen=%d\n", buffer, (int)buflen);
+
+ /* Recover our private data from the struct file instance */
+
+ priv = (FAR struct smartfs_file_s *)filep->f_priv;
+ DEBUGASSERT(priv);
+
+ /* TODO: Provide the requested data */
+
+ ret = 0;
+
+ /* Update the file offset */
+
+ if (ret > 0)
+ {
+ filep->f_pos += ret;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_dup
+ *
+ * Description:
+ * Duplicate open file data in the new file structure.
+ *
+ ****************************************************************************/
+
+static int smartfs_dup(FAR const struct file *oldp, FAR struct file *newp)
+{
+ FAR struct smartfs_file_s *oldpriv;
+ FAR struct smartfs_file_s *newpriv;
+
+ fvdbg("Dup %p->%p\n", oldp, newp);
+
+ /* Recover our private data from the old struct file instance */
+
+ oldpriv = (FAR struct smartfs_file_s *)oldp->f_priv;
+ DEBUGASSERT(oldpriv);
+
+ /* Allocate a new container to hold the task and attribute selection */
+
+ newpriv = (FAR struct smartfs_file_s *)kzalloc(sizeof(struct smartfs_file_s));
+ if (!newpriv)
+ {
+ fdbg("ERROR: Failed to allocate file attributes\n");
+ return -ENOMEM;
+ }
+
+ /* The copy the file attribtes from the old attributes to the new */
+
+ memcpy(newpriv, oldpriv, sizeof(struct smartfs_file_s));
+
+ /* Save the new attributes in the new file structure */
+
+ newp->f_priv = (FAR void *)newpriv;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: smartfs_opendir
+ *
+ * Description:
+ * Open a directory for read access
+ *
+ ****************************************************************************/
+
+static int smartfs_opendir(FAR const char *relpath, FAR struct fs_dirent_s *dir)
+{
+ FAR struct smartfs_level1_s *level1;
+
+ fvdbg("relpath: \"%s\"\n", relpath ? relpath : "NULL");
+ DEBUGASSERT(relpath && dir && !dir->u.procfs);
+
+ /* The path refers to the 1st level sbdirectory. Allocate the level1
+ * dirent structure.
+ */
+
+ level1 = (FAR struct smartfs_level1_s *)
+ kzalloc(sizeof(struct smartfs_level1_s));
+
+ if (!level1)
+ {
+ fdbg("ERROR: Failed to allocate the level1 directory structure\n");
+ return -ENOMEM;
+ }
+
+ /* TODO: Initialze context specific data */
+
+
+ /* Initialze base structure components */
+
+ level1->base.level = 1;
+ level1->base.nentries = 0;
+ level1->base.index = 0;
+
+ dir->u.procfs = (FAR void *) level1;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: smartfs_closedir
+ *
+ * Description: Close the directory listing
+ *
+ ****************************************************************************/
+
+static int smartfs_closedir(FAR struct fs_dirent_s *dir)
+{
+ FAR struct smartfs_level1_s *priv;
+
+ DEBUGASSERT(dir && dir->u.procfs);
+ priv = dir->u.procfs;
+
+ if (priv)
+ {
+ kfree(priv);
+ }
+
+ dir->u.procfs = NULL;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: smartfs_readdir
+ *
+ * Description: Read the next directory entry
+ *
+ ****************************************************************************/
+
+static int smartfs_readdir(struct fs_dirent_s *dir)
+{
+ FAR struct smartfs_level1_s *level1;
+ char filename[16];
+ int ret, index;
+
+ DEBUGASSERT(dir && dir->u.procfs);
+ level1 = dir->u.procfs;
+
+ /* TODO: Perform device specific readdir function here. This may
+ * or may not involve validating the nentries variable
+ * in the base depending on the implementation.
+ */
+
+ /* Have we reached the end of the directory */
+
+ index = level1->base.index;
+ if (index >= level1->base.nentries)
+ {
+ /* We signal the end of the directory by returning the special
+ * error -ENOENT
+ */
+
+ fvdbg("Entry %d: End of directory\n", index);
+ ret = -ENOENT;
+ }
+
+ /* We are tranversing a subdirectory of task attributes */
+
+ else
+ {
+ DEBUGASSERT(level1->base.level == 1);
+
+ /* TODO: Add device specific entries */
+
+ strcpy(filename, "dummy");
+
+ /* TODO: Specify the type of entry */
+
+ dir->fd_dir.d_type = DTYPE_FILE;
+ strncpy(dir->fd_dir.d_name, filename, 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.
+ */
+
+ ret = OK;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_rewindir
+ *
+ * Description: Reset directory read to the first entry
+ *
+ ****************************************************************************/
+
+static int smartfs_rewinddir(struct fs_dirent_s *dir)
+{
+ FAR struct smartfs_level1_s *priv;
+
+ DEBUGASSERT(dir && dir->u.procfs);
+ priv = dir->u.procfs;
+
+ priv->base.index = 0;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: smartfs_stat
+ *
+ * Description: Return information about a file or directory
+ *
+ ****************************************************************************/
+
+static int smartfs_stat(const char *relpath, struct stat *buf)
+{
+ int ret = -ENOENT;
+
+ /* TODO: Decide if the relpath is valid and if it is a file
+ * or a directory and set it's permissions.
+ */
+
+ buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR;
+ ret = OK;
+
+ /* File/directory size, access block size */
+
+ buf->st_size = 0;
+ buf->st_blksize = 0;
+ buf->st_blocks = 0;
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+#endif /* CONFIG_FS_PROCFS && !CONFIG_FS_PROCFS_EXCLUDE_SMARTFS */