From f63a3cac0e482da68a046ed495aeea62171b02ba Mon Sep 17 00:00:00 2001
From: Oreh <a.oryshchenko@gmail.com>
Date: Mon, 6 Feb 2023 16:10:20 +0100
Subject: [PATCH] Add NSH lomtd command (creates MTD loop device)

---
 nshlib/Kconfig       |   4 ++
 nshlib/nsh.h         |   3 +
 nshlib/nsh_command.c |   8 +++
 nshlib/nsh_fscmds.c  | 167 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 182 insertions(+)

diff --git a/nshlib/Kconfig b/nshlib/Kconfig
index a9d3194e9..9607facb7 100644
--- a/nshlib/Kconfig
+++ b/nshlib/Kconfig
@@ -377,6 +377,10 @@ config NSH_DISABLE_LOSMART
 	bool "Disable losmart"
 	default DEFAULT_SMALL || !MTD_SMART
 
+config NSH_DISABLE_LOMTD
+	bool "Disable lomtd"
+	default DEFAULT_SMALL || !MTD_LOOP
+
 config NSH_DISABLE_LN
 	bool "Disable ln"
 	default DEFAULT_SMALL
diff --git a/nshlib/nsh.h b/nshlib/nsh.h
index 4bd8ae974..e072b1057 100644
--- a/nshlib/nsh.h
+++ b/nshlib/nsh.h
@@ -1018,6 +1018,9 @@ int cmd_irqinfo(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv);
 #  if defined(CONFIG_SMART_DEV_LOOP) && !defined(CONFIG_NSH_DISABLE_LOSMART)
   int cmd_losmart(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv);
 #  endif
+#  if defined(CONFIG_MTD_LOOP) && !defined(CONFIG_NSH_DISABLE_LOMTD)
+  int cmd_lomtd(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv);
+#  endif
 #  if defined(CONFIG_PIPES) && CONFIG_DEV_FIFO_SIZE > 0 && \
       !defined(CONFIG_NSH_DISABLE_MKFIFO)
   int cmd_mkfifo(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv);
diff --git a/nshlib/nsh_command.c b/nshlib/nsh_command.c
index 28009f49b..efd3a366b 100644
--- a/nshlib/nsh_command.c
+++ b/nshlib/nsh_command.c
@@ -283,6 +283,14 @@ static const struct cmdmap_s g_cmdmap[] =
 # endif
 #endif
 
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+# if defined(CONFIG_MTD_LOOP) && !defined(CONFIG_NSH_DISABLE_LOMTD)
+  { "lomtd",   cmd_lomtd, 3, 9,
+    "[-d <dev-path>] | [[-o <offset>] [-e <erase-size>] "
+    "[-s <sect-size>] <dev-path> <file-path>]]" },
+# endif
+#endif
+
 #if !defined(CONFIG_NSH_DISABLE_LN) && defined(CONFIG_PSEUDOFS_SOFTLINKS)
   { "ln",       cmd_ln,       3, 4, "[-s] <target> <link>" },
 #endif
diff --git a/nshlib/nsh_fscmds.c b/nshlib/nsh_fscmds.c
index 68419faeb..c13d829bd 100644
--- a/nshlib/nsh_fscmds.c
+++ b/nshlib/nsh_fscmds.c
@@ -56,6 +56,9 @@
 #    include <sys/ioctl.h>
 #    include <nuttx/fs/smart.h>
 #  endif
+#  ifdef CONFIG_MTD_LOOP
+#    include <nuttx/fs/loopmtd.h>
+#  endif
 #  ifdef CONFIG_NFS
 #    include <sys/socket.h>
 #    include <netinet/in.h>
@@ -998,6 +1001,170 @@ errout_with_paths:
 #endif
 #endif
 
