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

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


The following commit(s) were added to refs/heads/master by this push:
     new c2b787bea system/nxinit: Add the mount and umount commands.
c2b787bea is described below

commit c2b787beabf1395c176376de2ef86748b7a01bb6
Author: Marco Casaroli <[email protected]>
AuthorDate: Wed Oct 21 00:43:45 2026 +0200

    system/nxinit: Add the mount and umount commands.
    
    The NXInit documentation lists mount and umount as built-in commands,
    but NXInit did not have them: a "mount" line ran the NSH command
    through "sh -c", which needs NSH and has the NSH syntax.
    
    Add them.  "mount" takes the syntax of the Android init language and
    the syntax of the NSH command:
    
      mount <type> <device> <dir> [<flag>]* [<options>]
      mount -t <type> [-o <options>] [<device>] <dir>
      umount <dir>
    
    The flags are defaults, rw, ro, nosuid, nodev, noexec, sync, remount,
    dirsync and noatime.  In both forms, <options> is a comma-separated
    list of flags and file system options, as in NSH.  For a file system
    without a device (tmpfs, procfs), <device> is any name that is not a
    device, for example "none".  The lines "mount -t tmpfs /tmp" of the
    init.rc of the sim board do not need NSH now.  As for the other
    commands, a failure sets the result that "&&" and "||" test.
    
    A "mount" line in neither form fails, so NXInit does not need NSH for
    "mount".  With CONFIG_DISABLE_MOUNTPOINT there are no mount() and
    umount2() calls, and NXInit has no mount and umount commands.  Add a
    unit test case.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 system/nxinit/builtin.c                 | 195 ++++++++++++++++++++++++++++++++
 system/nxinit/test/test_nxinit.c        |   1 +
 system/nxinit/test/test_nxinit.h        |   1 +
 system/nxinit/test/test_nxinit_action.c | 117 +++++++++++++++++++
 4 files changed, 314 insertions(+)

diff --git a/system/nxinit/builtin.c b/system/nxinit/builtin.c
index a0ae5382a..a0a40a05d 100644
--- a/system/nxinit/builtin.c
+++ b/system/nxinit/builtin.c
@@ -33,6 +33,7 @@
 #include <unistd.h>
 #include <sys/boardctl.h>
 #include <sys/ioctl.h>
+#include <sys/mount.h>
 #include <sys/param.h>
 #include <sys/wait.h>
 
@@ -59,6 +60,14 @@ struct cmd_map_s
   CODE int (*func)(FAR struct action_manager_s *, int, FAR char **);
 };
 
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+struct mount_flag_s
+{
+  FAR const char *name;
+  unsigned long flag;
+};
+#endif
+
 /****************************************************************************
  * Private Function Prototypes
  ****************************************************************************/
