Newer
Older
/****************************************************************************
* Copyright (C) 2007, 2008 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#if CONFIG_NFILE_DESCRIPTORS > 0
# include <sys/stat.h>
# include <fcntl.h>
# ifdef CONFIG_FS_READABLE /* Need at least one filesytem in configuration */
# include <nuttx/ramdisk.h>
# endif
# ifdef CONFIG_FS_FAT
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <limits.h>
#include "nsh.h"
/****************************************************************************
* Definitions
****************************************************************************/
#define LSFLAGS_SIZE 1
#define LSFLAGS_LONG 2
#define LSFLAGS_RECURSIVE 4
/* The size of the I/O buffer may be specified in the
* configs/<board-name>defconfig file -- provided that it is at least as
* large as PATH_MAX.
*/
#if CONFIG_NFILE_DESCRIPTORS > 0
# ifdef CONFIG_EXAMPLES_NSH_FILEIOSIZE
# if CONFIG_EXAMPLES_NSH_FILEIOSIZE > (PATH_MAX + 1)
# define IOBUFFERSIZE CONFIG_EXAMPLES_NSH_FILEIOSIZE
# else
# define IOBUFFERSIZE (PATH_MAX + 1)
# endif
# else
# endif
# else
# define IOBUFFERSIZE (PATH_MAX + 1)
#endif
/****************************************************************************
* Private Types
****************************************************************************/
typedef int (*direntry_handler_t)(FAR struct nsh_vtbl_s *, const char *, struct dirent *, void *);
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
/* Common buffer for file I/O. Note the use of this common buffer precludes
* multiple copies of NSH running concurrently. It should be allocated per
* NSH instance and retained in the "vtbl" as is done for the telnet
/****************************************************************************
* Public Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: trim_dir
****************************************************************************/
static void trim_dir(char *arg)
{
/* Skip any trailing '/' characters (unless it is also the leading '/') */
int len = strlen(arg) - 1;
while (len > 0 && arg[len] == '/')
{
arg[len] = '\0';
len--;
}
}
/****************************************************************************
****************************************************************************/
static char *nsh_getdirpath(const char *path, const char *file)
g_iobuffer[PATH_MAX] = '\0';
return strdup(g_iobuffer);
}
/****************************************************************************
* Name: foreach_direntry
****************************************************************************/
static int foreach_direntry(FAR struct nsh_vtbl_s *vtbl, const char *cmd, const char *dirpath,
direntry_handler_t handler, void *pvarg)
{
DIR *dirp;
int ret = OK;
/* Trim trailing '/' from directory names */
trim_dir(arg);
#endif
/* Open the directory */
dirp = opendir(dirpath);
if (!dirp)
{
/* Failed to open the directory */
nsh_output(vtbl, g_fmtnosuch, cmd, "directory", dirpath);
return ERROR;
}
/* Read each directory entry */
for (;;)
{
struct dirent *entryp = readdir(dirp);
if (!entryp)
{
/* Finished with this directory */
break;
}
/* Call the handler with this directory entry */
{
/* The handler reported a problem */
ret = ERROR;
break;
}
}
closedir(dirp);
return ret;
}
/****************************************************************************
* Name: ls_specialdir
****************************************************************************/
static inline int ls_specialdir(const char *dir)
{
/* '.' and '..' directories are not listed like normal directories */
return (strcmp(dir, ".") == 0 || strcmp(dir, "..") == 0);
}
/****************************************************************************
* Name: ls_handler
****************************************************************************/
#if CONFIG_NFILE_DESCRIPTORS > 0
static int ls_handler(FAR struct nsh_vtbl_s *vtbl, const char *dirpath, struct dirent *entryp, void *pvarg)
{
unsigned int lsflags = (unsigned int)pvarg;
int ret;
/* Check if any options will require that we stat the file */
if ((lsflags & (LSFLAGS_SIZE|LSFLAGS_LONG)) != 0)
{
struct stat buf;
char *fullpath = nsh_getdirpath(dirpath, entryp->d_name);
/* Yes, stat the file */
ret = stat(fullpath, &buf);
free(fullpath);
if (ret != 0)
{
nsh_output(vtbl, g_fmtcmdfailed, "ls", "stat", NSH_ERRNO);
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
}
if ((lsflags & LSFLAGS_LONG) != 0)
{
char details[] = "----------";
if (S_ISDIR(buf.st_mode))
{
details[0]='d';
}
else if (S_ISCHR(buf.st_mode))
{
details[0]='c';
}
else if (S_ISBLK(buf.st_mode))
{
details[0]='b';
}
if ((buf.st_mode & S_IRUSR) != 0)
{
details[1]='r';
}
if ((buf.st_mode & S_IWUSR) != 0)
{
details[2]='w';
}
if ((buf.st_mode & S_IXUSR) != 0)
{
details[3]='x';
}
if ((buf.st_mode & S_IRGRP) != 0)
{
details[4]='r';
}
if ((buf.st_mode & S_IWGRP) != 0)
{
details[5]='w';
}
if ((buf.st_mode & S_IXGRP) != 0)
{
details[6]='x';
}
if ((buf.st_mode & S_IROTH) != 0)
{
details[7]='r';
}
if ((buf.st_mode & S_IWOTH) != 0)
{
details[8]='w';
}
if ((buf.st_mode & S_IXOTH) != 0)
{
details[9]='x';
}
}
}
/* then provide the filename that is common to normal and verbose output */
if (DIRENT_ISDIRECTORY(entryp->d_type) && !ls_specialdir(entryp->d_name))
}
return OK;
}
#endif
/****************************************************************************
* Name: ls_recursive
****************************************************************************/
#if CONFIG_NFILE_DESCRIPTORS > 0
static int ls_recursive(FAR struct nsh_vtbl_s *vtbl, const char *dirpath,
struct dirent *entryp, void *pvarg)
/* Is this entry a directory (and not one of the special directories, . and ..)? */
if (DIRENT_ISDIRECTORY(entryp->d_type) && !ls_specialdir(entryp->d_name))
/* Traverse the directory */
ret = foreach_direntry(vtbl, "ls", newpath, ls_handler, pvarg);
if (ret == 0)
{
/* Then recurse to list each directory within the directory */
ret = foreach_direntry(vtbl, "ls", newpath, ls_recursive, pvarg);
free(newpath);
}
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: cmd_cat
****************************************************************************/
#if CONFIG_NFILE_DESCRIPTORS > 0
#ifndef CONFIG_EXAMPLES_NSH_DISABLE_CAT
int cmd_cat(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
/* Loop for each file name on the command line */
fullpath = nsh_getfullpath(vtbl, argv[i]);
if (fullpath)
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO);
}
else
{
/* And just dump it byte for byte into stdout */
/* Check for read errors */
if (nbytesread < 0)
if (errno != EINTR)
{
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "read", NSH_ERRNO);
ret = ERROR;
break;
}
}
while (nbyteswritten < nbytesread)
{
int n = write(1, buffer, nbytesread);
if (n < 0)
/* EINTR is not an error */
if (errno != EINTR)
{
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "write", NSH_ERRNO);
ret = ERROR;
break;
}
}
else
{
nbyteswritten += n;
/* Free the allocated full path */
nsh_freefullpath(fullpath);
/****************************************************************************
* Name: cmd_cp
****************************************************************************/
#if CONFIG_NFILE_DESCRIPTORS > 0
#ifndef CONFIG_EXAMPLES_NSH_DISABLE_CP
int cmd_cp(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
char *srcpath = NULL;
char *destpath = NULL;
char *allocpath = NULL;
int oflags = O_WRONLY|O_CREAT|O_TRUNC;
int rdfd;
int wrfd;
/* Get the full path to the source file */
srcpath = nsh_getfullpath(vtbl, argv[1]);
if (!srcpath)
{
goto errout;
}
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO);
goto errout_with_srcpath;
}
/* Get the full path to the destination file or directory */
destpath = nsh_getfullpath(vtbl, argv[2]);
if (!destpath)
{
goto errout_with_rdfd;
}
/* Check if the destination is a directory */
if (ret == 0)
{
/* Something exists here... is it a directory? */
if (S_ISDIR(buf.st_mode))
{
/* Yes, it is a directory. Remove any trailing '/' characters from the path */
trim_dir(argv[2]);
/* Construct the full path to the new file */
allocpath = nsh_getdirpath(argv[2], basename(argv[1]) );
if (!allocpath)
/* Open then dest for writing */
nsh_freefullpath(destpath);
destpath = allocpath;
}
else if (!S_ISREG(buf.st_mode))
{
/* Maybe it is a driver? */
oflags = O_WRONLY;
}
}
/* Now open the destination */
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO);
}
/* Now copy the file */
for (;;)
{
int nbytesread;
int nbyteswritten;
do
{
nbytesread = read(rdfd, g_iobuffer, IOBUFFERSIZE);
if (nbytesread == 0)
{
/* End of file */
}
else if (nbytesread < 0 && errno != EINTR)
{
/* Read error */
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "read", NSH_ERRNO);
}
}
while (nbytesread <= 0);
do
{
nbyteswritten = write(wrfd, g_iobuffer, nbytesread);
if (nbyteswritten >= 0)
{
nbytesread -= nbyteswritten;
}
else if (errno != EINTR)
{
/* Read error */
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "write", NSH_ERRNO);
errout_with_destpath:
if (destpath && !allocpath)
{
nsh_freefullpath(destpath);
}
errout_with_rdfd:
errout_with_srcpath:
if (srcpath)
{
nsh_freefullpath(srcpath);
}
errout:
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
/****************************************************************************
* Name: cmd_losetup
****************************************************************************/
#if CONFIG_NFILE_DESCRIPTORS > 0 && !defined(CONFIG_DISABLE_MOUNTPOINT)
#ifndef CONFIG_EXAMPLES_NSH_DISABLE_LOSETUP
int cmd_losetup(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
char *loopdev = NULL;
char *filepath = NULL;
boolean teardown = FALSE;
boolean readonly = FALSE;
off_t offset = 0;
int ret = ERROR;
int option;
/* Get the losetup options: Two forms are supported:
*
* losetup -d <loop-device>
* losetup [-o <offset>] [-r] <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:r")) != ERROR)
{
switch (option)
{
case 'd':
loopdev = nsh_getfullpath(vtbl, optarg);
teardown = TRUE;
break;
case 'o':
offset = atoi(optarg);
break;
case 'r':
readonly = TRUE;
break;
case '?':
default:
nsh_output(vtbl, g_fmtarginvalid, argv[0]);
return ERROR;
}
}
/* 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_output(vtbl, g_fmtargrequired, argv[0]);
goto errout_with_paths;
}
}
/* There should be nothing else on the command line */
if (optind < argc)
{
nsh_output(vtbl, g_fmttoomanyargs, argv[0]);
goto errout_with_paths;
}
/* Perform the teardown operation */
if (teardown)
{
/* Tear down the loop device. */
ret = loteardown(loopdev);
if (ret < 0)
{
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "loteardown", NSH_ERRNO_OF(-ret));
goto errout_with_paths;
}
}
else
{
/* Set up the loop device */
ret = losetup(loopdev, filepath, 512, offset, readonly);
if (ret < 0)
{
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "losetup", NSH_ERRNO_OF(-ret));
goto errout_with_paths;
}
}
/* Free memory */
errout_with_paths:
if (loopdev)
{
free(loopdev);
}
if (filepath)
{
free(filepath);
}
return ret;
}
#endif
#endif
/****************************************************************************
* Name: cmd_ls
****************************************************************************/
#if CONFIG_NFILE_DESCRIPTORS > 0
#ifndef CONFIG_EXAMPLES_NSH_DISABLE_LS
int cmd_ls(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
int ret;
/* Get the ls options */
int option;
while ((option = getopt(argc, argv, "lRs")) != ERROR)
{
switch (option)
{
case 'l':
lsflags |= (LSFLAGS_SIZE|LSFLAGS_LONG);
break;
case 'R':
lsflags |= LSFLAGS_RECURSIVE;
break;
case 's':
lsflags |= LSFLAGS_SIZE;
break;
case '?':
default:
#ifndef CONFIG_DISABLE_ENVIRON
relpath = nsh_getcwd();
#else
#endif
}
else
{
relpath = argv[optind];
}
/* Get the fullpath to the directory */
fullpath = nsh_getfullpath(vtbl, relpath);
if (!fullpath)
{
return ERROR;
nsh_output(vtbl, "%s:\n", fullpath);
ret = foreach_direntry(vtbl, "ls", fullpath, ls_handler, (void*)lsflags);
if (ret == OK && (lsflags & LSFLAGS_RECURSIVE) != 0)
{
/* Then recurse to list each directory within the directory */
ret = foreach_direntry(vtbl, "ls", fullpath, ls_recursive, (void*)lsflags);
/****************************************************************************
* Name: cmd_mkdir
****************************************************************************/
#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_WRITABLE)
#ifndef CONFIG_EXAMPLES_NSH_DISABLE_MKDIR
int cmd_mkdir(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
char *fullpath = nsh_getfullpath(vtbl, argv[1]);
int ret = ERROR;
if (fullpath)
ret = mkdir(fullpath, 0777);
if (ret < 0)
{
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "mkdir", NSH_ERRNO);
}
nsh_freefullpath(fullpath);
/****************************************************************************
* Name: cmd_mkfatfs
****************************************************************************/
#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_FAT)
#ifndef CONFIG_EXAMPLES_NSH_DISABLE_MKFATFS
int cmd_mkfatfs(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
struct fat_format_s fmt = FAT_FORMAT_INITIALIZER;
char *fullpath = nsh_getfullpath(vtbl, argv[1]);
int ret = ERROR;
if (fullpath)
{
ret = mkfatfs(fullpath, &fmt);
if (ret < 0)
{
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "mkfatfs", NSH_ERRNO);
}
nsh_freefullpath(fullpath);
/****************************************************************************
* Name: cmd_mkfifo
****************************************************************************/
#ifndef CONFIG_EXAMPLES_NSH_DISABLE_MKFIFO
int cmd_mkfifo(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
char *fullpath = nsh_getfullpath(vtbl, argv[1]);
int ret = ERROR;
if (fullpath)
ret = mkfifo(fullpath, 0777);
if (ret < 0)
{
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "mkfifo", NSH_ERRNO);
}
nsh_freefullpath(fullpath);
/****************************************************************************
* Name: cmd_mkrd
****************************************************************************/
#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_WRITABLE)
#ifndef CONFIG_EXAMPLES_NSH_DISABLE_MKRD
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
int cmd_mkrd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
const char *fmt;
ubyte *buffer;
uint32 nsectors;
int sectsize = 512;
int minor = 0;
int ret;
/* Get the mount options */
int option;
while ((option = getopt(argc, argv, ":m:s:")) != ERROR)
{
switch (option)
{
case 'm':
minor = atoi(optarg);
if (minor < 0 || minor > 255)
{
fmt = g_fmtargrange;
goto errout_with_fmt;
}
break;
case 's':
sectsize = atoi(optarg);
if (minor < 0 || minor > 16384)
{
fmt = g_fmtargrange;
goto errout_with_fmt;
}
break;
case ':':
fmt = g_fmtargrequired;
goto errout_with_fmt;
case '?':
default:
fmt = g_fmtarginvalid;
goto errout_with_fmt;
}
}
/* There should be exactly on parameter left on the command-line */
if (optind == argc-1)
{
nsectors = (uint32)atoi(argv[optind]);
}
else if (optind >= argc)
{
fmt = g_fmttoomanyargs;
goto errout_with_fmt;
}
else
{
fmt = g_fmtargrequired;
goto errout_with_fmt;
}
/* Allocate the memory backing up the ramdisk */