+/****************************************************************************
+ * Name: cmd_lomtd
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+#  if defined(CONFIG_MTD_LOOP) && !defined(CONFIG_NSH_DISABLE_LOMTD)
+int cmd_lomtd(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
+{
+  FAR char *loopdev = NULL;
+  FAR char *filepath = NULL;
+  struct mtd_losetup_s setup;
+  bool teardown = false;
+  int erasesize = -1;
+  int sectsize = -1;
+  off_t offset = 0;
+  bool badarg = false;
+  int ret = ERROR;
+  int option;
+  int fd;
+
+  /* Get the lomtd options:  Two forms are supported:
+   *
+   *   lomtd -d <loop-device>
+   *   lomtd [-o <offset>] [-e erasesize] [-s sectsize]
+   *         <loop-device> <filename>
+   *
+   * NOTE that the -o and -r options are accepted with the -d option, but
+   * will be ignored.
+   */
+
+  while ((option = getopt(argc, argv, "d:o:e:s:")) != ERROR)
+    {
+      switch (option)
+        {
+        case 'd':
+          loopdev  = nsh_getfullpath(vtbl, optarg);
+          teardown = true;
+          break;
+
+        case 'e':
+          erasesize = atoi(optarg);
+          break;
+
+        case 'o':
+          offset = atoi(optarg);
+          break;
+
+        case 's':
+          sectsize = atoi(optarg);
+          break;
+
+        case '?':
+        default:
+          nsh_error(vtbl, g_fmtarginvalid, argv[0]);
+          badarg = true;
+          break;
+        }
+    }
+
+  /* If a bad argument was encountered,
+   * then return without processing the command
+   */
+
+  if (badarg)
+    {
+      goto errout_with_paths;
+    }
+
+  /* If this is not a tear down operation, then additional command line
+   * parameters are required.
+   */
+
+  if (!teardown)
+    {
+      /* There must be two arguments on the command line after the options */
+
+      if (optind + 1 < argc)
+        {
+          loopdev = nsh_getfullpath(vtbl, argv[optind]);
+          optind++;
+
+          filepath = nsh_getfullpath(vtbl, argv[optind]);
+          optind++;
+        }
+      else
+        {
+          nsh_error(vtbl, g_fmtargrequired, argv[0]);
+          goto errout_with_paths;
+        }
+    }
+
+  /* There should be nothing else on the command line */
+
+  if (optind < argc)
+    {
+      nsh_error(vtbl, g_fmttoomanyargs, argv[0]);
+      goto errout_with_paths;
+    }
+
+  /* Open the loop device */
+
+  fd = open("/dev/loopmtd", O_RDONLY);
+  if (fd < 0)
+    {
+      nsh_error(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO);
+      goto errout_with_paths;
+    }
+
+  /* Perform the teardown operation */
+
+  if (teardown)
+    {
+      /* Tear down the loop device. */
+
+      ret = ioctl(fd, MTD_LOOPIOC_TEARDOWN,
+                  (unsigned long)((uintptr_t)loopdev));
+      if (ret < 0)
+        {
+          nsh_error(vtbl, g_fmtcmdfailed, argv[0], "ioctl", NSH_ERRNO);
+          goto errout_with_fd;
+        }
+    }
+  else
+    {
+      /* Set up the loop device */
+
+      setup.devname   = loopdev;    /* The loop block device to be created */
+      setup.filename  = filepath;   /* The file or character device to use */
+      setup.sectsize  = sectsize;   /* The sector size to use with the block device */
+      setup.erasesize = erasesize;  /* The sector size to use with the block device */
+      setup.offset    = offset;     /* An offset that may be applied to the device */
+
+      ret = ioctl(fd, MTD_LOOPIOC_SETUP,
+                  (unsigned long)((uintptr_t)&setup));
+      if (ret < 0)
+        {
+          nsh_error(vtbl, g_fmtcmdfailed, argv[0], "ioctl", NSH_ERRNO);
+          goto errout_with_fd;
+        }
+    }
+
+  ret = OK;
+
+  /* Free resources */
+
+errout_with_fd:
+  close(fd);
+
+errout_with_paths:
+  if (loopdev)
+    {
+      free(loopdev);
+    }
+
+  if (filepath)
+    {
+      free(filepath);
+    }
+
+  return ret;
+}
+#  endif
+#endif
+
 /****************************************************************************
  * Name: cmd_ln
  ****************************************************************************/