+These are the major changes from version 3.7 to version 3.8:
+
+The Makefile has been updated for several distribution's standards.
+The ext2_fs include file location has been changed.
+Some compiler warnings were fixed.
+
+The -ls command has the -n option to print numeric user and group ids.
+
+The -n option might be needed in case the unsuppressable dynamic
+linking used to lookup the names fails.
+
+The -chroot, -pivot_root, and -losetup commands have been added.
+
+The exit status for commands has been implemented (such as -exit).
+Thanks to Tollef Fog Heen for the patches.
+
+
These are the major changes from version 3.6 to version 3.7:
A few bugs in the dd command have been fixed.
#
# The HAVE_GZIP definition adds the -gzip and -gunzip commands.
# The HAVE_LINUX_ATTR definition adds the -chattr and -lsattr commands.
+# The HAVE_LINUX_CHROOT definition adds the -chroot command.
+# The HAVE_LINUX_PIVOT definition adds the -pivot_root command.
+# The HAVE_LINUX_LOSETUP definition adds the -losetup command.
# The HAVE_LINUX_MOUNT definition makes -mount and -umount work on Linux.
# The HAVE_BSD_MOUNT definition makes -mount and -umount work on BSD.
# The MOUNT_TYPE definition sets the default file system type for -mount.
#
+# Note that the linker may show warnings about 'statically linked
+# programs' requiring getpwnam, getpwuid, getgrnam and getgrgid.
+# This is unavoidable since those routines use dynamic libraries anyway.
+# Sash will still run, but if there are shared library problems then
+# the user might have to be be careful when using the -chown, -chgrp,
+# and -ls commands.
+#
+
HAVE_GZIP = 1
HAVE_LINUX_ATTR = 1
+HAVE_LINUX_CHROOT = 1
+HAVE_LINUX_LOSETUP = 1
+HAVE_LINUX_PIVOT = 1
HAVE_LINUX_MOUNT = 1
HAVE_BSD_MOUNT = 0
MOUNT_TYPE = '"ext3"'
+OPT = -O3
-CFLAGS = -O3 -Wall -Wmissing-prototypes \
+CFLAGS = $(OPT) -Wall -Wmissing-prototypes \
-DHAVE_GZIP=$(HAVE_GZIP) \
-DHAVE_LINUX_ATTR=$(HAVE_LINUX_ATTR) \
+ -DHAVE_LINUX_CHROOT=$(HAVE_LINUX_CHROOT) \
+ -DHAVE_LINUX_LOSETUP=$(HAVE_LINUX_LOSETUP) \
+ -DHAVE_LINUX_PIVOT=$(HAVE_LINUX_PIVOT) \
-DHAVE_LINUX_MOUNT=$(HAVE_LINUX_MOUNT) \
-DHAVE_BSD_MOUNT=$(HAVE_BSD_MOUNT) \
-DMOUNT_TYPE=$(MOUNT_TYPE)
-LDFLAGS = -static -s
+LDFLAGS = -static
LIBS = -lz
+DESTDIR =
BINDIR = /bin
-MANDIR = /usr/man/man1
+MANDIR = /usr/man
OBJS = sash.o cmds.o cmd_dd.o cmd_ed.o cmd_grep.o cmd_ls.o cmd_tar.o \
sash: $(OBJS)
$(CC) $(LDFLAGS) -o sash $(OBJS) $(LIBS)
+ strip sash
clean:
rm -f $(OBJS) sash
install: sash
- cp sash $(BINDIR)/sash
- cp sash.1 $(MANDIR)/sash.1
+ cp sash $(DESTDIR)/$(BINDIR)/sash
+ cp sash.1 $(DESTDIR)/$(MANDIR)/man1/sash.1
$(OBJS): sash.h
-This is release 3.7 of sash, my stand-alone shell for Linux or other systems.
+This is release 3.8 of sash, my stand-alone shell for Linux or other systems.
The purpose of this program is to make system recovery possible in
many cases where there are missing shared libraries or executables.
systems can be removed and the mount command can be configured.
David I. Bell
-dbell@canb.auug.org.au
-12 January 2004
+dbell@tip.net.au
+8 March 2014
* provided that this copyright notice remains intact.
*
* Modified:
- * Copyright (c) 2002 by David I. Bell
+ * Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
*/
typedef struct
{
- int fd; /* file reading archive from */
- BOOL eof; /* end of file has been seen */
- BOOL rescan; /* rescan the header just read */
- unsigned char * nameTable; /* long name table */
+ int fd; /* file reading archive from */
+ BOOL eof; /* end of file has been seen */
+ BOOL rescan; /* rescan the header just read */
+ char * nameTable; /* long name table */
/*
* Information about the current file read from the archive.
* This is extracted from the latest member header read.
*/
- char * name; /* current file name */
- time_t date; /* date of file */
- uid_t uid; /* user id */
- gid_t gid; /* group id */
- mode_t mode; /* file protection */
- off_t size; /* file size */
- int pad; /* padding to next header */
+ char * name; /* current file name */
+ time_t date; /* date of file */
+ uid_t uid; /* user id */
+ gid_t gid; /* group id */
+ mode_t mode; /* file protection */
+ off_t size; /* file size */
+ int pad; /* padding to next header */
} Archive;
unsigned long * ul);
-void
+int
do_ar(int argc, const char ** argv)
{
const char * options;
BOOL doTable;
BOOL doPrint;
BOOL verbose;
+ int r;
Archive arch;
+ r = 0;
verbose = FALSE;
doExtract = FALSE;
doTable = FALSE;
{
fprintf(stderr, "Too few arguments for ar\n");
- return;
+ return 1;
}
/*
case 'd': case 'm': case 'q': case 'r':
fprintf(stderr, "Writing ar files is not supported\n");
- return;
+ return 1;
default:
fprintf(stderr, "Unknown ar flag: %c\n", *options);
- return;
+ return 1;
}
}
fprintf(stderr,
"Exactly one of 'x', 'p' or 't' must be specified\n");
- return;
+ return 1;
}
/*
initArchive(&arch);
if (!openArchive(archiveName, &arch))
- return;
+ return 1;
/*
* Read the first special member of the archive.
*/
if (!readSpecialMember(&arch))
- return;
+ return 1;
/*
* Read all of the normal members of the archive.
}
if (!success)
+ {
+ r = 1;
break;
+ }
}
else
{
fprintf(stderr, "Oops -- I don't know what to do\n");
+ r = 1;
break;
}
}
closeArchive(&arch);
+
+ return r;
}
* specifying the mode in the open() call, because that mode is
* munged by the umask.
*/
- (void) fchmod(fd, arch->mode);
- (void) fchown(fd, arch->uid, arch->gid);
+ checkStatus("fchmod", fchmod(fd, arch->mode));
+ checkStatus("fchown", fchown(fd, arch->uid, arch->gid));
return fd;
}
static BOOL
writeFile(const Archive * arch, int outfd)
{
- unsigned char buf[BUF_SIZE];
- off_t n;
+ char buf[BUF_SIZE];
+ off_t n;
n = arch->size;
/*
- * Copyright (c) 2002 by David I. Bell
+ * Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
#include <sys/ioctl.h>
#include <sys/types.h>
-#include <linux/ext2_fs.h>
+
+/*
+ * These were used for old linux versions.
+ * #include <linux/fs.h>
+ * #include <linux/ext2_fs.h>
+ */
+
+#include <ext2fs/ext2_fs.h>
+
#include "sash.h"
* The chattr command.
* This can turn on or off the immutable and append-only ext2 flags.
*/
-void
+int
do_chattr(int argc, const char ** argv)
{
const char * fileName;
int oldFlags;
int newFlags;
int fd;
+ int r;
+ r = 0;
argc--;
argv++;
fprintf(stderr, "Unknown flag '%c'\n",
options[-1]);
- return;
+ return 1;
}
}
}
{
fprintf(stderr, "No attributes specified\n");
- return;
+ return 1;
}
if ((onFlags & offFlags) != 0)
{
fprintf(stderr, "Inconsistent attributes specified\n");
- return;
+ return 1;
}
/*
{
fprintf(stderr, "No files specified for setting attributes\n");
- return;
+ return 1;
}
/*
if (fd < 0)
{
perror(fileName);
-
+ r = 1;
continue;
}
if (ioctl(fd, EXT2_IOC_GETFLAGS, &oldFlags) < 0)
{
perror(fileName);
-
+ r = 1;
(void) close(fd);
continue;
if (ioctl(fd, EXT2_IOC_SETFLAGS, &newFlags) < 0)
{
perror(fileName);
-
+ r = 1;
(void) close(fd);
continue;
*/
(void) close(fd);
}
+
+ return r;
}
* The lsattr command.
* This lists the immutable and append-only ext2 flags.
*/
-void
+int
do_lsattr(int argc, const char ** argv)
{
const char * fileName;
+ int r;
int fd;
int status;
int flags;
char string[4];
+ r = 0;
argc--;
argv++;
if (fd < 0)
{
perror(fileName);
-
+ r = 1;
continue;
}
if (status < 0)
{
perror(fileName);
+ r = 1;
continue;
}
*/
printf("%s %s\n", string, fileName);
}
+
+ return r;
}
#endif
/*
- * Copyright (c) 2004 by David I. Bell
+ * Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
static long getNum(const char * cp);
-void
+int
do_dd(int argc, const char ** argv)
{
const char * str;
long outPartial;
char * buf;
char localBuf[BUF_SIZE];
+ int r;
inFile = NULL;
outFile = NULL;
skipVal = 0;
blockSize = 512;
count = -1;
+ r = 0;
while (--argc > 0)
{
{
fprintf(stderr, "Bad dd argument\n");
- return;
+ return 1;
}
*cp++ = '\0';
{
fprintf(stderr, "Multiple input files illegal\n");
- return;
+ return 1;
}
inFile = cp;
{
fprintf(stderr, "Multiple output files illegal\n");
- return;
+ return 1;
}
outFile = cp;
{
fprintf(stderr, "Bad block size value\n");
- return;
+ return 1;
}
break;
{
fprintf(stderr, "Bad count value\n");
- return;
+ return 1;
}
break;
{
fprintf(stderr, "Bad seek value\n");
- return;
+ return 1;
}
break;
{
fprintf(stderr, "Bad skip value\n");
- return;
+ return 1;
}
break;
default:
fprintf(stderr, "Unknown dd parameter\n");
- return;
+ return 1;
}
}
{
fprintf(stderr, "No input file specified\n");
- return;
+ return 1;
}
if (outFile == NULL)
{
fprintf(stderr, "No output file specified\n");
- return;
+ return 1;
}
buf = localBuf;
{
fprintf(stderr, "Cannot allocate buffer\n");
- return;
+ return 1;
}
}
if (buf != localBuf)
free(buf);
- return;
+ return 1;
}
outFd = creat(outFile, 0666);
if (buf != localBuf)
free(buf);
- return;
+ return 1;
}
if (skipVal)
if (inCc < 0)
{
perror(inFile);
+ r = 1;
goto cleanup;
}
if (inCc == 0)
{
fprintf(stderr, "End of file while skipping\n");
+ r = 1;
goto cleanup;
}
}
if (lseek(outFd, seekVal * blockSize, 0) < 0)
{
perror(outFile);
-
+ r = 1;
goto cleanup;
}
}
if (intFlag)
{
fprintf(stderr, "Interrupted\n");
+ r = 1;
goto cleanup;
}
if (outCc < 0)
{
perror(outFile);
+ r = 1;
goto cleanup;
}
close(inFd);
if (close(outFd) < 0)
+ {
perror(outFile);
+ r = 1;
+ }
if (buf != localBuf)
free(buf);
printf("%ld+%ld records in\n", inFull, inPartial);
printf("%ld+%ld records out\n", outFull, outPartial);
+
+ return r;
}
/*
- * Copyright (c) 2002 by David I. Bell
+ * Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
(const LINE * lp, const char * str, LEN len, LEN offset);
-void
+int
do_ed(int argc, const char ** argv)
{
if (!initEdit())
- return;
+ return 1;
if (argc > 1)
{
fprintf(stderr, "No memory\n");
termEdit();
- return;
+ return 1;
}
if (!readLines(fileName, 1))
{
termEdit();
- return;
+ return 1;
}
if (lastNum)
doCommands();
termEdit();
+ return 0;
}
fflush(stdout);
buf[0] = '\0';
- fgets(buf, sizeof(buf), stdin);
+
+ if (fgets(buf, sizeof(buf), stdin) == NULL)
+ return;
+
cp = buf;
while (isBlank(*cp))
static BOOL
printLines(NUM num1, NUM num2, BOOL expandFlag)
{
- const LINE * lp;
- const unsigned char * cp;
- int ch;
- LEN count;
+ const LINE * lp;
+ const char * cp;
+ int ch;
+ LEN count;
if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
{
{
if (!expandFlag)
{
- write(STDOUT, lp->data, lp->len);
+ tryWrite(STDOUT, lp->data, lp->len);
setCurNum(num1++);
lp = lp->next;
while (count-- > 0)
{
- ch = *cp++;
+ ch = *cp++ & 0xff;
if (ch & 0x80)
{
/*
- * Copyright (c) 2002 by David I. Bell
+ * Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
static const char * checkFile(const char * name);
-void
+int
do_file(int argc, const char ** argv)
{
const char * name;
printf("%s: %s\n", name, info);
}
+
+ return 0;
}
/*
- * Copyright (c) 2002 by David I. Bell
+ * Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
* Find files from the specified directory path.
* This is limited to just printing their file names.
*/
-void
+int
do_find(int argc, const char ** argv)
{
const char * cp;
{
fprintf(stderr, "No path specified\n");
- return;
+ return 1;
}
path = *argv++;
{
fprintf(stderr, "Missing type string\n");
- return;
+ return 1;
}
argc--;
{
fprintf(stderr, "Missing file name\n");
- return;
+ return 1;
}
argc--;
{
fprintf(stderr, "Missing file size\n");
- return;
+ return 1;
}
argc--;
{
fprintf(stderr, "Bad file size specified\n");
- return;
+ return 1;
}
}
else
else
fprintf(stderr, "Unknown option\n");
- return;
+ return 1;
}
}
fprintf(stderr, "Cannot stat \"%s\": %s\n", path,
strerror(errno));
- return;
+ return 1;
}
if (!S_ISDIR(statBuf.st_mode))
{
fprintf(stderr, "Path \"%s\" is not a directory\n", path);
- return;
+ return 1;
}
/*
* Now examine the files in the directory.
*/
examineDirectory(path);
+
+ return 0;
}
/*
- * Copyright (c) 2002 by David I. Bell
+ * Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
(const char * string, const char * word, BOOL ignoreCase);
-void
+int
do_grep(int argc, const char ** argv)
{
FILE * fp;
BOOL tellLine;
long line;
char buf[BUF_SIZE];
+ int r;
+ r = 1;
ignoreCase = FALSE;
tellLine = FALSE;
default:
fprintf(stderr, "Unknown option\n");
- return;
+ return 1;
}
}
if (fp == NULL)
{
perror(name);
+ r = 1;
continue;
}
{
fclose(fp);
- return;
+ return 1;
}
line++;
if (search(buf, word, ignoreCase))
{
+ r = 0;
if (tellName)
printf("%s: ", name);
fclose(fp);
}
+
+ return r;
}
/*
- * Copyright (c) 2002 by David I. Bell
+ * Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
(const CONVERT * table, const char * inFile);
-void
+int
do_gzip(int argc, const char ** argv)
{
const char * outPath;
const char * inFile;
const char * outFile;
int i;
+ int r;
+ r = 0;
argc--;
argv++;
else
fprintf(stderr, "Illegal option\n");
- return;
+ return 1;
}
}
* Try to compress the file.
*/
if (!gzip(inFile, outFile))
+ {
+ r = 1;
+
continue;
+ }
/*
* This was successful.
{
fprintf(stderr, "%s: %s\n", inFile,
"Compressed ok but unlink failed");
+
+ r = 1;
}
}
- return;
+ return r;
}
/*
if (!isDirectory(outPath))
{
if (argc == 1)
- (void) gzip(*argv, outPath);
+ r = !gzip(*argv, outPath);
else
+ {
fprintf(stderr, "Exactly one input file is required\n");
+ r = 1;
+ }
- return;
+ return r;
}
/*
/*
* Compress the input file without deleting the input file.
*/
- (void) gzip(inFile, outFile);
+ if (!gzip(inFile, outFile))
+ r = 1;
}
+
+ return r;
}
-void
+int
do_gunzip(int argc, const char ** argv)
{
const char * outPath;
const char * inFile;
const char * outFile;
int i;
+ int r;
+ r = 0;
argc--;
argv++;
else
fprintf(stderr, "Illegal option\n");
- return;
+ return 1;
}
}
{
fprintf(stderr, "%s: %s\n", inFile,
"missing compression extension");
+ r = 1;
continue;
}
* Try to uncompress the file.
*/
if (!gunzip(inFile, outFile))
+ {
+ r = 1;
continue;
+ }
/*
* This was successful.
{
fprintf(stderr, "%s: %s\n", inFile,
"Uncompressed ok but unlink failed");
+ r = 1;
}
}
- return;
+ return r;
}
/*
if (isDevice(outPath))
{
while (!intFlag && (argc-- > 0))
- (void) gunzip(*argv++, outPath);
+ {
+ if (!gunzip(*argv++, outPath))
+ r = 1;
+ }
- return;
+ return r;
}
/*
if (!isDirectory(outPath))
{
if (argc == 1)
- (void) gunzip(*argv, outPath);
+ return !gunzip(*argv, outPath);
else
fprintf(stderr, "Exactly one input file is required\n");
- return;
+ return 1;
}
/*
/*
* Uncompress the input file without deleting the input file.
*/
- (void) gunzip(inFile, outFile);
+ if (!gunzip(inFile, outFile))
+ r = 1;
}
+
+ return r;
}
/*
- * Copyright (c) 2002 by David I. Bell
+ * Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
#define LSF_MULT 0x08
#define LSF_FLAG 0x10
#define LSF_COLUMN 0x20
+#define LSF_NUMERIC 0x40
/*
static int listSize;
static int listUsed;
+/*
+ * Cached user and group name data.
+ */
+static char userName[12];
+static int userId;
+static BOOL userIdKnown;
+static char groupName[12];
+static int groupId;
+static BOOL groupIdKnown;
+
/*
* Local procedures.
static void clearListNames(void);
-void
+int
do_ls(int argc, const char ** argv)
{
const char * cp;
struct dirent * dp;
char fullName[PATH_LEN];
struct stat statBuf;
+ int r;
static const char * def[] = {"."};
*/
clearListNames();
+ userIdKnown = FALSE;
+ groupIdKnown = FALSE;
+
displayWidth = 0;
flags = 0;
while (*cp) switch (*cp++)
{
case 'l': flags |= LSF_LONG; break;
+ case 'n': flags |= LSF_NUMERIC; break;
case 'd': flags |= LSF_DIR; break;
case 'i': flags |= LSF_INODE; break;
case 'F': flags |= LSF_FLAG; break;
default:
fprintf(stderr, "Unknown option -%c\n", cp[-1]);
- return;
+ return 1;
}
}
/*
- * If long listing is specified then turn off column listing.
+ * If long or numeric listing is specified then turn off column listing.
*/
- if (flags & LSF_LONG)
+ if (flags & (LSF_LONG | LSF_NUMERIC))
flags &= ~LSF_COLUMN;
/*
if ((flags & LSF_DIR) || !isDirectory(argv[i]))
{
if (!addListName(argv[i]))
- return;
+ return 1;
}
}
* If directories were being listed as themselves, then we are done.
*/
if (flags & LSF_DIR)
- return;
+ return r;
/*
* Now iterate over the file names processing the directories.
if (LSTAT(name, &statBuf) < 0)
{
perror(name);
+ r = 1;
continue;
}
{
closedir(dirp);
- return;
+ return 1;
}
}
listAllFiles(flags, displayWidth);
clearListNames();
}
+
+ return r;
}
int flagChar;
int usedWidth;
char buf[PATH_LEN];
- static char userName[12];
- static int userId;
- static BOOL userIdKnown;
- static char groupName[12];
- static int groupId;
- static BOOL groupIdKnown;
mode = statBuf->st_mode;
}
/*
- * Create the long status line if requested.
+ * Create the long or numeric status line if requested.
*/
- if (flags & LSF_LONG)
+ if (flags & (LSF_LONG | LSF_NUMERIC))
{
strcpy(cp, modeString(mode));
cp += strlen(cp);
- sprintf(cp, "%3d ", statBuf->st_nlink);
+ sprintf(cp, "%3ld ", (long) statBuf->st_nlink);
cp += strlen(cp);
if (!userIdKnown || (statBuf->st_uid != userId))
{
- pwd = getpwuid(statBuf->st_uid);
+ if (flags & LSF_NUMERIC)
+ pwd = 0;
+ else
+ pwd = getpwuid(statBuf->st_uid);
if (pwd)
strcpy(userName, pwd->pw_name);
if (!groupIdKnown || (statBuf->st_gid != groupId))
{
- grp = getgrgid(statBuf->st_gid);
+ if (flags & LSF_NUMERIC)
+ grp = 0;
+ else
+ grp = getgrgid(statBuf->st_gid);
if (grp)
strcpy(groupName, grp->gr_name);
/*
- * Copyright (c) 2002 by David I. Bell
+ * Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
/*
* Local procedures to restore files from a tar file.
*/
-static void readTarFile(int fileCount, const char ** fileTable);
-static void readData(const char * cp, int count);
-static void createPath(const char * name, int mode);
+static BOOL readTarFile(int fileCount, const char ** fileTable);
+static BOOL readData(const char * cp, int count);
+static BOOL createPath(const char * name, int mode);
static long getOctal(const char * cp, int len);
-static void readHeader(const TarHeader * hp,
+static BOOL readHeader(const TarHeader * hp,
int fileCount, const char ** fileTable);
static void writeHeader(const char * fileName,
const struct stat * statbuf);
-static void writeTarFile(int fileCount, const char ** fileTable);
+static BOOL writeTarFile(int fileCount, const char ** fileTable);
static void writeTarBlock(const char * buf, int len);
static BOOL putOctal(char * cp, int len, long value);
-void
+int
do_tar(int argc, const char ** argv)
{
const char * options;
+ BOOL successFlag;
argc--;
argv++;
{
fprintf(stderr, "Too few arguments for tar\n");
- return;
+ return 1;
}
extractFlag = FALSE;
{
fprintf(stderr, "Only one 'f' option allowed\n");
- return;
+ return 1;
}
tarName = *argv++;
default:
fprintf(stderr, "Unknown tar flag '%c'\n", *options);
- return;
+ return 1;
}
}
{
fprintf(stderr, "Exactly one of 'c', 'x' or 't' must be specified\n");
- return;
+ return 1;
}
if (tarName == NULL)
{
fprintf(stderr, "The 'f' flag must be specified\n");
- return;
+ return 1;
}
/*
* command line arguments as the list of files to process.
*/
if (createFlag)
- writeTarFile(argc, argv);
+ successFlag = writeTarFile(argc, argv);
else
- readTarFile(argc, argv);
+ successFlag = readTarFile(argc, argv);
+
+ return !successFlag;
}
/*
* Read a tar file and extract or list the specified files within it.
* If the list is empty than all files are extracted or listed.
+ * Returns TRUE on success.
*/
-static void
+static BOOL
readTarFile(int fileCount, const char ** fileTable)
{
const char * cp;
+ BOOL successFlag;
int cc;
int inCc;
int blockSize;
warnedRoot = FALSE;
eofFlag = FALSE;
inHeader = TRUE;
+ successFlag = TRUE;
+
inCc = 0;
dataCc = 0;
outFd = -1;
{
perror(tarName);
- return;
+ return FALSE;
}
/*
if (inCc < 0)
{
perror(tarName);
+ successFlag = FALSE;
goto done;
}
fprintf(stderr,
"Unexpected end of file from \"%s\"",
tarName);
+ successFlag = FALSE;
goto done;
}
*/
if (inHeader)
{
- readHeader((const TarHeader *) cp, fileCount, fileTable);
+ if (!readHeader((const TarHeader *) cp, fileCount, fileTable))
+ successFlag = FALSE;
cp += TAR_BLOCK_SIZE;
inCc -= TAR_BLOCK_SIZE;
if (cc > dataCc)
cc = dataCc;
- readData(cp, cc);
+ if (!readData(cp, cc))
+ successFlag = FALSE;
/*
* If the amount left isn't an exact multiple of the tar block
* Check for an interrupt.
*/
if (intFlag)
+ {
fprintf(stderr, "Interrupted - aborting\n");
-
+ successFlag = FALSE;
+ }
done:
/*
* Close the tar file if needed.
*/
if ((tarFd >= 0) && (close(tarFd) < 0))
+ {
perror(tarName);
+ successFlag = FALSE;
+ }
/*
* Close the output file if needed.
*/
if (outFd >= 0)
(void) close(outFd);
+
+ return successFlag;
}
/*
* Examine the header block that was just read.
* This can specify the information for another file, or it can mark
- * the end of the tar file.
+ * the end of the tar file. Returns TRUE on success.
*/
-static void
+static BOOL
readHeader(const TarHeader * hp, int fileCount, const char ** fileTable)
{
int mode;
int uid;
int gid;
- int checkSum;
long size;
time_t mtime;
const char * name;
for (cc = TAR_BLOCK_SIZE; cc > 0; cc--)
{
if (*name++)
- return;
+ return TRUE;
}
eofFlag = TRUE;
- return;
+ return TRUE;
}
/*
gid = getOctal(hp->gid, sizeof(hp->gid));
size = getOctal(hp->size, sizeof(hp->size));
mtime = getOctal(hp->mtime, sizeof(hp->mtime));
- checkSum = getOctal(hp->checkSum, sizeof(hp->checkSum));
if ((mode < 0) || (uid < 0) || (gid < 0) || (size < 0))
{
badHeader = TRUE;
- return;
+ return FALSE;
}
badHeader = FALSE;
skipFileFlag = TRUE;
- return;
+ return TRUE;
}
/*
printf("\n");
- return;
+ return TRUE;
}
/*
if (hardLink)
{
if (link(hp->linkName, name) < 0)
+ {
perror(name);
+ return FALSE;
+ }
- return;
+ return TRUE;
}
if (softLink)
{
#ifdef S_ISLNK
if (symlink(hp->linkName, name) < 0)
+ {
perror(name);
+
+ return FALSE;
+ }
+
+ return TRUE;
#else
fprintf(stderr, "Cannot create symbolic links\n");
#endif
- return;
+ return FALSE;
}
/*
* If the file is a directory, then just create the path.
*/
if (S_ISDIR(mode))
- {
- createPath(name, mode);
-
- return;
- }
+ return createPath(name, mode);
/*
* There is a file to write.
* First create the path to it if necessary with a default permission.
*/
- createPath(name, 0777);
+ if (!createPath(name, 0777))
+ return FALSE;
inHeader = (size == 0);
dataCc = size;
perror(name);
skipFileFlag = TRUE;
- return;
+ return FALSE;
}
/*
(void) close(outFd);
outFd = -1;
}
+
+ return TRUE;
}
/*
* Handle a data block of some specified size that was read.
+ * Returns TRUE on success.
*/
-static void
+static BOOL
readData(const char * cp, int count)
{
/*
* skipped then do nothing more.
*/
if (!extractFlag || skipFileFlag)
- return;
+ return TRUE;
/*
* Write the data to the output file.
outFd = -1;
skipFileFlag = TRUE;
- return;
+ return FALSE;
}
/*
perror(outName);
outFd = -1;
+
+ return FALSE;
}
+
+ return TRUE;
}
/*
* Write a tar file containing the specified files.
+ * Returns TRUE on success.
*/
-static void
+static BOOL
writeTarFile(int fileCount, const char ** fileTable)
{
struct stat statbuf;
+ BOOL successFlag;
+ successFlag = TRUE;
errorFlag = FALSE;
/*
{
fprintf(stderr, "No files specified to be saved\n");
- return;
+ return FALSE;
}
/*
{
perror(tarName);
- return;
+ return FALSE;
}
/*
if (fstat(tarFd, &statbuf) < 0)
{
perror(tarName);
+ successFlag = FALSE;
goto done;
}
}
if (intFlag)
+ {
fprintf(stderr, "Interrupted - aborting archiving\n");
+ successFlag = FALSE;
+ }
/*
* Now write an empty block of zeroes to end the archive.
* Close the tar file and check for errors if it was opened.
*/
if ((tarFd >= 0) && (close(tarFd) < 0))
+ {
perror(tarName);
+ successFlag = FALSE;
+ }
+
+ return successFlag;
}
* the final component. The mode is given for the final directory only,
* while all previous ones get default protections. Errors are not reported
* here, as failures to restore files can be reported later.
+ * Returns TRUE on success.
*/
-static void
+static BOOL
createPath(const char * name, int mode)
{
char * cp;
*cpOld = '/';
}
+
+ return TRUE;
}
/*
- * Copyright (c) 2002 by David I. Bell
+ * Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
#include <linux/fs.h>
#endif
+/* Need to tell loop.h what the actual dev_t type is. */
+#undef dev_t
+#if defined(__alpha) || (defined(__sparc__) && defined(__arch64__))
+#define dev_t unsigned int
+#else
+#define dev_t unsigned short
+#endif
+#include <linux/loop.h>
+#undef dev_t
+#define dev_t dev_t
+
-void
+int
do_echo(int argc, const char ** argv)
{
BOOL first;
}
fputc('\n', stdout);
+
+ return 0;
}
-void
+int
do_pwd(int argc, const char ** argv)
{
char buf[PATH_LEN];
{
fprintf(stderr, "Cannot get current directory\n");
- return;
+ return 1;
}
printf("%s\n", buf);
+
+ return 0;
}
-void
+int
do_cd(int argc, const char ** argv)
{
const char * path;
{
fprintf(stderr, "No HOME environment variable\n");
- return;
+ return 1;
}
}
if (chdir(path) < 0)
+ {
perror(path);
+
+ return 1;
+ }
+
+ return 0;
}
-void
+int
do_mkdir(int argc, const char ** argv)
{
+ int r = 0;
+
while (argc-- > 1)
{
if (mkdir(argv[1], 0777) < 0)
+ {
perror(argv[1]);
+ r = 1;
+ }
argv++;
}
+
+ return r;
}
-void
+int
do_mknod(int argc, const char ** argv)
{
const char * cp;
{
fprintf(stderr, "Bad device type\n");
- return;
+ return 1;
}
major = 0;
{
fprintf(stderr, "Bad major number\n");
- return;
+ return 1;
}
minor = 0;
{
fprintf(stderr, "Bad minor number\n");
- return;
+ return 1;
}
if (mknod(argv[1], mode, major * 256 + minor) < 0)
+ {
perror(argv[1]);
+
+ return 1;
+ }
+
+ return 0;
}
-void
+#if HAVE_LINUX_PIVOT
+
+int
+do_pivot_root(int argc, const char ** argv)
+{
+ if (pivot_root(argv[1], argv[2]) < 0)
+ {
+ perror("pivot_root");
+
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
+
+
+#if HAVE_LINUX_CHROOT
+
+int
+do_chroot(int argc, const char ** argv)
+{
+ if (chroot(argv[1]) < 0)
+ {
+ perror("chroot");
+
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
+
+
+int
do_rmdir(int argc, const char ** argv)
{
+ int r = 0;
+
while (argc-- > 1)
{
if (rmdir(argv[1]) < 0)
+ {
perror(argv[1]);
+ r = 1;
+ }
argv++;
}
+
+ return r;
}
-void
+int
do_sync(int argc, const char ** argv)
{
sync();
+
+ return 0;
}
-void
+int
do_rm(int argc, const char ** argv)
{
+ int r = 0;
+
while (argc-- > 1)
{
if (unlink(argv[1]) < 0)
+ {
perror(argv[1]);
+ r = 1;
+ }
argv++;
}
+
+ return r;
}
-void
+int
do_chmod(int argc, const char ** argv)
{
const char * cp;
int mode;
+ int r;
+ r = 0;
mode = 0;
cp = argv[1];
{
fprintf(stderr, "Mode must be octal\n");
- return;
+ return 1;
}
argc--;
while (argc-- > 1)
{
if (chmod(argv[1], mode) < 0)
+ {
perror(argv[1]);
+ r = 1;
+ }
argv++;
}
+
+ return r;
}
-void
+int
do_chown(int argc, const char ** argv)
{
const char * cp;
int uid;
struct passwd * pwd;
struct stat statBuf;
+ int r;
+ r = 0;
cp = argv[1];
if (isDecimal(*cp))
{
fprintf(stderr, "Bad uid value\n");
- return;
+ return 1;
}
- } else {
+ }
+ else
+ {
pwd = getpwnam(cp);
if (pwd == NULL)
{
fprintf(stderr, "Unknown user name\n");
- return;
+ return 1;
}
uid = pwd->pw_uid;
(chown(*argv, uid, statBuf.st_gid) < 0))
{
perror(*argv);
+ r = 1;
}
}
+
+ return r;
}
-void
+int
do_chgrp(int argc, const char ** argv)
{
const char * cp;
int gid;
struct group * grp;
struct stat statBuf;
+ int r;
+ r = 0;
cp = argv[1];
if (isDecimal(*cp))
{
fprintf(stderr, "Bad gid value\n");
- return;
+ return 1;
}
}
else
{
fprintf(stderr, "Unknown group name\n");
- return;
+ return 1;
}
gid = grp->gr_gid;
(chown(*argv, statBuf.st_uid, gid) < 0))
{
perror(*argv);
+ r = 1;
}
}
+
+ return r;
}
-void
+int
do_touch(int argc, const char ** argv)
{
const char * name;
int fd;
struct utimbuf now;
+ int r;
+ r = 0;
time(&now.actime);
now.modtime = now.actime;
continue;
}
+ if (errno != EEXIST)
+ {
+ perror(name);
+ r = 1;
+
+ continue;
+ }
+
if (utime(name, &now) < 0)
+ {
perror(name);
+ r = 1;
+ }
}
+
+ return r;
}
-void
+int
do_mv(int argc, const char ** argv)
{
const char * srcName;
const char * destName;
const char * lastArg;
BOOL dirFlag;
+ int r;
+ r = 0;
lastArg = argv[argc - 1];
dirFlag = isDirectory(lastArg);
{
fprintf(stderr, "%s: not a directory\n", lastArg);
- return;
+ return 1;
}
while (!intFlag && (argc-- > 2))
if (access(srcName, 0) < 0)
{
perror(srcName);
+ r = 1;
continue;
}
if (errno != EXDEV)
{
perror(destName);
+ r = 1;
continue;
}
if (!copyFile(srcName, destName, TRUE))
+ {
+ r = 1;
+
continue;
+ }
if (unlink(srcName) < 0)
+ {
perror(srcName);
+ r = 1;
+ }
}
+
+ return r;
}
-void
+int
do_ln(int argc, const char ** argv)
{
const char * srcName;
const char * destName;
const char * lastArg;
BOOL dirFlag;
+ int r;
+
+ r = 0;
if (argv[1][0] == '-')
{
{
fprintf(stderr, "Unknown option\n");
- return;
+ return 1;
}
if (argc != 4)
{
fprintf(stderr, "Wrong number of arguments for symbolic link\n");
- return;
+ return 1;
}
#ifdef S_ISLNK
if (symlink(argv[2], argv[3]) < 0)
+ {
perror(argv[3]);
+
+ return 1;
+ }
+
+ return 0;
#else
fprintf(stderr, "Symbolic links are not allowed\n");
+
+ return 1;
#endif
- return;
}
/*
{
fprintf(stderr, "%s: not a directory\n", lastArg);
- return;
+ return 1;
}
while (argc-- > 2)
if (access(srcName, 0) < 0)
{
perror(srcName);
+ r = 1;
continue;
}
if (link(srcName, destName) < 0)
{
perror(destName);
+ r = 1;
continue;
}
}
+
+ return r;
}
-void
+int
do_cp(int argc, const char ** argv)
{
const char * srcName;
const char * destName;
const char * lastArg;
BOOL dirFlag;
+ int r;
+ r = 0;
lastArg = argv[argc - 1];
dirFlag = isDirectory(lastArg);
{
fprintf(stderr, "%s: not a directory\n", lastArg);
- return;
+ return 1;
}
while (!intFlag && (argc-- > 2))
if (dirFlag)
destName = buildName(destName, srcName);
- (void) copyFile(srcName, destName, FALSE);
+ if (!copyFile(srcName, destName, FALSE))
+ r = 1;
}
+
+ return r;
}
-void
+int
do_mount(int argc, const char ** argv)
{
const char * str;
{
fprintf(stderr, "Missing file system type\n");
- return;
+ return 1;
}
type = *argv++;
default:
fprintf(stderr, "Unknown option\n");
- return;
+ return 1;
}
}
{
fprintf(stderr, "Wrong number of arguments for mount\n");
- return;
+ return 1;
}
#if HAVE_LINUX_MOUNT
- if (mount(argv[0], argv[1], type, flags, 0) < 0)
- perror("mount failed");
-
+ if (mount(argv[0], argv[1], type, flags, 0) < 0)
+ {
+ perror("mount failed");
+ return 1;
+ }
#elif HAVE_BSD_MOUNT
{
struct ufs_args ufs;
struct msdosfs_args msdosfs;
void * args;
- if(!strcmp(type, "ffs") || !strcmp(type, "ufs")) {
+ if (!strcmp(type, "ffs") || !strcmp(type, "ufs"))
+ {
ufs.fspec = (char*) argv[0];
args = &ufs;
- } else if(!strcmp(type, "adosfs")) {
+ }
+ else if (!strcmp(type, "adosfs"))
+ {
adosfs.fspec = (char*) argv[0];
adosfs.uid = 0;
adosfs.gid = 0;
args = &adosfs;
- } else if(!strcmp(type, "cd9660")) {
+ }
+ else if (!strcmp(type, "cd9660"))
+ {
iso.fspec = (char*) argv[0];
args = &iso;
- } else if(!strcmp(type, "mfs")) {
+ }
+ else if (!strcmp(type, "mfs"))
+ {
mfs.fspec = (char*) argv[0];
args = &mfs;
- } else if(!strcmp(type, "msdos")) {
+ }
+ else if (!strcmp(type, "msdos"))
+ {
msdosfs.fspec = (char*) argv[0];
msdosfs.uid = 0;
msdosfs.gid = 0;
args = &msdosfs;
- } else {
+ }
+ else
+ {
fprintf(stderr, "Unknown filesystem type: %s", type);
fprintf(stderr,
"Supported: ffs ufs adosfs cd9660 mfs msdos\n");
- return;
+
+ return 1;
}
if (mount(type, argv[1], flags, args) < 0)
- perror(argv[0]);
+ {
+ perror(argv[0]);
+
+ return 1;
+ }
}
#endif
+ return 0;
}
-void
+int
do_umount(int argc, const char ** argv)
{
#if HAVE_LINUX_MOUNT
if (umount(argv[1]) < 0)
+ {
perror(argv[1]);
+
+ return 1;
+ }
#elif HAVE_BSD_MOUNT
{
const char * str;
int flags = 0;
for (argc--, argv++;
- (argc > 0) && (**argv == '-');) {
+ (argc > 0) && (**argv == '-');)
+ {
argc--;
str = *argv++;
- while (*++str) {
+ while (*++str)
+{
switch (*str)
{
case 'f':
}
if (unmount(argv[0], flags) < 0)
+ {
perror(argv[0]);
+
+ return 1;
+ }
}
#endif
+ return 0;
}
-void
+int
do_cmp(int argc, const char ** argv)
{
int fd1;
char buf2[BUF_SIZE];
struct stat statBuf1;
struct stat statBuf2;
+ int r;
+
+ r = 0;
if (stat(argv[1], &statBuf1) < 0)
{
perror(argv[1]);
- return;
+ return 1;
}
if (stat(argv[2], &statBuf2) < 0)
{
perror(argv[2]);
- return;
+ return 1;
}
if ((statBuf1.st_dev == statBuf2.st_dev) &&
{
printf("Files are links to each other\n");
- return;
+ return 0;
}
if (statBuf1.st_size != statBuf2.st_size)
{
printf("Files are different sizes\n");
- return;
+ return 1;
}
fd1 = open(argv[1], O_RDONLY);
{
perror(argv[1]);
- return;
+ return 1;
}
fd2 = open(argv[2], O_RDONLY);
perror(argv[2]);
close(fd1);
- return;
+ return 1;
}
pos = 0;
if (cc1 < 0)
{
perror(argv[1]);
+ r = 1;
goto closefiles;
}
if (cc2 < 0)
{
perror(argv[2]);
+ r = 1;
goto closefiles;
}
if ((cc1 == 0) && (cc2 == 0))
{
printf("Files are identical\n");
+ r = 0;
goto closefiles;
}
if (cc1 < cc2)
{
printf("First file is shorter than second\n");
+ r = 1;
goto closefiles;
}
if (cc1 > cc2)
{
printf("Second file is shorter than first\n");
+ r = 1;
goto closefiles;
}
pos++;
printf("Files differ at byte position %ld\n", pos);
+ r = 1;
goto closefiles;
}
closefiles:
close(fd1);
close(fd2);
+
+ return r;
}
-void
+int
do_more(int argc, const char ** argv)
{
FILE * fp;
{
perror(name);
- return;
+ return 1;
}
printf("<< %s >>\n", name);
if (fp)
fclose(fp);
- return;
+ return 0;
}
ch = buf[0];
case 'q':
fclose(fp);
- return;
+ return 0;
}
col = 0;
if (fp)
fclose(fp);
}
+
+ return 0;
}
-void
+int
do_sum(int argc, const char ** argv)
{
const char * name;
int i;
unsigned long checksum;
char buf[BUF_SIZE];
+ int r;
argc--;
argv++;
+ r = 0;
while (argc-- > 0)
{
if (fd < 0)
{
perror(name);
+ r = 1;
continue;
}
if (cc < 0)
{
perror(name);
+ r = 1;
(void) close(fd);
printf("%05lu %s\n", checksum, name);
}
+
+ return r;
}
-void
+int
do_exit(int argc, const char ** argv)
{
+ int r = 0;
+
if (getpid() == 1)
{
fprintf(stderr, "You are the INIT process!\n");
- return;
+ return 1;
+ }
+
+ if (argc == 2)
+ {
+ r = atoi(argv[1]);
}
- exit(0);
+ exit(r);
+
+ return 1;
}
-void
+int
do_setenv(int argc, const char ** argv)
{
const char * name;
{
fprintf(stderr, "Cannot allocate memory\n");
- return;
+ return 1;
}
strcpy(str, name);
strcat(str, value);
putenv(str);
+
+ return 0;
}
-void
+int
do_printenv(int argc, const char ** argv)
{
const char ** env;
while (*env)
printf("%s\n", *env++);
- return;
+ return 0;
}
len = strlen(argv[1]);
{
printf("%s\n", &env[0][len+1]);
- return;
+ return 0;
}
env++;
}
+
+ return 0;
}
-void
+int
do_umask(int argc, const char ** argv)
{
const char * cp;
umask(mask);
printf("%03o\n", mask);
- return;
+ return 0;
}
mask = 0;
{
fprintf(stderr, "Bad umask value\n");
- return;
+ return 1;
}
umask(mask);
+
+ return 0;
}
-void
+int
do_kill(int argc, const char ** argv)
{
const char * cp;
int sig;
int pid;
+ int r;
+ r = 0;
sig = SIGTERM;
if (argv[1][0] == '-')
{
fprintf(stderr, "Unknown signal\n");
- return;
+ return 1;
}
}
{
fprintf(stderr, "Non-numeric pid\n");
- return;
+ return 1;
}
if (kill(pid, sig) < 0)
+ {
perror(*argv);
+ r = 1;
+ }
}
+
+ return r;
}
-void
+int
do_where(int argc, const char ** argv)
{
const char * program;
char * endPath;
char * fullPath;
BOOL found;
+ int r;
found = FALSE;
program = argv[1];
{
fprintf(stderr, "Program name cannot include a path\n");
- return;
+ return 1;
}
path = getenv("PATH");
{
fprintf(stderr, "Memory allocation failed\n");
- return;
+ return 1;
}
/*
if (access(fullPath, X_OK) < 0)
{
if (errno != ENOENT)
- printf("%s: %s\n", fullPath, strerror(errno));
+ {
+ perror(fullPath);
+ r = 1;
+ }
continue;
}
}
if (!found)
+ {
printf("Program \"%s\" not found in PATH\n", program);
+ r = 1;
+ }
+
+ return r;
}
+#if HAVE_LINUX_LOSETUP
+
+int
+do_losetup(int argc, const char ** argv)
+{
+ int loopfd;
+ int targfd;
+ struct loop_info loopInfo;
+
+ if (!strcmp(argv[1], "-d"))
+ {
+ loopfd = open(argv[2], O_RDWR);
+
+ if (loopfd < 0)
+ {
+ fprintf(stderr, "Error opening %s: %s\n", argv[2],
+ strerror(errno));
+
+ return 1;
+ }
+
+ if (ioctl(loopfd, LOOP_CLR_FD, 0))
+ {
+ fprintf(stderr, "Error unassociating device: %s\n",
+ strerror(errno));
+
+ return 1;
+ }
+ }
+
+ loopfd = open(argv[1], O_RDWR);
+
+ if (loopfd < 0)
+ {
+ fprintf(stderr, "Error opening %s: %s\n", argv[1],
+ strerror(errno));
+
+ return 1;
+ }
+
+ targfd = open(argv[2], O_RDWR);
+
+ if (targfd < 0)
+ {
+ fprintf(stderr, "Error opening %s: %s\n", argv[2],
+ strerror(errno));
+
+ return 1;
+ }
+
+ if (ioctl(loopfd, LOOP_SET_FD, targfd))
+ {
+ fprintf(stderr, "Error setting up loopback device: %s\n",
+ strerror(errno));
+
+ return 1;
+ }
+
+ memset(&loopInfo, 0, sizeof(loopInfo));
+ strcpy(loopInfo.lo_name, argv[2]);
+
+ if (ioctl(loopfd, LOOP_SET_STATUS, &loopInfo))
+ {
+ fprintf(stderr, "Error setting up loopback device: %s\n",
+ strerror(errno));
+
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
+
/* END CODE */
These built-in commands are:
.PP
.nf
- -ar, -chattr, -chgrp, -chmod, -chown, -cmp, -cp,
- -dd, -echo, -ed, -grep, -file, -find, -gunzip,
- -gzip, -kill, -ln, -ls, -lsattr, -mkdir, -mknod,
- -more, -mount, -mv, -printenv, -pwd, -rm, -rmdir,
- -sum, -sync, -tar, -touch, -umount, -where
+ -ar, -chattr, -chgrp, -chmod, -chown, -chroot, -cmp,
+ -cp, -dd, -echo, -ed, -grep, -file, -find, -gunzip,
+ -gzip, -kill, -losetup, -ln, -ls, -lsattr, -mkdir,
+ -mknod, -more, -mount, -mv, -pivot_root, -printenv, -pwd,
+ -rm, -rmdir, -sum, -sync, -tar, -touch, -umount, -where
.fi
.PP
These commands are generally similar to the standard programs with similar
can
either be a user name, or a decimal value.
.TP
+.B -chroot path
+Changes the root directory to that specified in
+.I path.
+This directory
+will be used for path names beginning with /. The root directory is
+inherited by all children of the current process.
+.TP
.B -cmp fileName1 fileName2
Determines whether or not the specified file names have identical data.
This says that the files are links to each other, are different sizes,
QUIT, KILL, TERM, STOP, CONT, USR1 or USR2.
If no signal is specified then SIGTERM is used.
.TP
+.B -losetup [-d] loopDev [file]
+Associates loopback devices with files on the system. If
+.I -d
+is not given,
+the loopback device
+.I loopDev
+is associated with
+.I file.
+If
+.I -d
+is given,
+.I loopDev
+is unassociated with the file it's currently configured for.
+.TP
.B -ln [-s] srcName ... destName
Links one or more files from the
.I srcName
.B -ls [-lidFC] fileName ...
Display information about the specified list of file names.
The normal listing is simply a list of file names, one per line.
-The options available are -l, -i, -d, and -F.
+The options available are -l, -n, -i, -d, and -F.
The -l option produces a long listing giving the normal 'ls' information.
+The -n option is like -l except that numeric user and group ids are shown.
The -i option displays the inode numbers of the files.
The -d option displays information about a directory, instead of the
files within it.
this fails because of the files being on different filesystems,
then copies and deletes are done instead.
.TP
+.B -pivot_root newRoot putOld
+Moves the root file system of the current process to the directory
+.I putOld
+and makes
+.I newRoot
+the new root file system of the current process.
+.TP
.B -printenv [name]
If
.I name
.SH WARNINGS
.B Sash
should obviously be linked statically, otherwise its purpose is lost.
+Note that even if the rest of the program is linked statically, the
+password and group lookup routines in the C library can still be dynamic.
+For that reason, if there are problems then it might be necessary to
+only use numeric ids for the -chown and -chgrp commands and to use
+the -n option instead of -l for the -ls command.
.PP
Several other system commands might be necessary for system recovery,
but aren't built-in to
.SH AUTHOR
.nf
David I. Bell
-dbell@canb.auug.org.au
-12 January 2004
+dbell@tip.net.au
+5 March 2014
.fi
/*
- * Copyright (c) 2004 by David I. Bell
+ * Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
#include "sash.h"
-static const char * const version = "3.7";
+static const char * const version = "3.8";
/*
typedef struct
{
const char * name;
- void (*func)(int argc, const char ** argv);
+ int (*func)(int argc, const char ** argv);
int minArgs;
int maxArgs;
const char * description;
"srcName ... destName"
},
+#ifdef HAVE_LINUX_CHROOT
+ {
+ "-chroot", do_chroot, 2, 2,
+ "change root file system",
+ "new_root_dir"
+ },
+#endif
+
{
"-dd", do_dd, 3, INFINITE_ARGS,
"Copy data between two files",
},
{
- "exit", do_exit, 1, 1,
+ "exit", do_exit, 1, 2,
"Exit from sash",
- ""
+ "[exit value]"
},
{
"[-sig] pid ..."
},
+#ifdef HAVE_LINUX_LOSETUP
+ {
+ "-losetup", do_losetup, 3, 3,
+ "Associate a loopback device with a file",
+ "[-d] device\n -losetup device filename"
+ },
+#endif
+
{
"-ln", do_ln, 3, INFINITE_ARGS,
"Link one fileName to another",
"srcName ... destName"
},
+#ifdef HAVE_LINUX_PIVOT
+ {
+ "-pivot_root", do_pivot_root, 3, 3,
+ "pivot the root file system",
+ "new_dir old_dir"
+ },
+#endif
+
{
"-printenv", do_printenv, 1, 2,
"Print environment variables",
*/
static void catchInt(int);
static void catchQuit(int);
-static void readFile(const char * name);
-static void command(const char * cmd);
+static int readFile(const char * name);
+static int command(const char * cmd);
static BOOL tryBuiltIn(const char * cmd);
-static void runCmd(const char * cmd);
+static int runCmd(const char * cmd);
static void childProcess(const char * cmd);
static void showPrompt(void);
static void usage(void);
static Alias * findAlias(const char * name);
+static void expandVariable(char * name);
/*
const char * commandFile;
BOOL quietFlag;
BOOL aliasFlag;
+ BOOL interactiveFlag;
char buf[PATH_LEN];
singleCommand = NULL;
commandFile = NULL;
quietFlag = FALSE;
aliasFlag = FALSE;
+ interactiveFlag = FALSE;
/*
* Look for options.
while (*cp) switch (*cp++)
{
+ case '-':
+ /*
+ * Ignore. This is so that we can be
+ * run from login.
+ */
+ break;
+
case 'c':
/*
* Execute specified command.
*/
- if ((argc != 1) || singleCommand)
+ if ((argc != 1) || singleCommand || interactiveFlag)
usage();
singleCommand = *argv++;
break;
+ case 'i':
+ /*
+ * Be an interactive shell
+ * ..is a no-op, but some contexts require this
+ * ..interactiveFlag is to avoid -ic as a legacy
+ */
+ if (singleCommand)
+ usage();
+
+ interactiveFlag = TRUE;
+ break;
+
case 'p':
/*
* Set the prompt string.
*/
if (singleCommand)
{
- command(singleCommand);
-
- return 0;
+ return command(singleCommand);
}
/*
/*
* Read commands from stdin or from a command file.
*/
- readFile(commandFile);
+ return readFile(commandFile);
- return 0;
}
* Read commands from the specified file.
* A null name pointer indicates to read from stdin.
*/
-static void
+static int
readFile(const char * name)
{
FILE * fp;
int cc;
BOOL ttyFlag;
char buf[CMD_LEN];
+ int r = 0;
if (sourceCount >= MAX_SOURCE)
{
fprintf(stderr, "Too many source files\n");
- return;
+ return 1;
}
fp = stdin;
{
perror(name);
- return;
+ return 1;
}
}
fclose(fp);
sourceCount--;
- return;
+ return 1;
}
if (fgets(buf, CMD_LEN - 1, fp) == NULL)
buf[cc] = '\0';
- command(buf);
+ r = command(buf);
}
if (ferror(fp))
fclose(fp);
sourceCount--;
+
+ return r;
}
* This breaks the command line up into words, checks to see if the
* command is an alias, and expands wildcards.
*/
-static void
+static int
command(const char * cmd)
{
const char * endCmd;
* If the command is empty or is a comment then ignore it.
*/
if ((*cmd == '\0') || (*cmd == '#'))
- return;
+ return 0;
/*
* Look for the end of the command name and then copy the
cmd = newCommand;
}
+ /*
+ * Expand simple environment variables
+ */
+ while (strstr(cmd, "$(")) expandVariable((char *)cmd);
+
/*
* Now look for the command in the builtin table, and execute
* the command if found.
*/
if (tryBuiltIn(cmd))
- return;
+ return 0; /* This is a blatant lie */
/*
* The command is not a built-in, so run the program along
* the PATH list.
*/
- runCmd(cmd);
+ return runCmd(cmd);
}
/*
* Execute the specified command either by forking and executing
- * the program ourself, or else by using the shell.
+ * the program ourself, or else by using the shell. Returns the
+ * exit status, or -1 if the program cannot be executed at all.
*/
-static void
+static int
runCmd(const char * cmd)
{
const char * cp;
* command using the shell.
*/
if (magic)
- {
- system(cmd);
-
- return;
- }
+ return trySystem(cmd);
/*
* No magic characters were in the command, so we can do the fork
{
perror("fork failed");
- return;
+ return -1;
}
/*
{
fprintf(stderr, "Error from waitpid: %s", strerror(errno));
- return;
+ return -1;
}
if (WIFSIGNALED(status))
{
fprintf(stderr, "pid %ld: killed by signal %d\n",
(long) pid, WTERMSIG(status));
+
+ return -1;
}
+
+ return WEXITSTATUS(status);
}
*/
if (!makeArgs(cmd, &argc, &argv))
{
- system(cmd);
- exit(0);
+ int status = trySystem(cmd);
+
+ if (status == -1)
+ exit(99);
+
+ exit(status);
}
/*
*/
if (errno == ENOEXEC)
{
- system(cmd);
- exit(0);
+ int status = trySystem(cmd);
+
+ if (status == -1)
+ exit(99);
+
+ exit(status);
}
/*
}
-void
+int
do_help(int argc, const char ** argv)
{
const CommandEntry * entry;
printf("usage: %s %s\n", entry->name,
entry->usage);
- return;
+ return 0;
}
}
}
printf("%-10s %s\n", entry->name, entry->usage);
}
}
+
+ return 0;
}
-void
+int
do_alias(int argc, const char ** argv)
{
const char * name;
for (alias = aliasTable; count-- > 0; alias++)
printf("%s\t%s\n", alias->name, alias->value);
- return;
+ return 0;
}
name = argv[1];
if (alias)
printf("%s\n", alias->value);
else
+ {
fprintf(stderr, "Alias \"%s\" is not defined\n", name);
- return;
+ return 1;
+ }
+
+ return 0;
}
if (strcmp(name, "alias") == 0)
{
fprintf(stderr, "Cannot alias \"alias\"\n");
- return;
+ return 1;
}
if (!makeString(argc - 2, argv + 2, buf, CMD_LEN))
- return;
+ return 1;
value = malloc(strlen(buf) + 1);
{
fprintf(stderr, "No memory for alias value\n");
- return;
+ return 1;
}
strcpy(value, buf);
free(alias->value);
alias->value = value;
- return;
+ return 0;
}
if ((aliasCount % ALIAS_ALLOC) == 0)
free(value);
fprintf(stderr, "No memory for alias table\n");
- return;
+ return 1;
}
aliasTable = alias;
free(value);
fprintf(stderr, "No memory for alias name\n");
- return;
+ return 1;
}
strcpy(alias->name, name);
alias->value = value;
aliasCount++;
+
+ return 0;
}
* Build aliases for all of the built-in commands which start with a dash,
* using the names without the dash.
*/
-void
+int
do_aliasall(int argc, const char **argv)
{
const CommandEntry * entry;
do_alias(3, newArgv);
}
+
+ return 0;
}
}
-void
+int
do_source(int argc, const char ** argv)
{
- readFile(argv[1]);
+ return readFile(argv[1]);
}
-void
+int
do_exec(int argc, const char ** argv)
{
const char * name;
name = argv[1];
- if (access(name, 4))
- {
- perror(name);
-
- return;
- }
-
while (--sourceCount >= 0)
{
if (sourcefiles[sourceCount] != stdin)
argv[argc] = NULL;
execvp(name, (char **) argv + 1);
- exit(1);
+ perror(name);
+
+ return 1;
}
-void
+int
do_prompt(int argc, const char ** argv)
{
char * cp;
char buf[CMD_LEN];
if (!makeString(argc - 1, argv + 1, buf, CMD_LEN))
- return;
+ return 1;
cp = malloc(strlen(buf) + 2);
{
fprintf(stderr, "No memory for prompt\n");
- return;
+ return 1;
}
strcpy(cp, buf);
free(prompt);
prompt = cp;
+
+ return 0;
}
-void
+int
do_unalias(int argc, const char ** argv)
{
Alias * alias;
alias->name = aliasTable[aliasCount].name;
alias->value = aliasTable[aliasCount].value;
}
+
+ return 0;
}
if (prompt)
cp = prompt;
- write(STDOUT, cp, strlen(cp));
+ tryWrite(STDOUT, cp, strlen(cp));
}
intFlag = TRUE;
if (intCrlf)
- write(STDOUT, "\n", 1);
+ tryWrite(STDOUT, "\n", 1);
}
intFlag = TRUE;
if (intCrlf)
- write(STDOUT, "\n", 1);
+ tryWrite(STDOUT, "\n", 1);
}
{
fprintf(stderr, "Stand-alone shell (version %s)\n", version);
fprintf(stderr, "\n");
- fprintf(stderr, "Usage: sash [-a] [-q] [-f fileName] [-c command] [-p prompt]\n");
+ fprintf(stderr, "Usage: sash [-a] [-q] [-f fileName] [-c command] [-p prompt] [-i]\n");
exit(1);
}
+
+/*
+ * Expand one environment variable: Syntax $(VAR)
+ */
+static void
+expandVariable(char * cmd)
+{
+ char tmp[CMD_LEN];
+ char *cp;
+ char *ep;
+
+ strcpy(tmp, cmd);
+ cp = strstr(tmp, "$(");
+ if (cp) {
+ *cp++ = '\0';
+ strcpy(cmd, tmp);
+ ep = ++cp;
+ while (*ep && (*ep != ')')) ep++;
+ if (*ep == ')') *ep++ = '\0';
+ cp = getenv(cp);
+ if (cp) strcat(cmd, cp);
+ strcat(cmd, ep);
+ }
+ return;
+}
+
/* END CODE */
/*
- * Copyright (c) 2002 by David I. Bell
+ * Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
/*
* Built-in command functions.
*/
-extern void do_alias(int argc, const char ** argv);
-extern void do_aliasall(int argc, const char ** argv);
-extern void do_cd(int argc, const char ** argv);
-extern void do_exec(int argc, const char ** argv);
-extern void do_exit(int argc, const char ** argv);
-extern void do_prompt(int argc, const char ** argv);
-extern void do_source(int argc, const char ** argv);
-extern void do_umask(int argc, const char ** argv);
-extern void do_unalias(int argc, const char ** argv);
-extern void do_help(int argc, const char ** argv);
-extern void do_ln(int argc, const char ** argv);
-extern void do_cp(int argc, const char ** argv);
-extern void do_mv(int argc, const char ** argv);
-extern void do_rm(int argc, const char ** argv);
-extern void do_chmod(int argc, const char ** argv);
-extern void do_mkdir(int argc, const char ** argv);
-extern void do_rmdir(int argc, const char ** argv);
-extern void do_mknod(int argc, const char ** argv);
-extern void do_chown(int argc, const char ** argv);
-extern void do_chgrp(int argc, const char ** argv);
-extern void do_sum(int argc, const char ** argv);
-extern void do_sync(int argc, const char ** argv);
-extern void do_printenv(int argc, const char ** argv);
-extern void do_more(int argc, const char ** argv);
-extern void do_cmp(int argc, const char ** argv);
-extern void do_touch(int argc, const char ** argv);
-extern void do_ls(int argc, const char ** argv);
-extern void do_dd(int argc, const char ** argv);
-extern void do_tar(int argc, const char ** argv);
-extern void do_ar(int argc, const char ** argv);
-extern void do_mount(int argc, const char ** argv);
-extern void do_umount(int argc, const char ** argv);
-extern void do_setenv(int argc, const char ** argv);
-extern void do_pwd(int argc, const char ** argv);
-extern void do_echo(int argc, const char ** argv);
-extern void do_kill(int argc, const char ** argv);
-extern void do_grep(int argc, const char ** argv);
-extern void do_file(int argc, const char ** argv);
-extern void do_find(int argc, const char ** argv);
-extern void do_ed(int argc, const char ** argv);
-extern void do_where(int argc, const char ** argv);
+extern int do_alias(int argc, const char ** argv);
+extern int do_aliasall(int argc, const char ** argv);
+extern int do_cd(int argc, const char ** argv);
+extern int do_exec(int argc, const char ** argv);
+extern int do_exit(int argc, const char ** argv);
+extern int do_prompt(int argc, const char ** argv);
+extern int do_source(int argc, const char ** argv);
+extern int do_umask(int argc, const char ** argv);
+extern int do_unalias(int argc, const char ** argv);
+extern int do_help(int argc, const char ** argv);
+extern int do_ln(int argc, const char ** argv);
+extern int do_cp(int argc, const char ** argv);
+extern int do_mv(int argc, const char ** argv);
+extern int do_rm(int argc, const char ** argv);
+extern int do_chmod(int argc, const char ** argv);
+extern int do_mkdir(int argc, const char ** argv);
+extern int do_rmdir(int argc, const char ** argv);
+extern int do_mknod(int argc, const char ** argv);
+extern int do_chown(int argc, const char ** argv);
+extern int do_chgrp(int argc, const char ** argv);
+extern int do_sum(int argc, const char ** argv);
+extern int do_sync(int argc, const char ** argv);
+extern int do_printenv(int argc, const char ** argv);
+extern int do_more(int argc, const char ** argv);
+extern int do_cmp(int argc, const char ** argv);
+extern int do_touch(int argc, const char ** argv);
+extern int do_ls(int argc, const char ** argv);
+extern int do_dd(int argc, const char ** argv);
+extern int do_tar(int argc, const char ** argv);
+extern int do_ar(int argc, const char ** argv);
+extern int do_mount(int argc, const char ** argv);
+extern int do_umount(int argc, const char ** argv);
+extern int do_setenv(int argc, const char ** argv);
+extern int do_pwd(int argc, const char ** argv);
+extern int do_echo(int argc, const char ** argv);
+extern int do_kill(int argc, const char ** argv);
+extern int do_grep(int argc, const char ** argv);
+extern int do_file(int argc, const char ** argv);
+extern int do_find(int argc, const char ** argv);
+extern int do_ed(int argc, const char ** argv);
+extern int do_where(int argc, const char ** argv);
#if HAVE_GZIP
-extern void do_gzip(int argc, const char ** argv);
-extern void do_gunzip(int argc, const char ** argv);
+extern int do_gzip(int argc, const char ** argv);
+extern int do_gunzip(int argc, const char ** argv);
#endif
#if HAVE_LINUX_ATTR
-extern void do_lsattr(int argc, const char ** argv);
-extern void do_chattr(int argc, const char ** argv);
+extern int do_lsattr(int argc, const char ** argv);
+extern int do_chattr(int argc, const char ** argv);
+#endif
+
+#if HAVE_LINUX_CHROOT
+extern int do_chroot(int argc, const char ** argv);
+#endif
+
+#if HAVE_LINUX_LOSETUP
+extern int do_losetup(int argc, const char ** argv);
+#endif
+
+#if HAVE_LINUX_PIVOT
+extern int do_pivot_root(int argc, const char ** argv);
+extern int pivot_root(const char *new_root, const char *put_old);
#endif
extern char * getChunk(int size);
extern char * chunkstrdup(const char *);
extern void freeChunks(void);
+extern int trySystem(const char * cmd);
+extern void tryWrite(int fd, const char * buf, int len);
extern int fullWrite(int fd, const char * buf, int len);
extern int fullRead(int fd, char * buf, int len);
+extern void checkStatus(const char * name, int status);
extern BOOL match(const char * text, const char * pattern);
extern const char * buildName
/*
- * Copyright (c) 2002 by David I. Bell
+ * Copyright (c) 2014 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
goto error_exit;
}
- (void) close(rfd);
+ checkStatus("close", close(rfd));
if (close(wfd) < 0)
{
if (setModes)
{
- (void) chmod(destName, statBuf1.st_mode);
+ checkStatus("chmod", chmod(destName, statBuf1.st_mode));
- (void) chown(destName, statBuf1.st_uid, statBuf1.st_gid);
+ checkStatus("chown", chown(destName, statBuf1.st_uid, statBuf1.st_gid));
times.actime = statBuf1.st_atime;
times.modtime = statBuf1.st_mtime;
- (void) utime(destName, ×);
+ checkStatus("utime", utime(destName, ×));
}
return TRUE;
}
+/*
+ * Try writing data to the specified file descriptor.
+ * Only the first write error if any is printed.
+ * This is used when writing to STDOUT.
+ */
+void
+tryWrite(int fd, const char * cp, int len)
+{
+ static int failed = FALSE;
+
+ int status = fullWrite(fd, cp, len);
+
+ if ((status < 0) && !failed)
+ {
+ failed = TRUE;
+ perror("write");
+ }
+}
+
+
/*
* Write all of the supplied buffer out to a file.
* This does multiple writes as necessary.
return total;
}
+
+/*
+ * Call system for the specified command and print an error
+ * message if the execution fails. The exit status of the
+ * command is returned.
+ */
+int
+trySystem(const char * cmd)
+{
+ int status;
+
+ status = system(cmd);
+
+ if (status == -1)
+ fprintf(stderr, "Error starting command: %s\n", cmd);
+
+ return status;
+}
+
+
+/*
+ * Check the status for the most recent system call and complain
+ * if its value is -1 which indicates it failed.
+ */
+void
+checkStatus(const char * name, int status)
+{
+ if (status == -1)
+ perror(name);
+}
+
/* END CODE */