This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 86e00896d3580c6f9ac9e9e667b6c5a54534fcbd
Author: guohao15 <[email protected]>
AuthorDate: Fri Aug 18 20:14:38 2023 +0800

    fs:notify add support for inotify
    
    support API: open close read write unlink mkdir rename fchstat rmdir symlink
    
    Signed-off-by: guohao15 <[email protected]>
---
 fs/Kconfig                                         |    1 +
 fs/Makefile                                        |    1 +
 fs/driver/fs_registerblockdriver.c                 |    7 +-
 fs/driver/fs_registerdriver.c                      |    7 +-
 fs/driver/fs_registermtddriver.c                   |    7 +-
 fs/driver/fs_registerpipedriver.c                  |    7 +-
 fs/driver/fs_unregisterblockdriver.c               |    6 +
 fs/driver/fs_unregisterdriver.c                    |    6 +
 fs/driver/fs_unregistermtddriver.c                 |    6 +
 fs/driver/fs_unregisterpipedriver.c                |    6 +
 fs/fs_initialize.c                                 |    5 +
 fs/mount/fs_mount.c                                |    6 +-
 fs/mount/fs_umount2.c                              |    4 +
 fs/mqueue/mq_open.c                                |    4 +
 fs/mqueue/mq_unlink.c                              |    4 +
 fs/notify/Kconfig                                  |   25 +
 fs/notify/Make.defs                                |   29 +
 fs/notify/inotify.c                                | 1356 ++++++++++++++++++++
 .../fs_unregisterblockdriver.c => notify/notify.h} |   49 +-
 fs/semaphore/sem_close.c                           |    5 +-
 fs/semaphore/sem_open.c                            |    4 +
 fs/semaphore/sem_unlink.c                          |    4 +
 fs/shm/shm_open.c                                  |    8 +
 fs/shm/shm_unlink.c                                |    8 +
 fs/vfs/fs_close.c                                  |    1 +
 fs/vfs/fs_fchstat.c                                |    8 +
 fs/vfs/fs_mkdir.c                                  |    4 +
 fs/vfs/fs_open.c                                   |   14 +-
 fs/vfs/fs_pseudofile.c                             |    4 +
 fs/vfs/fs_read.c                                   |    8 +
 fs/vfs/fs_rename.c                                 |   41 +
 fs/vfs/fs_rmdir.c                                  |    4 +
 fs/vfs/fs_symlink.c                                |    4 +
 fs/vfs/fs_unlink.c                                 |    4 +
 fs/vfs/fs_write.c                                  |   12 +-
 include/sys/inotify.h                              |  169 +++
 include/sys/syscall_lookup.h                       |    9 +
 syscall/syscall.csv                                |    4 +
 38 files changed, 1820 insertions(+), 31 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index e27224adf0..3485a1878d 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -118,6 +118,7 @@ source "fs/mqueue/Kconfig"
 source "fs/shm/Kconfig"
 source "fs/mmap/Kconfig"
 source "fs/partition/Kconfig"
+source "fs/notify/Kconfig"
 source "fs/fat/Kconfig"
 source "fs/nfs/Kconfig"
 source "fs/nxffs/Kconfig"
diff --git a/fs/Makefile b/fs/Makefile
index 29449b7983..d18a339973 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -59,6 +59,7 @@ include rpmsgfs/Make.defs
 include zipfs/Make.defs
 include mnemofs/Make.defs
 include v9fs/Make.defs
+include notify/Make.defs
 endif
 
 CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)fs
diff --git a/fs/driver/fs_registerblockdriver.c 
b/fs/driver/fs_registerblockdriver.c
index 94a586a957..63bedb73f6 100644
--- a/fs/driver/fs_registerblockdriver.c
+++ b/fs/driver/fs_registerblockdriver.c
@@ -30,6 +30,7 @@
 #include <nuttx/fs/fs.h>
 
 #include "inode/inode.h"
+#include "notify/notify.h"
 
 #ifndef CONFIG_DISABLE_MOUNTPOINT
 
@@ -90,7 +91,11 @@ int register_blockdriver(FAR const char *path,
 
       node->u.i_bops  = bops;
       node->i_private = priv;
-      ret             = OK;
+      inode_unlock();
+#ifdef CONFIG_FS_NOTIFY
+      notify_create(path);
+#endif
+      return OK;
     }
 
   inode_unlock();
diff --git a/fs/driver/fs_registerdriver.c b/fs/driver/fs_registerdriver.c
index 1f3be6a4bc..b3db8b9bde 100644
--- a/fs/driver/fs_registerdriver.c
+++ b/fs/driver/fs_registerdriver.c
@@ -31,6 +31,7 @@
 #include <nuttx/sched_note.h>
 
 #include "inode/inode.h"
+#include "notify/notify.h"
 
 /****************************************************************************
  * Public Functions
@@ -89,7 +90,11 @@ int register_driver(FAR const char *path,
 
       node->u.i_ops   = fops;
       node->i_private = priv;
-      ret             = OK;
+      inode_unlock();
+#ifdef CONFIG_FS_NOTIFY
+      notify_create(path);
+#endif
+      return OK;
     }
 
   inode_unlock();
diff --git a/fs/driver/fs_registermtddriver.c b/fs/driver/fs_registermtddriver.c
index e9f6ef0616..8eb76b13d9 100644
--- a/fs/driver/fs_registermtddriver.c
+++ b/fs/driver/fs_registermtddriver.c
@@ -31,6 +31,7 @@
 #include <nuttx/mtd/mtd.h>
 
 #include "inode/inode.h"
+#include "notify/notify.h"
 
 #if defined(CONFIG_MTD) && !defined(CONFIG_DISABLE_MOUNTPOINT)
 
@@ -90,7 +91,11 @@ int register_mtddriver(FAR const char *path, FAR struct 
mtd_dev_s *mtd,
 
       node->u.i_mtd   = mtd;
       node->i_private = priv;
-      ret             = OK;
+      inode_unlock();
+#ifdef CONFIG_FS_NOTIFY
+      notify_create(path);
+#endif
+      return OK;
     }
 
   inode_unlock();
diff --git a/fs/driver/fs_registerpipedriver.c 
b/fs/driver/fs_registerpipedriver.c
index 031d4c66f8..ed5f48d8a1 100644
--- a/fs/driver/fs_registerpipedriver.c
+++ b/fs/driver/fs_registerpipedriver.c
@@ -30,6 +30,7 @@
 #include <nuttx/fs/fs.h>
 
 #include "inode/inode.h"
+#include "notify/notify.h"
 
 #ifdef CONFIG_PIPES
 
@@ -88,7 +89,11 @@ int register_pipedriver(FAR const char *path,
 
       node->u.i_ops   = fops;
       node->i_private = priv;
-      ret             = OK;
+      inode_unlock();
+#ifdef CONFIG_FS_NOTIFY
+      notify_create(path);
+#endif
+      return OK;
     }
 
   inode_unlock();
diff --git a/fs/driver/fs_unregisterblockdriver.c 
b/fs/driver/fs_unregisterblockdriver.c
index be697e1f96..73cd6982b8 100644
--- a/fs/driver/fs_unregisterblockdriver.c
+++ b/fs/driver/fs_unregisterblockdriver.c
@@ -27,6 +27,7 @@
 #include <nuttx/fs/fs.h>
 
 #include "inode/inode.h"
+#include "notify/notify.h"
 
 /****************************************************************************
  * Public Functions
@@ -49,7 +50,12 @@ int unregister_blockdriver(FAR const char *path)
     {
       ret = inode_remove(path);
       inode_unlock();
+#ifdef CONFIG_FS_NOTIFY
+      notify_unlink(path);
+#endif
+      return OK;
     }
 
+  inode_unlock();
   return ret;
 }
diff --git a/fs/driver/fs_unregisterdriver.c b/fs/driver/fs_unregisterdriver.c
index 0b5d9b6c76..9b40c1f63f 100644
--- a/fs/driver/fs_unregisterdriver.c
+++ b/fs/driver/fs_unregisterdriver.c
@@ -27,6 +27,7 @@
 #include <nuttx/fs/fs.h>
 
 #include "inode/inode.h"
+#include "notify/notify.h"
 
 /****************************************************************************
  * Public Functions
@@ -59,7 +60,12 @@ int unregister_driver(FAR const char *path)
     {
       ret = inode_remove(path);
       inode_unlock();
+#ifdef CONFIG_FS_NOTIFY
+      notify_unlink(path);
+#endif
+      return OK;
     }
 
+  inode_unlock();
   return ret;
 }
diff --git a/fs/driver/fs_unregistermtddriver.c 
b/fs/driver/fs_unregistermtddriver.c
index 3442e92cce..b99a46bc91 100644
--- a/fs/driver/fs_unregistermtddriver.c
+++ b/fs/driver/fs_unregistermtddriver.c
@@ -28,6 +28,7 @@
 #include <nuttx/mtd/mtd.h>
 
 #include "inode/inode.h"
+#include "notify/notify.h"
 
 /****************************************************************************
  * Public Functions
@@ -50,7 +51,12 @@ int unregister_mtddriver(FAR const char *path)
     {
       ret = inode_remove(path);
       inode_unlock();
+#ifdef CONFIG_FS_NOTIFY
+      notify_unlink(path);
+#endif
+      return OK;
     }
 
+  inode_unlock();
   return ret;
 }
diff --git a/fs/driver/fs_unregisterpipedriver.c 
b/fs/driver/fs_unregisterpipedriver.c
index c8253ca693..f29b90ae58 100644
--- a/fs/driver/fs_unregisterpipedriver.c
+++ b/fs/driver/fs_unregisterpipedriver.c
@@ -27,6 +27,7 @@
 #include <nuttx/fs/fs.h>
 
 #include "inode/inode.h"
+#include "notify/notify.h"
 
 #ifdef CONFIG_PIPES
 
@@ -51,8 +52,13 @@ int unregister_pipedriver(FAR const char *path)
     {
       ret = inode_remove(path);
       inode_unlock();
+#ifdef CONFIG_FS_NOTIFY
+      notify_unlink(path);
+#endif
+      return OK;
     }
 
+  inode_unlock();
   return ret;
 }
 
diff --git a/fs/fs_initialize.c b/fs/fs_initialize.c
index c7285ee1ab..4424711196 100644
--- a/fs/fs_initialize.c
+++ b/fs/fs_initialize.c
@@ -26,6 +26,7 @@
 #include <nuttx/reboot_notifier.h>
 #include <nuttx/trace.h>
 
+#include "notify/notify.h"
 #include "rpmsgfs/rpmsgfs.h"
 #include "inode/inode.h"
 #include "aio/aio.h"
@@ -97,6 +98,10 @@ void fs_initialize(void)
   rpmsgfs_server_init();
 #endif
 
+#ifdef CONFIG_FS_NOTIFY
+  notify_initialize();
+#endif
+
   register_reboot_notifier(&g_sync_nb);
   fs_trace_end();
 }
diff --git a/fs/mount/fs_mount.c b/fs/mount/fs_mount.c
index 40c6b54182..b6804defe6 100644
--- a/fs/mount/fs_mount.c
+++ b/fs/mount/fs_mount.c
@@ -33,8 +33,9 @@
 
 #include <nuttx/fs/fs.h>
 
-#include "inode/inode.h"
 #include "driver/driver.h"
+#include "inode/inode.h"
+#include "notify/notify.h"
 
 /****************************************************************************
  * Pre-processor Definitions
@@ -494,6 +495,9 @@ int nx_mount(FAR const char *source, FAR const char *target,
 
 #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
   RELEASE_SEARCH(&desc);
+#endif
+#ifdef CONFIG_FS_NOTIFY
+  notify_create(target);
 #endif
   return OK;
 
diff --git a/fs/mount/fs_umount2.c b/fs/mount/fs_umount2.c
index e7b9206858..121be901a8 100644
--- a/fs/mount/fs_umount2.c
+++ b/fs/mount/fs_umount2.c
@@ -32,6 +32,7 @@
 #include <nuttx/fs/fs.h>
 
 #include "inode/inode.h"
+#include "notify/notify.h"
 
 /****************************************************************************
  * Public Functions
@@ -184,6 +185,9 @@ int nx_umount2(FAR const char *target, unsigned int flags)
     }
 
   RELEASE_SEARCH(&desc);
+#ifdef CONFIG_FS_NOTIFY
+  notify_unmount(target);
+#endif
   return OK;
 
   /* A lot of goto's!  But they make the error handling much simpler */