@@ -108,11 +117,35 @@ static int cmd_rptun(FAR struct action_manager_s *am,
 #endif
 static int cmd_unlink(FAR struct action_manager_s *am,
                       int argc, FAR char **argv);
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+static int cmd_mount(FAR struct action_manager_s *am,
+                     int argc, FAR char **argv);
+static int cmd_umount(FAR struct action_manager_s *am,
+                      int argc, FAR char **argv);
+#endif
 
 /****************************************************************************
  * Private Data
  ****************************************************************************/
 
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+/* The flags of the "mount" command, as in the Android init language */
+
+static const struct mount_flag_s g_mount_flags[] =
+{
+  {"defaults", 0},
+  {"rw", 0},
+  {"ro", MS_RDONLY},
+  {"nosuid", MS_NOSUID},
+  {"nodev", MS_NODEV},
+  {"noexec", MS_NOEXEC},
+  {"sync", MS_SYNCHRONOUS},
+  {"remount", MS_REMOUNT},
+  {"dirsync", MS_DIRSYNC},
+  {"noatime", MS_NOATIME},
+};
+#endif
+
 static const struct cmd_map_s g_builtin[] =
 {
 #ifdef CONFIG_BOARDCTL_FINALINIT
@@ -125,6 +158,9 @@ static const struct cmd_map_s g_builtin[] =
   {"class_stop", 2, 2, cmd_class_stop},
   {"exec", 3, 99, cmd_exec},
   {"exec_start", 2, 2, cmd_exec_start},
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+  {"mount", 4, CONFIG_SYSTEM_NXINIT_ACTION_CMD_ARGS_MAX, cmd_mount},
+#endif
 #ifdef CONFIG_NETUTILS_NETINIT
   {"netinit", 1, 1, cmd_netinit},
 #endif
@@ -143,6 +179,9 @@ static const struct cmd_map_s g_builtin[] =
 #endif
   {"stop", 2, 2, cmd_stop},
   {"trigger", 2, 2, cmd_trigger},
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+  {"umount", 2, 2, cmd_umount},
+#endif
   {"unlink", 2, 2, cmd_unlink},
 };
 
@@ -359,6 +398,162 @@ static int cmd_unlink(FAR struct action_manager_s *am,
   return ret;
 }
 
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+/* Copy the comma-separated list in options to data without the flags
+ * (g_mount_flags), and return the flags.  options does not change, as the
+ * command can run again.  data has CONFIG_SYSTEM_NXINIT_RC_LINE_MAX bytes,
+ * and an argument is part of one line.
+ */
+
+static unsigned long mount_parse_options(FAR const char *options,
+                                         FAR char *data)
+{
+  FAR const char *end;
+  FAR char *dst = data;
+  unsigned long flags = 0;
+  size_t len;
+  size_t j;
+
+  while (*options != '\0')
+    {
+      end = strchrnul(options, ',');
+      len = end - options;
+
+      for (j = 0; j < nitems(g_mount_flags); j++)
+        {
+          if (!strncmp(g_mount_flags[j].name, options, len) &&
+              g_mount_flags[j].name[len] == '\0')
+            {
+              flags |= g_mount_flags[j].flag;
+              break;
+            }
+        }
+
+      if (j == nitems(g_mount_flags) && len > 0)
+        {
+          if (dst != data)
+            {
+              *dst++ = ',';
+            }
+
+          memcpy(dst, options, len);
+          dst += len;
+        }
+
+      options = *end == ',' ? end + 1 : end;
+    }
+
+  *dst = '\0';
+  return flags;
+}
+
+/* mount <type> <device> <dir> [<flag>]* [<options>]
+ * mount -t <type> [-o <options>] [<device>] <dir>
+ *
+ * <flag> is one of g_mount_flags.  <options> is a comma-separated list of
+ * flags and file system options (the data of mount()), as in the NSH
+ * command.  The second form is the one of the NSH command.
+ */
+
+static int cmd_mount(FAR struct action_manager_s *am,
+                     int argc, FAR char **argv)
+{
+  char data[CONFIG_SYSTEM_NXINIT_RC_LINE_MAX];
+  FAR const char *options = NULL;
+  FAR const char *source = NULL;
+  FAR const char *target;
+  FAR const char *type = NULL;
+  unsigned long flags = 0;
+  int ret;
+  int i;
+
+  UNUSED(am);
+
+  data[0] = '\0';
+  if (argc > 1 && argv[1][0] == '-')
+    {
+      for (i = 1; i < argc - 1 && argv[i][0] == '-'; i += 2)
+        {
+          if (!strcmp(argv[i], "-t"))
+            {
+              type = argv[i + 1];
+            }
+          else if (!strcmp(argv[i], "-o"))
+            {
+              options = argv[i + 1];
+            }
+          else
+            {
+              break;
+            }
+        }
+
+      if (type == NULL || i >= argc || argv[i][0] == '-' || argc - i > 2)
+        {
+          init_err("mount: bad arguments");
+          return -EINVAL;
+        }
+
+      if (argc - i == 2)
+        {
+          source = argv[i++];
+        }
+
+      target = argv[i];
+      if (options != NULL)
+        {
+          flags = mount_parse_options(options, data);
+        }
+    }
+  else
+    {
+      type   = argv[1];
+      source = argv[2];
+      target = argv[3];
+
+      for (i = 4; i < argc; i++)
+        {
+          flags |= mount_parse_options(argv[i], data);
+          if (data[0] != '\0' && i != argc - 1)
+            {
+              init_err("mount '%s': unknown flag '%s'", target, argv[i]);
+              return -EINVAL;
+            }
+        }
+    }
+
+  if (mount(source, target, type, flags,
+            data[0] != '\0' ? data : NULL) < 0)
+    {
+      ret = -errno;
+      init_err("mount %s '%s' on '%s' failed: %d",
+               type, source ? source : "", target, -ret);
+      return ret;
+    }
+
+  return 0;
+}
+
+/* umount <dir> */
+
+static int cmd_umount(FAR struct action_manager_s *am,
+                      int argc, FAR char **argv)
+{
+  int ret;
+
+  UNUSED(am);
+
+  if (umount(argv[1]) < 0)
+    {
+      ret = errno;
+      init_err("umount '%s' failed: %d", argv[1], ret);
+      return -ret;
+    }
+
+  return 0;
+}
+#endif
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
diff --git a/system/nxinit/test/test_nxinit.c b/system/nxinit/test/test_nxinit.c
index c38cbf3d0..b486bf96a 100644
--- a/system/nxinit/test/test_nxinit.c
+++ b/system/nxinit/test/test_nxinit.c
@@ -57,6 +57,7 @@ int main(int argc, FAR char *argv[])
       cmocka_unit_test(test_nxinit_action_event_match_fnmatch),
       cmocka_unit_test(test_nxinit_action_event_and_semantics),
       cmocka_unit_test(test_nxinit_action_empty_skipped),
+      cmocka_unit_test(test_nxinit_action_builtin_mount),
       cmocka_unit_test(test_nxinit_service_duplicate_conflict),
       cmocka_unit_test(test_nxinit_service_override_replaces_duplicate),
       cmocka_unit_test(test_nxinit_service_args_max_boundary),
diff --git a/system/nxinit/test/test_nxinit.h b/system/nxinit/test/test_nxinit.h
index c8aa70522..e9202d125 100644
--- a/system/nxinit/test/test_nxinit.h
+++ b/system/nxinit/test/test_nxinit.h
@@ -71,6 +71,7 @@ void test_nxinit_action_event_match_invert(FAR void **state);
 void test_nxinit_action_event_match_fnmatch(FAR void **state);
 void test_nxinit_action_event_and_semantics(FAR void **state);
 void test_nxinit_action_empty_skipped(FAR void **state);
+void test_nxinit_action_builtin_mount(FAR void **state);
 
 /****************************************************************************
  * Name: test_nxinit_service_*
diff --git a/system/nxinit/test/test_nxinit_action.c 
b/system/nxinit/test/test_nxinit_action.c
index 7fe229ddb..c077f0900 100644
--- a/system/nxinit/test/test_nxinit_action.c
+++ b/system/nxinit/test/test_nxinit_action.c
@@ -27,6 +27,8 @@
 #include <nuttx/config.h>
 #include <nuttx/list.h>
 
+#include <errno.h>
+#include <fcntl.h>
 #include <setjmp.h>
 #include <limits.h>
 #include <stdarg.h>
@@ -34,12 +36,22 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 #include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
 #include <cmocka.h>
 
 #include "../action.h"
+#include "../builtin.h"
 #include "test_nxinit.h"
 
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define TEST_MNT "/nxinit_test_mnt"
+
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
@@ -292,3 +304,108 @@ void test_nxinit_action_empty_skipped(FAR void **state)
 
   action_manager_free_all(&am);
 }
+
+/****************************************************************************
+ * Name: test_nxinit_action_builtin_mount
+ *
+ * Description:
+ *   "mount" mounts a tmpfs and passes the flags of its flag word and its
+ *   option list to mount(), "umount" unmounts it, and an unknown flag
+ *   fails with -EINVAL.  The same with
+ *   the NSH form "mount -t <type> -o <options> <dir>".  A line in neither
+ *   form fails with -EINVAL.
+ ****************************************************************************/
+
+void test_nxinit_action_builtin_mount(FAR void **state)
+{
+#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_TMPFS)
+  FAR char *mnt[] =
+    {
+      "mount", "tmpfs", "none", TEST_MNT, "nosuid", "noexec,size=1", NULL
+    };
+
+  FAR char *bad[] =
+    {
+      "mount", "tmpfs", "none", TEST_MNT, "bogus", "size=1", NULL
+    };
+
+  FAR char *umnt[] =
+    {
+      "umount", TEST_MNT, NULL
+    };
+
+  FAR char *nshmnt[] =
+    {
+      "mount", "-t", "tmpfs", "-o", "noexec,size=1", TEST_MNT, NULL
+    };
+
+  FAR char *nshbad[] =
+    {
+      "mount", "-t", "tmpfs", "-x", TEST_MNT, NULL
+    };
+
+  FAR char *shortline[] =
+    {
+      "mount", "tmpfs", TEST_MNT, NULL
+    };
+
+  struct action_manager_s am;
+  struct statfs sfs;
+  struct stat st;
+  int fd;
+  int i;
+
+  action_manager_init(&am);
+
+  assert_int_equal(init_builtin_run(&am, 6, mnt), 0);
+  assert_string_equal(mnt[5], "noexec,size=1");
+  assert_int_equal(statfs(TEST_MNT, &sfs), 0);
+  assert_int_equal(sfs.f_type, TMPFS_MAGIC);
+
+  /* With "noexec" in the options, stat() reports no execute permission */
+
+  fd = open(TEST_MNT "/file", O_CREAT | O_WRONLY, 0755);
+  assert_true(fd >= 0);
+  close(fd);
+  assert_int_equal(stat(TEST_MNT "/file", &st), 0);
+  assert_int_equal(st.st_mode & S_IXUSR, 0);
+  unlink(TEST_MNT "/file");
+
+  assert_int_equal(init_builtin_run(&am, 2, umnt), 0);
+  assert_int_not_equal(statfs(TEST_MNT, &sfs), 0);
+
+  assert_int_equal(init_builtin_run(&am, 6, bad), -EINVAL);
+  assert_int_not_equal(statfs(TEST_MNT, &sfs), 0);
+
+  /* The NSH form with a flag in the -o list, twice: the line must not
+   * change when it runs
+   */
+
+  for (i = 0; i < 2; i++)
+    {
+      assert_int_equal(init_builtin_run(&am, 6, nshmnt), 0);
+      assert_string_equal(nshmnt[4], "noexec,size=1");
+      assert_int_equal(statfs(TEST_MNT, &sfs), 0);
+      assert_int_equal(sfs.f_type, TMPFS_MAGIC);
+
+      fd = open(TEST_MNT "/file", O_CREAT | O_WRONLY, 0755);
+      assert_true(fd >= 0);
+      close(fd);
+      assert_int_equal(stat(TEST_MNT "/file", &st), 0);
+      assert_int_equal(st.st_mode & S_IXUSR, 0);
+      unlink(TEST_MNT "/file");
+
+      assert_int_equal(init_builtin_run(&am, 2, umnt), 0);
+    }
+
+  assert_int_equal(init_builtin_run(&am, 5, nshbad), -EINVAL);
+  assert_int_not_equal(statfs(TEST_MNT, &sfs), 0);
+
+  /* A line in neither form fails and does not go to "sh -c" */
+
+  assert_int_equal(init_builtin_run(&am, 3, shortline), -EINVAL);
+  assert_int_not_equal(statfs(TEST_MNT, &sfs), 0);
+#else
+  skip();
+#endif
+}

Reply via email to