nshlib: add chmod and chown builtins

Add minimal chmod/chown support to NSH using the
existing libc/VFS syscall interfaces.

Supported forms:

  - chmod <octal-mode> <path>
  - chown <uid>[:gid] <path>

The chown implementation supports the numeric
ownership forms already handled by the underlying
NuttX chown() implementation, including unchanged
uid/gid semantics via omitted fields.

Only numeric permission modes and numeric uid/gid
forms are supported in this initial implementation.

This adds interactive filesystem permission and
ownership management support to NSH and aligns the
shell more closely with standard POSIX environments.

Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
This commit is contained in:
Abhishek Mishra 2026-05-13 21:40:51 +00:00 committed by Xiang Xiao
parent f0f7c5ada9
commit ecfc1b5b61
4 changed files with 147 additions and 0 deletions

View file

@ -341,6 +341,16 @@ config NSH_DISABLE_CD
bool "Disable cd"
default DEFAULT_SMALL
config NSH_DISABLE_CHMOD
bool "Disable chmod"
default DEFAULT_SMALL
depends on FS_PERMISSION
config NSH_DISABLE_CHOWN
bool "Disable chown"
default DEFAULT_SMALL
depends on FS_PERMISSION
config NSH_DISABLE_CP
bool "Disable cp"
default DEFAULT_SMALL

View file

@ -963,6 +963,12 @@ int cmd_irqinfo(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv);
#ifndef CONFIG_NSH_DISABLE_CAT
int cmd_cat(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv);
#endif
#if defined(CONFIG_FS_PERMISSION) && !defined(CONFIG_NSH_DISABLE_CHMOD)
int cmd_chmod(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv);
#endif
#if defined(CONFIG_FS_PERMISSION) && !defined(CONFIG_NSH_DISABLE_CHOWN)
int cmd_chown(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv);
#endif
#ifndef CONFIG_NSH_DISABLE_CP
int cmd_cp(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv);
#endif

View file

@ -168,6 +168,14 @@ static const struct cmdmap_s g_cmdmap[] =
CMD_MAP("cd", cmd_cd, 1, 2, "[<dir-path>|-|~|..]"),
#endif
#if defined(CONFIG_FS_PERMISSION) && !defined(CONFIG_NSH_DISABLE_CHMOD)
CMD_MAP("chmod", cmd_chmod, 3, 3, "<octal-mode> <path>"),
#endif
#if defined(CONFIG_FS_PERMISSION) && !defined(CONFIG_NSH_DISABLE_CHOWN)
CMD_MAP("chown", cmd_chown, 3, 3, "[<uid>][:<gid>] <path>"),
#endif
#ifndef CONFIG_NSH_DISABLE_CP
CMD_MAP("cp", cmd_cp, 3, 4, "[-r] <source-path> <dest-path>"),
#endif

View file

@ -843,6 +843,129 @@ int cmd_cat(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
}
#endif
/****************************************************************************
* Name: cmd_chmod
*
* Description:
* chmod <octal-mode> <path>
*
* Only numeric (octal) modes are supported.
*
****************************************************************************/
#if defined(CONFIG_FS_PERMISSION) && !defined(CONFIG_NSH_DISABLE_CHMOD)
int cmd_chmod(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
{
FAR char *fullpath;
FAR char *endptr;
long mode;
int ret = ERROR;
UNUSED(argc);
mode = strtol(argv[1], &endptr, 8);
if (endptr == argv[1] || *endptr != '\0' || mode < 0 || mode > 0777)
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
return ERROR;
}
fullpath = nsh_getfullpath(vtbl, argv[2]);
if (fullpath != NULL)
{
ret = chmod(fullpath, (mode_t)mode);
if (ret < 0)
{
nsh_error(vtbl, g_fmtcmdfailed, argv[0], "chmod", NSH_ERRNO);
}
nsh_freefullpath(fullpath);
}
return ret;
}
#endif
/****************************************************************************
* Name: cmd_chown
*
* Description:
* chown <uid>[:<gid>] <path>
* chown [<uid>]:<gid> <path>
*
* Only numeric uid/gid forms are supported. Empty uid or gid fields
* leave that side unchanged.
*
****************************************************************************/
#if defined(CONFIG_FS_PERMISSION) && !defined(CONFIG_NSH_DISABLE_CHOWN)
int cmd_chown(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
{
FAR const char *spec = argv[1];
FAR char *endptr;
FAR char *fullpath;
long value;
uid_t uid = (uid_t)-1;
gid_t gid = (gid_t)-1;
int ret = ERROR;
UNUSED(argc);
value = strtol(spec, &endptr, 10);
if (endptr != spec)
{
if (value < 0)
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
return ERROR;
}
uid = (uid_t)value;
}
if (*endptr == ':')
{
FAR const char *gidstr = endptr + 1;
if (*gidstr != '\0')
{
value = strtol(gidstr, &endptr, 10);
if (*endptr != '\0' || value < 0)
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
return ERROR;
}
gid = (gid_t)value;
}
}
else if (*endptr != '\0')
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
return ERROR;
}
if (uid == (uid_t)-1 && gid == (gid_t)-1)
{
nsh_error(vtbl, g_fmtarginvalid, argv[0]);
return ERROR;
}
fullpath = nsh_getfullpath(vtbl, argv[2]);
if (fullpath != NULL)
{
ret = lchown(fullpath, uid, gid);
if (ret < 0)
{
nsh_error(vtbl, g_fmtcmdfailed, argv[0], "chown", NSH_ERRNO);
}
nsh_freefullpath(fullpath);
}
return ret;
}
#endif
/****************************************************************************
* Name: cmd_dmesg
****************************************************************************/