diff --git a/fs/mqueue/mq_open.c b/fs/mqueue/mq_open.c
index a95a1575ab..7b30440de5 100644
--- a/fs/mqueue/mq_open.c
+++ b/fs/mqueue/mq_open.c
@@ -39,6 +39,7 @@
 
 #include "inode/inode.h"
 #include "mqueue/mqueue.h"
+#include "notify/notify.h"
 
 /****************************************************************************
  * Private Functions Prototypes
@@ -331,6 +332,9 @@ static int file_mq_vopen(FAR struct file *mq, FAR const 
char *mq_name,
 
   RELEASE_SEARCH(&desc);
   leave_critical_section(flags);
+#ifdef CONFIG_FS_NOTIFY
+  notify_open(fullpath, oflags);
+#endif
   return OK;
 
 errout_with_inode:
diff --git a/fs/mqueue/mq_unlink.c b/fs/mqueue/mq_unlink.c
index 55a2630243..5d205cfb0b 100644
--- a/fs/mqueue/mq_unlink.c
+++ b/fs/mqueue/mq_unlink.c
@@ -34,6 +34,7 @@
 
 #include "inode/inode.h"
 #include "mqueue/mqueue.h"
+#include "notify/notify.h"
 
 /****************************************************************************
  * Private Functions
@@ -173,6 +174,9 @@ int file_mq_unlink(FAR const char *mq_name)
   inode_unlock();
   mq_inode_release(inode);
   RELEASE_SEARCH(&desc);
+#ifdef CONFIG_FS_NOTIFY
+  notify_unlink(fullpath);
+#endif
   return OK;
 
 errout_with_lock:
diff --git a/fs/notify/Kconfig b/fs/notify/Kconfig
new file mode 100644
index 0000000000..13fe9ce179
--- /dev/null
+++ b/fs/notify/Kconfig
@@ -0,0 +1,25 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config FS_NOTIFY
+       bool "FS Notify System"
+       default n
+       ---help---
+               The Fsnotify System
+
+if FS_NOTIFY
+config FSNOTIFY_BUCKET_SIZE
+       int "Dir hash bucket size"
+       default 64
+
+config FSNOTIFY_MAX_EVENTS
+       int "Max events in one notify device"
+       default 1024
+
+config FSNOTIFY_FD_POLLWAITERS
+       int "Max pollwaiters in one notify devcie"
+       default 2
+
+endif # FS_NOTIFY
diff --git a/fs/notify/Make.defs b/fs/notify/Make.defs
new file mode 100644
index 0000000000..d61c4d84ec
--- /dev/null
+++ b/fs/notify/Make.defs
@@ -0,0 +1,29 @@
+############################################################################
+# fs/notify/Make.defs
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+# Include FSNOTIFY build support
+
+
+ifeq ($(CONFIG_FS_NOTIFY),y)
+CSRCS += inotify.c
+endif
+
+DEPPATH += --dep-path notify
+VPATH += :notify
diff --git a/fs/notify/inotify.c b/fs/notify/inotify.c
new file mode 100644
index 0000000000..dbfda950ed
--- /dev/null
+++ b/fs/notify/inotify.c
@@ -0,0 +1,1356 @@
+/****************************************************************************
+ * fs/notify/inotify.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/list.h>
+#include <nuttx/mutex.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/lib/lib.h>
+
+#include <sys/inotify.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <debug.h>
+#include <poll.h>
+#include <string.h>
+#include <search.h>
+#include <libgen.h>
+
+#include "inode/inode.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+ #define ROUND_UP(x, y) (((x) + (y) - 1) / (y) * (y))
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct inotify_device_s
+{
+  mutex_t            lock;        /* Enforces device exclusive access */
+  sem_t              sem;         /* Used to wait for poll events */
+  struct list_node   events;      /* List of queued events */
+  struct list_node   watches;     /* List of watches */
+  int                count;       /* Reference count */
+  uint32_t           event_size;  /* Size of the queue (bytes) */
+  uint32_t           event_count; /* Number of pending events */
+  FAR struct pollfd *fds[CONFIG_FSNOTIFY_FD_POLLWAITERS];
+};
+
+struct inotify_event_s
+{
+  struct list_node     node;     /* Entry in inotify_device's list */
+  struct inotify_event event;    /* The user-space event */
+};
+
+struct inotify_watch_list_s
+{
+  struct list_node watches;
+  FAR char        *path;
+};
+
+struct inotify_watch_s
+{
+  struct list_node                 d_node;  /* Add to device list */
+  struct list_node                 l_node;  /* Add to node hash list */
+  int                              wd;      /* Watch descriptor */
+  uint32_t                         mask;    /* Event mask for this watch */
+  FAR struct inotify_device_s     *dev;     /* Associated device */
+  FAR struct inotify_watch_list_s *list;    /* Associated watch list */
+};
+
+/****************************************************************************
+ * Private Functions Prototypes
+ ****************************************************************************/
+
+static int     inotify_open(FAR struct file *filep);
+static int     inotify_close(FAR struct file *filep);
+static ssize_t inotify_read(FAR struct file *filep, FAR char *buffer,
+                            size_t buflen);
+static int     inotify_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                            bool setup);
+static int     inotify_ioctl(FAR struct file *filep, int cmd,
+                             unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_inotify_fops =
+{
+  inotify_open,     /* open */
+  inotify_close,    /* close */
+  inotify_read,     /* read */
+  NULL,             /* write */
+  NULL,             /* seek */
+  inotify_ioctl,    /* ioctl */
+  NULL,             /* mmap */
+  NULL,             /* truncate */
+  inotify_poll,     /* poll */
+};
+
+static struct inode g_inotify_inode =
+{
+  NULL,
+  NULL,
+  NULL,
+  1,
+  FSNODEFLAG_TYPE_DRIVER,
+  {
+    &g_inotify_fops
+  }
+};
+
+static int g_inotify_event_cookie;
+static int g_inotify_watch_cookie;
+static struct hsearch_data g_inotify_hash;
+static mutex_t g_inotify_hash_lock = NXMUTEX_INITIALIZER;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: inotify_alloc_event
+ *
+ * Description:
+ *   Initialize a kernel event.
+ *   Size of name is rounded up to sizeof(struct inotify_event).
+ ****************************************************************************/
+
+static FAR struct inotify_event_s *
+inotify_alloc_event(int wd, uint32_t mask, uint32_t cookie,
+                    FAR const char *name)
+{
+  FAR struct inotify_event_s *event;
+  size_t len = 0;
+
+  if (name != NULL)
+    {
+      len = ROUND_UP(strlen(name) + 1, sizeof(struct inotify_event));
+    }
+
+  event = kmm_malloc(sizeof(struct inotify_event_s) + len);
+  if (event == NULL)
+    {
+      return NULL;
+    }
+
+  event->event.wd     = wd;
+  event->event.mask   = mask;
+  event->event.cookie = cookie;
+  event->event.len    = len;
+
+  if (name != NULL)
+    {
+      strcpy(event->event.name, name);
+    }
+
+  return event;
+}
+
+/****************************************************************************
+ * Name: inotify_queue_event
+ *
+ * Description:
+ *   Queue an event to the inotify device.
+ *
+ ****************************************************************************/
+
+static void inotify_queue_event(FAR struct inotify_device_s *dev, int wd,
+                                uint32_t mask, uint32_t cookie,
+                                FAR const char *name)
+{
+  FAR struct inotify_event_s *event;
+  FAR struct inotify_event_s *last;
+  int semcnt;
+
+  if (!list_is_empty(&dev->events))
+    {
+      /* Drop this event if it is a dupe of the previous */
+
+      last = list_last_entry(&dev->events,
+                             struct inotify_event_s, node);
+      if (last->event.mask == mask && last->event.wd == wd &&
+          last->event.cookie == cookie &&
+          ((name == NULL && last->event.len == 0) ||
+           (name && last->event.len && !strcmp(name, last->event.name))))
+        {
+          return;
+        }
+    }
+
+  if (dev->event_count > CONFIG_FSNOTIFY_MAX_EVENTS)
+    {
+      ferr("Too many events queued\n");
+      return;
+    }
+
+  if (dev->event_count == CONFIG_FSNOTIFY_MAX_EVENTS)
+    {
+      event = inotify_alloc_event(-1, IN_Q_OVERFLOW, cookie, NULL);
+    }
+  else
+    {
+      event = inotify_alloc_event(wd, mask, cookie, name);
+    }
+
+  if (event == NULL)
+    {
+      return;
+    }
+
+  dev->event_count++;
+  dev->event_size += sizeof(struct inotify_event) + event->event.len;
+  list_add_tail(&dev->events, &event->node);
+
+  poll_notify(dev->fds, CONFIG_FSNOTIFY_FD_POLLWAITERS, POLLIN);
+
+  while (nxsem_get_value(&dev->sem, &semcnt) == 0 && semcnt <= 1)
+    {
+      nxsem_post(&dev->sem);
+    }
+}
+
+/****************************************************************************
+ * Name: inotify_remove_watch_no_event
+ *
+ * Description:
+ *   Remove a watch from the inotify device without sending an event.
+ *
+ ****************************************************************************/
+
+static void
+inotify_remove_watch_no_event(FAR struct inotify_watch_s *watch)
+{
+  FAR struct inotify_watch_list_s *list = watch->list;
+
+  list_delete(&watch->d_node);
+  list_delete(&watch->l_node);
+  kmm_free(watch);
+
+  if (list_is_empty(&list->watches))
+    {
+      ENTRY item;
+      item.key = list->path;
+      hsearch_r(item, DELETE, NULL, &g_inotify_hash);
+    }
+}
+
+/****************************************************************************
+ * Name: inotify_remove_watch
+ *
+ * Description:
+ *   Remove a watch from the inotify device.
+ *
+ ****************************************************************************/
+
+static void inotify_remove_watch(FAR struct inotify_device_s *dev,
+                                 FAR struct inotify_watch_s *watch)
+{
+  inotify_queue_event(dev, watch->wd, IN_IGNORED, 0, NULL);
+  inotify_remove_watch_no_event(watch);
+}
+
+/****************************************************************************
+ * Name: inotify_remove_event
+ *
+ * Description:
+ *   Remove a kernel event from the inotify device.
+ *
+ ****************************************************************************/
+
+static void inotify_remove_event(FAR struct inotify_device_s *dev,
+                                 FAR struct inotify_event_s *event)
+{
+  list_delete(&event->node);
+  dev->event_size -= sizeof(struct inotify_event) + event->event.len;
+  dev->event_count--;
+  kmm_free(event);
+}
+
+/****************************************************************************
+ * Name: inotify_alloc_device
+ *
+ * Description:
+ *   Allocate a new inotify device.
+ *
+ ****************************************************************************/
+
+static FAR struct inotify_device_s *inotify_alloc_device(void)
+{
+  FAR struct inotify_device_s *dev;
+
+  dev = kmm_zalloc(sizeof(struct inotify_device_s));
+  if (dev == NULL)
+    {
+      return dev;
+    }
+
+  dev->count = 1;
+  nxmutex_init(&dev->lock);
+  nxsem_init(&dev->sem, 0, 0);
+  list_initialize(&dev->events);
+  list_initialize(&dev->watches);
+  return dev;
+}
+
+/****************************************************************************
+ * Name: inotify_open
+ *
+ * Description:
+ *   Open the inotify device.
+ *
+ ****************************************************************************/
+
+static int inotify_open(FAR struct file *filep)
+{
+  FAR struct inotify_device_s *dev = filep->f_priv;
+
+  nxmutex_lock(&dev->lock);
+  dev->count++;
+  nxmutex_unlock(&dev->lock);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: inotify_poll
+ *
+ * Description:
+ *   Poll the inotify device.
+ *
+ ****************************************************************************/
+
+static int inotify_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                        bool setup)
+{
+  FAR struct inotify_device_s *dev = filep->f_priv;
+  int ret = 0;
+  int i;
+
+  nxmutex_lock(&dev->lock);
+  if (!setup)
+    {
+      FAR struct pollfd **slot = fds->priv;
+      *slot = NULL;
+      fds->priv = NULL;
+      goto out;
+    }
+
+  for (i = 0; i < CONFIG_FSNOTIFY_FD_POLLWAITERS; i++)
+    {
+      if (dev->fds[i] == 0)
+        {
+          dev->fds[i] = fds;
+          fds->priv = &dev->fds[i];
+          break;
+        }
+    }
+
+  if (i >= CONFIG_FSNOTIFY_FD_POLLWAITERS)
+    {
+      fds->priv = NULL;
+      ret = -EBUSY;
+      goto out;
+    }
+
+  if (!list_is_empty(&dev->events))
+    {
+      poll_notify(dev->fds, CONFIG_FSNOTIFY_FD_POLLWAITERS, POLLIN);
+    }
+
+out:
+  nxmutex_unlock(&dev->lock);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: inotify_ioctl
+ *
+ * Description:
+ *   Perform an inotify ioctl.
+ *
+ ****************************************************************************/
+
+static int inotify_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+  FAR struct inotify_device_s *dev = filep->f_priv;
+  int ret = -ENOTTY;
+
+  switch (cmd)
+    {
+      case FIONREAD:
+        {
+          FAR int *nbytes = (FAR int *)((uintptr_t)arg);
+          if (nbytes)
+            {
+              *nbytes = dev->event_size;
+              ret = OK;
+            }
+        }
+        break;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: inotify_read
+ *
+ * Description:
+ *   Read from the event list of inotify device.
+ *
+ ****************************************************************************/
+
+static ssize_t inotify_read(FAR struct file *filp, FAR char *buffer,
+                            size_t len)
+{
+  FAR struct inotify_device_s *dev = filp->f_priv;
+  FAR char *start = buffer;
+  int ret = 0;
+
+  if (len < sizeof(struct inotify_event) || buffer == NULL)
+    {
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&dev->lock);
+  while (len >= sizeof(struct inotify_event))
+    {
+      if (list_is_empty(&dev->events))
+        {
+          if (start != buffer || (filp->f_oflags & O_NONBLOCK) != 0)
+            {
+              break;
+            }
+
+          nxmutex_unlock(&dev->lock);
+          ret = nxsem_wait_uninterruptible(&dev->sem);
+          nxmutex_lock(&dev->lock);
+          if (ret < 0)
+            {
+              break;
+            }
+        }
+      else
+        {
+          FAR struct inotify_event_s *event =
+            list_first_entry(&dev->events, struct inotify_event_s, node);
+
+          size_t eventlen = sizeof(struct inotify_event) + event->event.len;
+          if (len < eventlen)
+            {
+              break;
+            }
+
+          memcpy(buffer, &event->event, eventlen);
+          buffer += eventlen;
+          len -= eventlen;
+          inotify_remove_event(dev, event);
+        }
+    }
+
+  nxmutex_unlock(&dev->lock);
+  if (start != buffer)
+    {
+      ret = buffer - start;
+    }
+
+  return ret == 0 ? -EAGAIN : ret;
+}
+
+/****************************************************************************
+ * Name: inotify_close
+ *
+ * Description:
+ *   Close the inotify device.
+ *
+ ****************************************************************************/
+
+static int inotify_close(FAR struct file *filep)
+{
+  FAR struct inotify_device_s *dev = filep->f_priv;
+
+  nxmutex_lock(&g_inotify_hash_lock);
+  nxmutex_lock(&dev->lock);
+  if (--dev->count > 0)
+    {
+      nxmutex_unlock(&dev->lock);
+      nxmutex_unlock(&g_inotify_hash_lock);
+      return OK;
+    }
+
+  /* Destroy all of the watches on this device  */
+
+  while (!list_is_empty(&dev->watches))
+    {
+      FAR struct inotify_watch_s *watch;
+      watch = list_first_entry(&dev->watches, struct inotify_watch_s,
+                               d_node);
+      inotify_remove_watch_no_event(watch);
+    }
+
+  /* Destroy all of the events on this device  */
+
+  while (!list_is_empty(&dev->events))
+    {
+      FAR struct inotify_event_s *event;
+      event = list_first_entry(&dev->events, struct inotify_event_s, node);
+      inotify_remove_event(dev, event);
+    }
+
+  nxmutex_unlock(&dev->lock);
+  nxmutex_unlock(&g_inotify_hash_lock);
+  nxmutex_destroy(&dev->lock);
+  nxsem_destroy(&dev->sem);
+  kmm_free(dev);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: inotify_get_device_from_fd
+ *
+ * Description:
+ *   Get the inotify device from the file descriptor.
+ *
+ ****************************************************************************/
+
+static FAR struct inotify_device_s *inotify_get_device_from_fd(int fd)
+{
+  FAR struct file *filep;
+
+  if (fs_getfilep(fd, &filep) < 0)
+    {
+      return NULL;
+    }
+
+  if (filep == NULL || filep->f_inode != &g_inotify_inode)
+    {
+      return NULL;
+    }
+
+  return filep->f_priv;
+}
+
+/****************************************************************************
+ * Name: inotify_get_watch_from_list
+ *
+ * Description:
+ *   Get the inotify watch from the file node.
+ *
+ ****************************************************************************/
+
+static FAR struct inotify_watch_s *
+inotify_get_watch_from_list(FAR struct inotify_device_s *dev,
+                            FAR struct inotify_watch_list_s *list)
+{
+  FAR struct inotify_watch_s *watch;
+
+  list_for_every_entry(&list->watches, watch, struct inotify_watch_s, l_node)
+    {
+      if (watch->dev == dev)
+        {
+          return watch;
+        }
+    }
+
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: inotify_alloc_watch
+ *
+ * Description:
+ *   Allocate a new inotify watch.
+ *
+ ****************************************************************************/
+
+static FAR struct inotify_watch_s *
+inotify_alloc_watch(FAR struct inotify_device_s *dev,
+                    FAR struct inotify_watch_list_s *list,
+                    uint32_t mask)
+{
+  FAR struct inotify_watch_s *watch;
+
+  watch = kmm_zalloc(sizeof(struct inotify_watch_s));
+  if (watch == NULL)
+    {
+      return NULL;
+    }
+
+  watch->dev = dev;
+  watch->mask = mask;
+  watch->wd = ++g_inotify_watch_cookie;
+  watch->list = list;
+  list_add_tail(&dev->watches, &watch->d_node);
+  list_add_tail(&list->watches, &watch->l_node);
+  return watch;
+}
+
+/****************************************************************************
+ * Name: inotify_find_watch
+ *
+ * Description:
+ *   Find the inotify watch from the watch descriptor.
+ *
+ ****************************************************************************/
+
+static FAR struct inotify_watch_s *
+inotify_find_watch(FAR struct inotify_device_s *dev, int wd)
+{
+  FAR struct inotify_watch_s *watch;
+
+  list_for_every_entry(&dev->watches, watch, struct inotify_watch_s, d_node)
+    {
+      if (watch->wd == wd)
+        {
+          return watch;
+        }
+    }
+
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: inotify_free_watch_list
+ *
+ * Description:
+ *   Release a reference to the inotify node.
+ *
+ ****************************************************************************/
+
+static void inotify_free_watch_list(FAR struct inotify_watch_list_s *list)
+{
+  FAR struct inotify_watch_s *watch;
+  FAR struct inotify_device_s *dev;
+  bool last_iteration = false;
+
+  do
+    {
+      if (list_is_singular(&list->watches))
+        {
+          last_iteration = true;
+        }
+
+      watch = list_first_entry(&list->watches, struct inotify_watch_s,
+                               l_node);
+      dev = watch->dev;
+      nxmutex_lock(&dev->lock);
+      inotify_remove_watch(dev, watch);
+      nxmutex_unlock(&dev->lock);
+    }
+  while (!last_iteration);
+}
+
+/****************************************************************************
+ * Name: inotify_alloc_watch_list
+ *
+ * Description:
+ *   Add the inotify node from the path.
+ *
+ ****************************************************************************/
+
+static FAR struct inotify_watch_list_s *
+inotify_alloc_watch_list(FAR const char *path)
+{
+  FAR struct inotify_watch_list_s *list;
+  FAR ENTRY *result;
+  ENTRY item;
+
+  list = kmm_zalloc(sizeof(struct inotify_watch_list_s));
+  if (list == NULL)
+    {
+      return NULL;
+    }
+
+  list_initialize(&list->watches);
+  list->path = strdup(path);
+  if (list->path == NULL)
+    {
+      kmm_free(list);
+      return NULL;
+    }
+
+  item.key = list->path;
+  item.data = list;
+  if (hsearch_r(item, ENTER, &result, &g_inotify_hash) == 0)
+    {
+      lib_free(list->path);
+      kmm_free(list);
+      return NULL;
+    }
+
+  return list;
+}
+
+/****************************************************************************
+ * Name: inotify_get_watch_list
+ *
+ * Description:
+ *   Get the watch list from the path.
+ *
+ ****************************************************************************/
+
+static FAR struct inotify_watch_list_s *
+inotify_get_watch_list(FAR const char *path)
+{
+  FAR ENTRY *result;
+  ENTRY item;
+
+  item.key = (FAR char *)path;
+  if (hsearch_r(item, FIND, &result, &g_inotify_hash) == 0)
+    {
+      return NULL;
+    }
+
+  return (FAR struct inotify_watch_list_s *)result->data;
+}
+
+/****************************************************************************
+ * Name: inotify_queue_watch_list_event
+ *
+ * Description:
+ *   Queue an event to the watch list.
+ *
+ ****************************************************************************/
+
+static void
+inotify_queue_watch_list_event(FAR struct inotify_watch_list_s *list,
+                               uint32_t mask, uint32_t cookie,
+                               FAR const char *name)
+{
+  FAR struct inotify_watch_s *watch;
+  FAR struct inotify_watch_s *w_tmp;
+
+  list_for_every_entry_safe(&list->watches, watch, w_tmp,
+                            struct inotify_watch_s, l_node)
+    {
+      uint32_t watch_mask = watch->mask;
+
+      if (watch_mask & mask)
+        {
+          FAR struct inotify_device_s *dev = watch->dev;
+          bool last_iteration = list_is_singular(&list->watches);
+
+          nxmutex_lock(&dev->lock);
+          inotify_queue_event(dev, watch->wd, mask, cookie, name);
+          if (watch_mask & IN_ONESHOT)
+            {
+              inotify_remove_watch(dev, watch);
+              if (last_iteration)
+                {
+                  nxmutex_unlock(&dev->lock);
+                  return;
+                }
+            }
+
+          nxmutex_unlock(&dev->lock);
+        }
+    }
+
+  if (mask & IN_DELETE_SELF)
+    {
+      inotify_free_watch_list(list);
+    }
+}
+
+/****************************************************************************
+ * Name: inotify_queue_parent_event
+ *
+ * Description:
+ *   Queue an event to the inotify inode.
+ *
+ ****************************************************************************/
+
+static void inotify_queue_parent_event(FAR char *path, uint32_t mask,
+                                       uint32_t cookie)
+{
+  FAR struct inotify_watch_list_s *list;
+  FAR char *name;
+
+  name = basename(path);
+  if (name == NULL)
+    {
+      return;
+    }
+
+  *(name - 1) = '\0';
+  list = inotify_get_watch_list(path);
+  if (list != NULL)
+    {
+      inotify_queue_watch_list_event(list, mask | IN_ISDIR, cookie, name);
+    }
+}
+
+/****************************************************************************
+ * Name: notify_queue_path_event
+ *
+ * Description:
+ *   Send the notification by the path.
+ *
+ ****************************************************************************/
+
+static void notify_queue_path_event(FAR const char *path, uint32_t mask)
+{
+  FAR struct inotify_watch_list_s *list;
+  FAR char *abspath;
+  uint32_t cookie = 0;
+
+  abspath = lib_realpath(path, NULL, true);
+  if (abspath == NULL)
+    {
+      return;
+    }
+
+  nxmutex_lock(&g_inotify_hash_lock);
+  if (mask & IN_MOVE)
+    {
+      if (mask & IN_MOVED_FROM)
+        {
+          ++g_inotify_event_cookie;
+        }
+
+      cookie = g_inotify_event_cookie;
+    }
+
+  list = inotify_get_watch_list(abspath);
+  inotify_queue_parent_event(abspath, mask, cookie);
+  if (list == NULL)
+    {
+      nxmutex_unlock(&g_inotify_hash_lock);
+      lib_free(abspath);
+      return;
+    }
+
+  if (mask & IN_MOVED_FROM)
+    {
+      mask ^= IN_MOVED_FROM;
+      mask |= IN_MOVE_SELF;
+    }
+
+  if (mask & IN_MOVED_TO)
+    {
+      mask ^= IN_MOVED_TO;
+    }
+
+  if (mask & IN_DELETE)
+    {
+      mask ^= IN_DELETE;
+      mask |= IN_DELETE_SELF;
+    }
+
+  if (mask != 0)
+    {
+      inotify_queue_watch_list_event(list, mask, cookie, NULL);
+    }
+
+  nxmutex_unlock(&g_inotify_hash_lock);
+  lib_free(abspath);
+}
+
+/****************************************************************************
+ * Name: notify_queue_filep_event
+ *
+ * Description:
+ *   Send the notification by the file pointer.
+ *
+ ****************************************************************************/
+
+static inline void notify_queue_filep_event(FAR struct file *filep,
+                                            uint32_t mask)
+{
+  char path[PATH_MAX];
+  int ret;
+
+  ret = file_fcntl(filep, F_GETPATH, path);
+  if (ret < 0)
+    {
+      ferr("Failed to get path\n");
+      return;
+    }
+
+  if (filep->f_oflags & O_DIRECTORY)
+    {
+      mask |= IN_ISDIR;
+    }
+
+  notify_queue_path_event(path, mask);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: inotify_add_watch
+ *
+ * Description:
+ *  Adds a new watch, or modifies an existing watch, for the file whose
+ *  location is specified in pathname; The caller must have read permission
+ *  for this file.  The fd argument is a file descriptor referring to the
+ *  inotify instance whose watch list is to be modified.  The events to be
+ *  monitored for pathname are specified in the mask bit-mask argument.
+ *
+ * Input Parameters:
+ *  fd - The file descriptor associated with an instance of inotify.
+ *  pathname - The path to the file to be monitored.
+ *  mask - The bit mask of events to be monitored.
+ *
+ * Returned Value:
+ *  On success, inotify_add_watch() returns a nonnegative watch descriptor.
+ *  On error, -1 is returned and errno is set appropriately.
+ *
+ ****************************************************************************/
+
+int inotify_add_watch(int fd, FAR const char *pathname, uint32_t mask)
+{
+  FAR struct inotify_watch_list_s *list;
+  FAR struct inotify_watch_s *watch;
+  FAR struct inotify_watch_s *old;
+  FAR struct inotify_device_s *dev;
+  FAR char *abspath;
+  struct stat buf;
+  int ret;
+
+  if ((mask & IN_ALL_EVENTS) == 0)
+    {
+      set_errno(EINVAL);
+      return ERROR;
+    }
+
+  dev = inotify_get_device_from_fd(fd);
+  if (dev == NULL)
+    {
+      set_errno(EBADF);
+      return ERROR;
+    }
+
+  abspath = lib_realpath(pathname, NULL, mask & IN_DONT_FOLLOW);
+  if (abspath == NULL)
+    {
+      return ERROR;
+    }
+
+  ret = stat(abspath, &buf);
+  if (ret < 0)
+    {
+      goto out_free;
+    }
+
+  if (!S_ISDIR(buf.st_mode) && (mask & IN_ONLYDIR))
+    {
+      ret = -ENOTDIR;
+      goto out_free;
+    }
+
+  nxmutex_lock(&g_inotify_hash_lock);
+  nxmutex_lock(&dev->lock);
+  list = inotify_get_watch_list(abspath);
+  if (list == NULL)
+    {
+      list = inotify_alloc_watch_list(abspath);
+      if (list == NULL)
+        {
+          ret = -ENOMEM;
+          goto out;
+        }
+    }
+
+  old = inotify_get_watch_from_list(dev, list);
+  if (old != NULL)
+    {
+      if (mask & IN_MASK_CREATE)
+        {
+          ret = -EEXIST;
+          goto out;
+        }
+      else if (mask & IN_MASK_ADD)
+        {
+          old->mask |= mask;
+        }
+      else
+        {
+          old->mask = mask;
+        }
+
+      ret = old->wd;
+    }
+  else
+    {
+      watch = inotify_alloc_watch(dev, list, mask);
+      if (watch == NULL && list_is_empty(&list->watches))
+        {
+          ENTRY item;
+          item.key = list->path;
+          hsearch_r(item, DELETE, NULL, &g_inotify_hash);
+          ret = -ENOMEM;
+          goto out;
+        }
+
+      ret = watch->wd;
+    }
+
+out:
+  nxmutex_unlock(&dev->lock);
+  nxmutex_unlock(&g_inotify_hash_lock);
+
+out_free:
+  lib_free(abspath);
+  if (ret < 0)
+    {
+      set_errno(-ret);
+      ret = ERROR;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: inotify_rm_watch
+ *
+ * Description:
+ *  Removes the watch associated with the watch descriptor wd from the
+ *  inotify instance associated with the file descriptor fd.
+ *
+ * Input Parameters:
+ *  fd - The file descriptor associated with an instance of inotify.
+ *  wd - The watch descriptor to be removed.
+ *
+ * Returned Value:
+ *  On success, inotify_rm_watch() returns zero.  On error, -1 is returned
+ *  and errno is set appropriately.
+ *
+ ****************************************************************************/
+
+int inotify_rm_watch(int fd, int wd)
+{
+  FAR struct inotify_device_s *dev;
+  FAR struct inotify_watch_s *watch;
+
+  dev = inotify_get_device_from_fd(fd);
+  if (dev == NULL)
+    {
+      set_errno(EBADF);
+      return ERROR;
+    }
+
+  nxmutex_lock(&g_inotify_hash_lock);
+  nxmutex_lock(&dev->lock);
+  watch = inotify_find_watch(dev, wd);
+  if (watch == NULL)
+    {
+      nxmutex_unlock(&dev->lock);
+      nxmutex_unlock(&g_inotify_hash_lock);
+      set_errno(EINVAL);
+      return ERROR;
+    }
+
+  inotify_remove_watch(dev, watch);
+  nxmutex_unlock(&dev->lock);
+  nxmutex_unlock(&g_inotify_hash_lock);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: inotify_init1
+ *
+ * Description:
+ *  Initializes a new inotify instance and returns a file descriptor
+ *  associated with a new inotify event queue.
+ *
+ * Input Parameters:
+ *  flags - The following values are recognized in flags:
+ *    IN_NONBLOCK - Set the O_NONBLOCK file status flag on the new open file
+ *      description. Using this flag saves extra calls to fcntl(2) to achieve
+ *      the same result.
+ *    IN_CLOEXEC - Set the close-on-exec (FD_CLOEXEC) flag on the new file
+ *      descriptor. See the description of the O_CLOEXEC flag in open(2) for
+ *      reasons why this may be useful.
+ *
+ * Returned Value:
+ *  On success, these system calls return a new file descriptor.
+ *  On error, -1 is returned and errno is set appropriately.
+ *
+ ****************************************************************************/
+
+int inotify_init1(int flags)
+{
+  FAR struct inotify_device_s *dev;
+  int ret;
+
+  if ((flags & ~(IN_NONBLOCK | IN_CLOEXEC)) != 0)
+    {
+      ret = -EINVAL;
+      goto exit_set_errno;
+    }
+
+  dev = inotify_alloc_device();
+  if (dev == NULL)
+    {
+      ferr("Failed to allocate inotify device\n");
+      ret = -ENOMEM;
+      goto exit_set_errno;
+    }
+
+  ret = file_allocate(&g_inotify_inode, O_RDOK | flags,
+                      0, dev, 0, true);
+  if (ret < 0)
+    {
+      ferr("Failed to allocate inotify fd\n");
+      goto exit_with_dev;
+    }
+
+  return ret;
+
+exit_with_dev:
+  nxmutex_destroy(&dev->lock);
+  nxsem_destroy(&dev->sem);
+  kmm_free(dev);
+exit_set_errno:
+  set_errno(-ret);
+  return ERROR;
+}
+
+/****************************************************************************
+ * Name: inotify_init
+ *
+ * Description:
+ *  Initializes a new inotify instance and returns a file descriptor
+ *  associated with a new inotify event queue.
+ *
+ * Returned Value:
+ *  On success, these system calls return a new file descriptor.
+ *  On error, -1 is returned and errno is set appropriately.
+ *
+ ****************************************************************************/
+
+int inotify_init(void)
+{
+  return inotify_init1(0);
+}
+
+/****************************************************************************
+ * Name: notify_initialize
+ *
+ * Description:
+ *  Initializes a new inotify root node.
+ *
+ ****************************************************************************/
+
+void notify_initialize(void)
+{
+  int ret;
+
+  ret = hcreate_r(CONFIG_FSNOTIFY_BUCKET_SIZE, &g_inotify_hash);
+  if (ret != 1)
+    {
+      ferr("Failed to create hash table\n");
+    }
+}
+
+/****************************************************************************
+ * Name: notify_open
+ *
+ * Description:
+ *   The hook is called when the file is opened.
+ *
+ ****************************************************************************/
+
+void notify_open(FAR const char *path, int oflags)
+{
+  uint32_t mask = IN_OPEN;
+
+  if (oflags & O_DIRECTORY)
+    {
+      mask |= IN_ISDIR;
+    }
+
+  if (oflags & O_CREAT)
+    {
+      mask |= IN_CREATE;
+    }
+
+  notify_queue_path_event(path, mask);
+}
+
+/****************************************************************************
+ * Name: notify_close
+ *
+ * Description:
+ *   The hook is called when the file is closed.
+ *
+ ****************************************************************************/
+
+void notify_close(FAR struct file *filep)
+{
+  if (filep->f_oflags & O_WROK)
+    {
+      notify_queue_filep_event(filep, IN_CLOSE_WRITE);
+    }
+  else
+    {
+      notify_queue_filep_event(filep, IN_CLOSE_NOWRITE);
+    }
+}
+
+/****************************************************************************
+ * Name: notify_close2
+ *
+ * Description:
+ *   The hook is called when the file is closed.
+ *
+ ****************************************************************************/
+
+void notify_close2(FAR struct inode *inode)
+{
+  char path[PATH_MAX];
+  if (inode_getpath(inode, path, PATH_MAX) >= 0)
+    {
+        notify_queue_path_event(path, IN_CLOSE_WRITE);
+    }
+}
+
+/****************************************************************************
+ * Name: notify_read
+ *
+ * Description:
+ *   The hook is called when the file is read.
+ *
+ ****************************************************************************/
+
+void notify_read(FAR struct file *filep)
+{
+  notify_queue_filep_event(filep, IN_ACCESS);
+}
+
+/****************************************************************************
+ * Name: notify_write
+ *
+ * Description:
+ *   The hook is called when the file is written.
+ *
+ ****************************************************************************/
+
+void notify_write(FAR struct file *filep)
+{
+  notify_queue_filep_event(filep, IN_MODIFY);
+}
+
+/****************************************************************************
+ * Name: notify_chstat
+ *
+ * Description:
+ *   The hook is called when the file attribute is changed.
+ *
+ ****************************************************************************/
+
+void notify_chstat(FAR struct file *filep)
+{
+  notify_queue_filep_event(filep, IN_ATTRIB);
+}
+
+/****************************************************************************
+ * Name: notify_unlink
+ *
+ * Description:
+ *   The hook is called when the file is unlinked.
+ *
+ ****************************************************************************/
+
+void notify_unlink(FAR const char *path)
+{
+  notify_queue_path_event(path, IN_DELETE);
+}
+
+/****************************************************************************
+ * Name: notify_unmount
+ *
+ * Description:
+ *   The hook is called when the file system is unmounted.
+ *
+ ****************************************************************************/
+
+void notify_unmount(FAR const char *path)
+{
+  notify_queue_path_event(path, IN_DELETE | IN_UNMOUNT);
+}
+
+/****************************************************************************
+ * Name: notify_mkdir
+ *
+ * Description:
+ *   The hook is called when the directory is created.
+ *
+ ****************************************************************************/
+
+void notify_mkdir(FAR const char *path)
+{
+  notify_queue_path_event(path, IN_CREATE | IN_ISDIR);
+}
+
+/****************************************************************************
+ * Name: notify_create
+ *
+ * Description:
+ *   The hook is called symlink is created.
+ *
+ ****************************************************************************/
+
+void notify_create(FAR const char *path)
+{
+  notify_queue_path_event(path, IN_CREATE);
+}
+
+/****************************************************************************
+ * Name: notify_rename
+ *
+ * Description:
+ *   The hook is called when the file is moved.
+ *
+ ****************************************************************************/
+
+void notify_rename(FAR const char *oldpath, bool oldisdir,
+                   FAR const char *newpath, bool newisdir)
+{
+  uint32_t newmask = IN_MOVED_TO;
+  uint32_t oldmask = IN_MOVED_FROM;
+
+  if (newisdir)
+    {
+      newmask |= IN_ISDIR;
+    }
+
+  if (oldisdir)
+    {
+      oldmask |= IN_ISDIR;
+    }
+
+  notify_queue_path_event(oldpath, oldmask);
+  notify_queue_path_event(newpath, newmask);
+}
diff --git a/fs/driver/fs_unregisterblockdriver.c b/fs/notify/notify.h
similarity index 59%
copy from fs/driver/fs_unregisterblockdriver.c
copy to fs/notify/notify.h
index be697e1f96..4a0fc11b02 100644
--- a/fs/driver/fs_unregisterblockdriver.c
+++ b/fs/notify/notify.h
@@ -1,5 +1,5 @@
 /****************************************************************************
- * fs/driver/fs_unregisterblockdriver.c
+ * fs/notify/notify.h
  *
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -18,38 +18,41 @@
  *
  ****************************************************************************/
 
+#ifndef __FS_NOTIFY_NOTIFY_H
+#define __FS_NOTIFY_NOTIFY_H
+
 /****************************************************************************
  * Included Files
  ****************************************************************************/
 
-#include <nuttx/config.h>
-
 #include <nuttx/fs/fs.h>
 
-#include "inode/inode.h"
-
 /****************************************************************************
- * Public Functions
+ * Pre-processor Definitions
  ****************************************************************************/
 
 /****************************************************************************
- * Name: unregister_blockdriver
- *
- * Description:
- *   Remove the block driver inode at 'path' from the pseudo-file system
- *
+ * Public Type Definitions
  ****************************************************************************/
 
-int unregister_blockdriver(FAR const char *path)
-{
-  int ret;
-
-  ret = inode_lock();
-  if (ret >= 0)
-    {
-      ret = inode_remove(path);
-      inode_unlock();
-    }
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
 
-  return ret;
-}
+/* These are internal OS interface and are not available to applications */
+
+void notify_open(FAR const char *path, int oflags);
+void notify_close(FAR struct file *filep);
+void notify_close2(FAR struct inode *inode);
+void notify_read(FAR struct file *filep);
+void notify_write(FAR struct file *filep);
+void notify_chstat(FAR struct file *filep);
+void notify_unlink(FAR const char *path);
+void notify_unmount(FAR const char *path);
+void notify_mkdir(FAR const char *path);
+void notify_create(FAR const char *path);
+void notify_rename(FAR const char *oldpath, bool oldisdir,
+                   FAR const char *newpath, bool newisdir);
+void notify_initialize(void);
+
+#endif
diff --git a/fs/semaphore/sem_close.c b/fs/semaphore/sem_close.c
index 90bc1f34b5..abfde41aad 100644
--- a/fs/semaphore/sem_close.c
+++ b/fs/semaphore/sem_close.c
@@ -33,6 +33,7 @@
 #include <nuttx/fs/fs.h>
 
 #include "inode/inode.h"
+#include "notify/notify.h"
 
 #ifdef CONFIG_FS_NAMED_SEMAPHORES
 
@@ -116,7 +117,9 @@ int nxsem_close(FAR sem_t *sem)
        */
 
       inode_unlock();
-
+#ifdef CONFIG_FS_NOTIFY
+      notify_close2(inode);
+#endif
       DEBUGASSERT(inode->i_peer == NULL);
       inode_free(inode);
       return OK;
diff --git a/fs/semaphore/sem_open.c b/fs/semaphore/sem_open.c
index e21e237c1a..028b5e6817 100644
--- a/fs/semaphore/sem_open.c
+++ b/fs/semaphore/sem_open.c
@@ -40,6 +40,7 @@
 #include <nuttx/fs/fs.h>
 
 #include "inode/inode.h"
+#include "notify/notify.h"
 #include "semaphore/semaphore.h"
 
 #ifdef CONFIG_FS_NAMED_SEMAPHORES
@@ -222,6 +223,9 @@ int nxsem_open(FAR sem_t **sem, FAR const char *name, int 
oflags, ...)
     }
 
   RELEASE_SEARCH(&desc);
+#ifdef CONFIG_FS_NOTIFY
+  notify_open(fullpath, oflags);
+#endif
   return OK;
 
 errout_with_inode:
diff --git a/fs/semaphore/sem_unlink.c b/fs/semaphore/sem_unlink.c
index b90fef5268..5b2e5b3216 100644
--- a/fs/semaphore/sem_unlink.c
+++ b/fs/semaphore/sem_unlink.c
@@ -34,6 +34,7 @@
 #include <nuttx/semaphore.h>
 
 #include "inode/inode.h"
+#include "notify/notify.h"
 #include "semaphore/semaphore.h"
 
 /****************************************************************************
@@ -138,6 +139,9 @@ int nxsem_unlink(FAR const char *name)
   inode_unlock();
   ret = nxsem_close(&inode->u.i_nsem->ns_sem);
   RELEASE_SEARCH(&desc);
+#ifdef CONFIG_FS_NOTIFY
+  notify_unlink(fullpath);
+#endif
   return ret;
 
 errout_with_lock:
diff --git a/fs/shm/shm_open.c b/fs/shm/shm_open.c
index 8f75d92fd5..59a96a82ee 100644
--- a/fs/shm/shm_open.c
+++ b/fs/shm/shm_open.c
@@ -29,6 +29,7 @@
 #include <errno.h>
 
 #include "inode/inode.h"
+#include "notify/notify.h"
 #include "shm/shmfs.h"
 
 /****************************************************************************
@@ -149,6 +150,13 @@ errout_with_sem:
   inode_unlock();
 errout_with_search:
   RELEASE_SEARCH(&desc);
+#ifdef CONFIG_FS_NOTIFY
+  if (ret >= 0)
+    {
+      notify_open(fullpath, oflags);
+    }
+#endif
+
   return ret;
 }
 
diff --git a/fs/shm/shm_unlink.c b/fs/shm/shm_unlink.c
index 7fda33e9e6..20f64171c7 100644
--- a/fs/shm/shm_unlink.c
+++ b/fs/shm/shm_unlink.c
@@ -27,6 +27,7 @@
 #include <errno.h>
 
 #include "inode/inode.h"
+#include "notify/notify.h"
 
 /****************************************************************************
  * Private Functions
@@ -140,6 +141,13 @@ errout_with_sem:
   inode_unlock();
 errout_with_search:
   RELEASE_SEARCH(&desc);
+#ifdef CONFIG_FS_NOTIFY
+  if (ret >= 0)
+    {
+      notify_unlink(fullpath);
+    }
+#endif
+
   return ret;
 }
 
diff --git a/fs/vfs/fs_close.c b/fs/vfs/fs_close.c
index a5197e49f7..52924030df 100644
--- a/fs/vfs/fs_close.c
+++ b/fs/vfs/fs_close.c
@@ -32,6 +32,7 @@
 
 #include <nuttx/fs/fs.h>
 
+#include "notify/notify.h"
 #include "inode/inode.h"
 #include "vfs/lock.h"
 
diff --git a/fs/vfs/fs_fchstat.c b/fs/vfs/fs_fchstat.c
index 87503d0879..ddcf2af011 100644
--- a/fs/vfs/fs_fchstat.c
+++ b/fs/vfs/fs_fchstat.c
@@ -31,6 +31,7 @@
 
 #include <nuttx/fs/fs.h>
 
+#include "notify/notify.h"
 #include "inode/inode.h"
 
 /****************************************************************************
@@ -191,6 +192,13 @@ int file_fchstat(FAR struct file *filep, FAR struct stat 
*buf, int flags)
       ret = inode_chstat(inode, buf, flags, 0);
     }
 
+#ifdef CONFIG_FS_NOTIFY
+  if (ret >= 0)
+    {
+      notify_chstat(filep);
+    }
+#endif
+
   return ret;
 }
 
diff --git a/fs/vfs/fs_mkdir.c b/fs/vfs/fs_mkdir.c
index fbe71e89bc..a5464bdb9b 100644
--- a/fs/vfs/fs_mkdir.c
+++ b/fs/vfs/fs_mkdir.c
@@ -32,6 +32,7 @@
 
 #include <nuttx/fs/fs.h>
 
+#include "notify/notify.h"
 #include "inode/inode.h"
 
 /****************************************************************************
@@ -165,6 +166,9 @@ int mkdir(const char *pathname, mode_t mode)
   /* Directory successfully created */
 
   RELEASE_SEARCH(&desc);
+#ifdef CONFIG_FS_NOTIFY
+  notify_mkdir(pathname);
+#endif
   return OK;
 
 errout_with_inode:
diff --git a/fs/vfs/fs_open.c b/fs/vfs/fs_open.c
index 2913f0cf24..6b0b90564b 100644
--- a/fs/vfs/fs_open.c
+++ b/fs/vfs/fs_open.c
@@ -38,6 +38,7 @@
 
 #include "inode/inode.h"
 #include "driver/driver.h"
+#include "notify/notify.h"
 
 /****************************************************************************
  * Private Functions
@@ -196,7 +197,15 @@ static int file_vopen(FAR struct file *filep, FAR const 
char *path,
 
       /* Get the file structure of the opened character driver proxy */
 
-      return block_proxy(filep, path, oflags);
+      ret = block_proxy(filep, path, oflags);
+#ifdef CONFIG_FS_NOTIFY
+      if (ret >= 0)
+        {
+          notify_open(path, filep->f_oflags);
+        }
+#endif
+
+      return ret;
     }
 #endif
 
@@ -255,6 +264,9 @@ static int file_vopen(FAR struct file *filep, FAR const 
char *path,
     }
 
   RELEASE_SEARCH(&desc);
+#ifdef CONFIG_FS_NOTIFY
+  notify_open(path, filep->f_oflags);
+#endif
   return OK;
 
 errout_with_inode:
diff --git a/fs/vfs/fs_pseudofile.c b/fs/vfs/fs_pseudofile.c
index f39f2e5162..a45d2875a8 100644
--- a/fs/vfs/fs_pseudofile.c
+++ b/fs/vfs/fs_pseudofile.c
@@ -37,6 +37,7 @@
 #include <nuttx/lib/math32.h>
 
 #include "inode/inode.h"
+#include "notify/notify.h"
 
 /****************************************************************************
  * Private Types
@@ -505,6 +506,9 @@ int pseudofile_create(FAR struct inode **node, FAR const 
char *path,
   (*node)->i_private = pf;
 
   inode_unlock();
+#ifdef CONFIG_FS_NOTIFY
+  notify_create(path);
+#endif
   return 0;
 
 reserve_err:
diff --git a/fs/vfs/fs_read.c b/fs/vfs/fs_read.c
index ba5316d31a..0218158d67 100644
--- a/fs/vfs/fs_read.c
+++ b/fs/vfs/fs_read.c
@@ -33,6 +33,7 @@
 
 #include <nuttx/cancelpt.h>
 
+#include "notify/notify.h"
 #include "inode/inode.h"
 
 /****************************************************************************
@@ -96,6 +97,13 @@ ssize_t file_read(FAR struct file *filep, FAR void *buf, 
size_t nbytes)
 
   /* Return the number of bytes read (or possibly an error code) */
 
+#ifdef CONFIG_FS_NOTIFY
+  if (ret > 0)
+    {
+      notify_read(filep);
+    }
+#endif
+
   return ret;
 }
 
diff --git a/fs/vfs/fs_rename.c b/fs/vfs/fs_rename.c
index 913fddfd06..ddbcfa2076 100644
--- a/fs/vfs/fs_rename.c
+++ b/fs/vfs/fs_rename.c
@@ -35,6 +35,7 @@
 #include <nuttx/fs/fs.h>
 #include <nuttx/lib/lib.h>
 
+#include "notify/notify.h"
 #include "inode/inode.h"
 
 /****************************************************************************
@@ -67,6 +68,9 @@ static int pseudorename(FAR const char *oldpath, FAR struct 
inode *oldinode,
   struct inode_search_s newdesc;
   FAR struct inode *newinode;
   FAR char *subdir = NULL;
+#ifdef CONFIG_FS_NOTIFY
+  bool isdir = INODE_IS_PSEUDODIR(oldinode);
+#endif
   int ret;
 
   /* According to POSIX, any new inode at this path should be removed
@@ -160,6 +164,9 @@ next_subdir:
            */
 
           inode_remove(newpath);
+#ifdef CONFIG_FS_NOTIFY
+          notify_unlink(newpath);
+#endif
         }
 
       inode_release(newinode);
@@ -244,6 +251,13 @@ errout_with_lock:
 
 errout:
   RELEASE_SEARCH(&newdesc);
+#ifdef CONFIG_FS_NOTIFY
+  if (ret >= 0)
+    {
+      notify_rename(oldpath, isdir, newpath, isdir);
+    }
+#endif
+
   if (subdir != NULL)
     {
       lib_free(subdir);
@@ -270,6 +284,10 @@ static int mountptrename(FAR const char *oldpath, FAR 
struct inode *oldinode,
   FAR struct inode *newinode;
   FAR const char *newrelpath;
   FAR char *subdir = NULL;
+#ifdef CONFIG_FS_NOTIFY
+  bool newisdir = false;
+  bool oldisdir = false;
+#endif
   int ret;
 
   DEBUGASSERT(oldinode->u.i_mops);
@@ -338,13 +356,26 @@ static int mountptrename(FAR const char *oldpath, FAR 
struct inode *oldinode,
     {
       struct stat buf;
 
+#ifdef CONFIG_FS_NOTIFY
+      ret = oldinode->u.i_mops->stat(oldinode, oldpath, &buf);
+      if (ret >= 0)
+        {
+          oldisdir = S_ISDIR(buf.st_mode);
+        }
+#endif
+
 next_subdir:
       ret = oldinode->u.i_mops->stat(oldinode, newrelpath, &buf);
       if (ret >= 0)
         {
           /* Is the directory entry a directory? */
 
+#ifdef CONFIG_FS_NOTIFY
+          newisdir = S_ISDIR(buf.st_mode);
+          if (newisdir)
+#else
           if (S_ISDIR(buf.st_mode))
+#endif
             {
               FAR char *subdirname;
 
@@ -417,6 +448,9 @@ next_subdir:
                    */
 
                    oldinode->u.i_mops->unlink(oldinode, newrelpath);
+#ifdef CONFIG_FS_NOTIFY
+                   notify_unlink(newrelpath);
+#endif
                 }
             }
         }
@@ -438,6 +472,13 @@ errout_with_newsearch:
       lib_free(subdir);
     }
 
+#ifdef CONFIG_FS_NOTIFY
+  if (ret >= 0)
+    {
+      notify_rename(oldpath, oldisdir, newpath, newisdir);
+    }
+#endif
+
   return ret;
 }
 #endif /* CONFIG_DISABLE_MOUNTPOINT */
diff --git a/fs/vfs/fs_rmdir.c b/fs/vfs/fs_rmdir.c
index e4249a4e8a..d144d6c624 100644
--- a/fs/vfs/fs_rmdir.c
+++ b/fs/vfs/fs_rmdir.c
@@ -31,6 +31,7 @@
 
 #include <nuttx/fs/fs.h>
 
+#include "notify/notify.h"
 #include "inode/inode.h"
 
 /****************************************************************************
@@ -165,6 +166,9 @@ int rmdir(FAR const char *pathname)
 
   inode_release(inode);
   RELEASE_SEARCH(&desc);
+#ifdef CONFIG_FS_NOTIFY
+  notify_unlink(pathname);
+#endif
   return OK;
 
 errout_with_inode:
diff --git a/fs/vfs/fs_symlink.c b/fs/vfs/fs_symlink.c
index 0ad492488d..9c6de4f994 100644
--- a/fs/vfs/fs_symlink.c
+++ b/fs/vfs/fs_symlink.c
@@ -35,6 +35,7 @@
 #include <nuttx/lib/lib.h>
 #include <nuttx/fs/fs.h>
 
+#include "notify/notify.h"
 #include "inode/inode.h"
 
 /****************************************************************************
@@ -167,6 +168,9 @@ int symlink(FAR const char *path1, FAR const char *path2)
   /* Symbolic link successfully created */
 
   RELEASE_SEARCH(&desc);
+#ifdef CONFIG_FS_NOTIFY
+  notify_create(path2);
+#endif
   return OK;
 
 errout_with_inode:
diff --git a/fs/vfs/fs_unlink.c b/fs/vfs/fs_unlink.c
index 4844803afc..b68b4da60d 100644
--- a/fs/vfs/fs_unlink.c
+++ b/fs/vfs/fs_unlink.c
@@ -31,6 +31,7 @@
 
 #include <nuttx/fs/fs.h>
 
+#include "notify/notify.h"
 #include "inode/inode.h"
 
 /****************************************************************************
@@ -190,6 +191,9 @@ int nx_unlink(FAR const char *pathname)
 
   inode_release(inode);
   RELEASE_SEARCH(&desc);
+#ifdef CONFIG_FS_NOTIFY
+  notify_unlink(pathname);
+#endif
   return OK;
 
 #if !defined(CONFIG_DISABLE_MOUNTPOINT) || 
!defined(CONFIG_DISABLE_PSEUDOFS_OPERATIONS)
diff --git a/fs/vfs/fs_write.c b/fs/vfs/fs_write.c
index 3d1164d9e8..df3f203b1c 100644
--- a/fs/vfs/fs_write.c
+++ b/fs/vfs/fs_write.c
@@ -33,6 +33,7 @@
 
 #include <nuttx/cancelpt.h>
 
+#include "notify/notify.h"
 #include "inode/inode.h"
 
 /****************************************************************************
@@ -68,6 +69,7 @@ ssize_t file_write(FAR struct file *filep, FAR const void 
*buf,
                    size_t nbytes)
 {
   FAR struct inode *inode;
+  ssize_t ret;
 
   /* Was this file opened for write access? */
 
@@ -86,7 +88,15 @@ ssize_t file_write(FAR struct file *filep, FAR const void 
*buf,
 
   /* Yes, then let the driver perform the write */
 
-  return inode->u.i_ops->write(filep, buf, nbytes);
+  ret = inode->u.i_ops->write(filep, buf, nbytes);
+#ifdef CONFIG_FS_NOTIFY
+  if (ret > 0)
+    {
+      notify_write(filep);
+    }
+#endif
+
+  return ret;
 }
 
 /****************************************************************************
diff --git a/include/sys/inotify.h b/include/sys/inotify.h
new file mode 100644
index 0000000000..2fb1edcefe
--- /dev/null
+++ b/include/sys/inotify.h
@@ -0,0 +1,169 @@
+/****************************************************************************
+ * include/sys/inotify.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_SYS_INOTIFY_H
+#define __INCLUDE_SYS_INOTIFY_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <fcntl.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define IN_ACCESS        0x00000001      /* File was accessed */
+#define IN_MODIFY        0x00000002      /* File was modified */
+#define IN_ATTRIB        0x00000004      /* Metadata changed */
+#define IN_CLOSE_WRITE   0x00000008      /* Writtable file was closed */
+#define IN_CLOSE_NOWRITE 0x00000010      /* Unwrittable file closed */
+#define IN_OPEN          0x00000020      /* File was opened */
+#define IN_MOVED_FROM    0x00000040      /* File was moved from X */
+#define IN_MOVED_TO      0x00000080      /* File was moved to Y */
+#define IN_CREATE        0x00000100      /* Subfile was created */
+#define IN_DELETE        0x00000200      /* Subfile was deleted */
+#define IN_DELETE_SELF   0x00000400      /* Self was deleted */
+#define IN_MOVE_SELF     0x00000800      /* Self was moved */
+
+#define IN_UNMOUNT       0x00002000      /* Backing fs was unmounted */
+#define IN_Q_OVERFLOW    0x00004000      /* Event queued overflowed */
+#define IN_IGNORED       0x00008000      /* File was ignored */
+
+#define IN_ONLYDIR       0x01000000      /* Only watch the path if it is a 
directory.  */
+#define IN_DONT_FOLLOW   0x02000000      /* Do not follow a sym link.  */
+#define IN_EXCL_UNLINK   0x04000000      /* Exclude events on unlinked 
objects.  */
+#define IN_MASK_CREATE   0x10000000      /* Only create watches.  */
+
+#define IN_MASK_ADD      0x20000000      /* Add to the mask of an already 
existing watch */
+#define IN_ISDIR         0x40000000      /* Event occurred against dir */
+#define IN_ONESHOT       0x80000000      /* Only send event once */
+
+#define IN_CLOSE        (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)  /* Close */
+#define IN_MOVE         (IN_MOVED_FROM | IN_MOVED_TO)        /* Moves */
+
+#define IN_ALL_EVENTS   (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \
+                         IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | 
IN_MOVED_TO | \
+                         IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF)
+
+#define IN_CLOEXEC       O_CLOEXEC   /* Set close_on_exec for the inotify file 
descriptor */
+#define IN_NONBLOCK      O_NONBLOCK  /* Set O_NONBLOCK for the inotify file 
descriptor */
+
+/****************************************************************************
+ * Type Definitions
+ ****************************************************************************/
+
+struct inotify_event
+{
+  int      wd;              /* Watch descriptor */
+  uint32_t mask;            /* Mask describing event */
+  uint32_t cookie;          /* Unique cookie associating related events (for 
rename(2)) */
+  uint32_t len;             /* Size of name field */
+  char     name[0];         /* Stub for possible name */
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: inotify_init
+ *
+ * Description:
+ *  Initializes a new inotify instance and returns a file descriptor
+ *  associated with a new inotify event queue.
+ *
+ * Returned Value:
+ *  On success, these system calls return a new file descriptor.
+ *  On error, -1 is returned and errno is set appropriately.
+ *
+ ****************************************************************************/
+
+int inotify_init(void);
+
+/****************************************************************************
+ * Name: inotify_init1
+ *
+ * Description:
+ *  Initializes a new inotify instance and returns a file descriptor
+ *  associated with a new inotify event queue.
+ *
+ * Input Parameters:
+ *  flags - The following values are recognized in flags:
+ *    IN_NONBLOCK - Set the O_NONBLOCK file status flag on the new open file
+ *      description. Using this flag saves extra calls to fcntl(2) to achieve
+ *      the same result.
+ *    IN_CLOEXEC - Set the close-on-exec (FD_CLOEXEC) flag on the new file
+ *      descriptor. See the description of the O_CLOEXEC flag in open(2) for
+ *      reasons why this may be useful.
+ *
+ * Returned Value:
+ *  On success, these system calls return a new file descriptor.
+ *  On error, -1 is returned and errno is set appropriately.
+ *
+ ****************************************************************************/
+
+int inotify_init1(int flags);
+
+/****************************************************************************
+ * Name: inotify_add_watch
+ *
+ * Description:
+ *  Adds a new watch, or modifies an existing watch, for the file whose
+ *  location is specified in pathname; the caller must have read permission
+ *  for this file.  The fd argument is a file descriptor referring to the
+ *  inotify instance whose watch list is to be modified.  The events to be
+ *  monitored for pathname are specified in the mask bit-mask argument.
+ *
+ * Input Parameters:
+ *  fd - The file descriptor associated with an instance of inotify.
+ *  pathname - The path to the file to be monitored.
+ *  mask - The bit mask of events to be monitored.
+ *
+ * Returned Value:
+ *  On success, inotify_add_watch() returns a nonnegative watch descriptor.
+ *  On error, -1 is returned and errno is set appropriately.
+ *
+ ****************************************************************************/
+
+int inotify_add_watch(int fd, FAR const char *pathname, uint32_t mask);
+
+/****************************************************************************
+ * Name: inotify_rm_watch
+ *
+ * Description:
+ *  Removes the watch associated with the watch descriptor wd from the
+ *  inotify instance associated with the file descriptor fd.
+ *
+ * Input Parameters:
+ *  fd - The file descriptor associated with an instance of inotify.
+ *  wd - The watch descriptor to be removed.
+ *
+ * Returned Value:
+ *  On success, inotify_rm_watch() returns zero.  On error, -1 is returned
+ *  and errno is set appropriately.
+ *
+ ****************************************************************************/
+
+int inotify_rm_watch(int fd, int wd);
+
+#endif
diff --git a/include/sys/syscall_lookup.h b/include/sys/syscall_lookup.h
index ee5781030e..6da57cff18 100644
--- a/include/sys/syscall_lookup.h
+++ b/include/sys/syscall_lookup.h
@@ -286,6 +286,15 @@ SYSCALL_LOOKUP(munmap,                     2)
   SYSCALL_LOOKUP(shm_unlink,               1)
 #endif
 
+/* The following are defined if the file system notify is enabled */
+
+#ifdef CONFIG_FS_NOTIFY
+  SYSCALL_LOOKUP(inotify_add_watch,        3)
+  SYSCALL_LOOKUP(inotify_init,             0)
+  SYSCALL_LOOKUP(inotify_init1,            1)
+  SYSCALL_LOOKUP(inotify_rm_watch,         2)
+#endif
+
 /* The following are defined if pthreads are enabled */
 
 #ifndef CONFIG_DISABLE_PTHREAD
diff --git a/syscall/syscall.csv b/syscall/syscall.csv
index 6b23fcd604..e2a36223eb 100644
--- a/syscall/syscall.csv
+++ b/syscall/syscall.csv
@@ -50,6 +50,10 @@
 "gettid","unistd.h","","pid_t"
 "gettimeofday","sys/time.h","","int","FAR struct timeval *","FAR struct 
timezone *"
 "getuid","unistd.h","defined(CONFIG_SCHED_USER_IDENTITY)","uid_t"
+"inotify_add_watch","sys/inotify.h","defined(CONFIG_FS_NOTIFY)","int","int","FAR
 const char *","uint32_t"
+"inotify_init","sys/inotify.h","defined(CONFIG_FS_NOTIFY)","int"
+"inotify_init1","sys/inotify.h","defined(CONFIG_FS_NOTIFY)","int","int"
+"inotify_rm_watch","sys/inotify.h","defined(CONFIG_FS_NOTIFY)","int","int","int"
 "insmod","nuttx/module.h","defined(CONFIG_MODULE)","FAR void *","FAR const 
char *","FAR const char *"
 "ioctl","sys/ioctl.h","","int","int","int","...","unsigned long"
 "kill","signal.h","","int","pid_t","int"

Reply via email to