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
+}