mirror of
https://github.com/open-simh/simtools.git
synced 2026-01-14 07:39:37 +00:00
* Fix warnings in config11 * Fix warnings in converters/asc * Fix warning in converters/decsys * Fix warning in converters/dtos8cvt * Fix strncpy() warning * Fix unsigned/signed warning * Fix assignment-within-if warning * Fix assignment-within-if warning * Fix unused `main` parameters warnings * Fix assignment-within-if warning * Fix assignment-within-if warning * Fix assignment-within-if warning * Fix assignment-within-if warning * Fix assignment-within-if warning * Fix assignment-within-if warning * config11: Remove accidentally committed debugging code --------- Co-authored-by: Jan-Benedict Glaw <jbglaw@getslash.de>
2188 lines
53 KiB
C
2188 lines
53 KiB
C
/*
|
||
* Copyright (C) 2018 John Forecast. All Rights Reserved.
|
||
*
|
||
* 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. The name of the author may not be used to endorse or promote products
|
||
* derived from this software without specific prior written permission.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY JOHN FORECAST "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 AUTHOR 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.
|
||
*/
|
||
|
||
/*
|
||
* Utility for manipulating files with file system container files as used
|
||
* by various emulators such as SIMH.
|
||
*/
|
||
|
||
/*
|
||
* Foreign file system interface (9/18/2019)
|
||
*
|
||
* Each foreign file system is described by a struct FSdef which is linked
|
||
* into a master list of supported file systems in FSioInit(). Each entry
|
||
* in struct FSdef is described here:
|
||
*
|
||
* struct FSdef *next;
|
||
*
|
||
* Pointer to the next supported file system. Set up by FSioInit().
|
||
*
|
||
* char *fstype;
|
||
*
|
||
* Pointer to the file system name. This is used by the "mount" command
|
||
* to locate a specific file system.
|
||
*
|
||
* char *descr;
|
||
*
|
||
* One line description of the file system. Output by the "help" command.
|
||
*
|
||
* uint16_t flags;
|
||
*
|
||
* Flags to control operation of the top-level code:
|
||
*
|
||
* FS_UNITVALID
|
||
*
|
||
* Some file systems (e.g. RT11) allow a container file to
|
||
* contain multiple file systems. Setting this bit allows the
|
||
* device name parser to include a unit number (8 bit, octal
|
||
* value) when referencing a file/filesystem. For example:
|
||
*
|
||
* mount dk rt11sys.dsk rt11
|
||
* dir dk1:
|
||
*
|
||
* displays the directory of the second partition within the
|
||
* container file.
|
||
*
|
||
* FS_TAPE
|
||
*
|
||
* The file system operates on SIMH .tap format magtape
|
||
* container files.
|
||
*
|
||
* size_t blocksz;
|
||
*
|
||
* The size of a data block for this particular file system. It is
|
||
* possible for a file system to have different block sizes for
|
||
* different disks (see dos11 for an example).
|
||
*
|
||
* int (*mount)(struct mountedFS *mount);
|
||
*
|
||
* This routine is called when processing a "mount" command. The container
|
||
* file is open, and this routine should verify that it contains a valid
|
||
* file system. This routine may print out information about the file
|
||
* system. Return 1 if the file system is valid, 0 if the file system is
|
||
* invalid and -1 if the file system is invalid and an erroir message
|
||
* has already been printed.
|
||
*
|
||
* void (*umount)(struct mountedFS *mount);
|
||
*
|
||
* This routine is called when processing an "unmount" command. This
|
||
* routine is responsible for cleaning up any memory allocations, open
|
||
* files etc (the framework will close the container file after calling
|
||
* this routine).
|
||
*
|
||
* size_t (*size)(void)
|
||
*
|
||
* Return the number of byte used for the container file. The command
|
||
* line switch (-t type) may be used to override the default size.
|
||
*
|
||
* int (*newfs)(struct mountedFS *mount, size_t size);
|
||
*
|
||
* Create an empty file system in the container file provided by the
|
||
* mountedFS structure. The size of the container file is provided
|
||
* in the "size" parameter. The file system is never completely mounted
|
||
* by this command. This routine returns 1 if the file system was
|
||
* successfully created, 0 otherwise.
|
||
*
|
||
* void (*set)(struct mountedFS *mount, uint8_t unit, uint8_t present);
|
||
*
|
||
* This routine is called when processing a "set" command. The arguments
|
||
* for the "set" command are passed on to the file system and the
|
||
* command syntax is handled completely by the file system code.
|
||
*
|
||
* void (*info)(struct mountedFS *mount, uint8_t unit, uint8_t present);
|
||
*
|
||
* This routine is called when processing an "info" command. It is most
|
||
* useful for the file system plug-in developer and should output
|
||
* information about the file system layout including information which
|
||
* is not normally available to users. On file systems with
|
||
* FS_UNITVALID set (see above), "present" is non-zero if a unit number
|
||
* was included on the command line. For example, RT-11 uses "present"
|
||
* to decide whether to display information about a single file system
|
||
* ("present" 0) or all file systems in the containder ("present" 1).
|
||
*
|
||
* void (*dir)(struct mountedFS *mount, uint8_t unit, char *fname);
|
||
*
|
||
* This routine is called to process a "dir" command. It should output
|
||
* information in a format native to the file system. If the "-f"
|
||
* switch is present (SWISSET('f')), more information may be output.
|
||
* "fname" is a filename string which may be present to filter the
|
||
* output. Wild carding may be used but the plug-in is responsible
|
||
* for parsing and handling wild card characters.
|
||
*
|
||
* void *(*openFileR)(struct mountedFS *mount, uint8_t unit, char *fname);
|
||
*
|
||
* Open a file on the specified filesystem for reading. The filename
|
||
* may not include wild card characters. This routine returns an
|
||
* opaque pointer as a file handle which will be passed to the
|
||
* read/write/close routines.
|
||
*
|
||
* void *(*openFileW)(struct mountedFS *mount, uint8_t unit,
|
||
* char *fname, off_t size);
|
||
*
|
||
* Open a file on the specified filesystem for writing, similar to
|
||
* openFileR() above. "size" is an estimate of the amount of space
|
||
* needed for the file (in bytes) which may be used to pre-allocate
|
||
* disk space. A value of 0 indicates that the system is unable to
|
||
* determine the amount of space required (e.g. input from /dev/tty).
|
||
*
|
||
* off_t (*fileSize)(void *filep);
|
||
*
|
||
* Returns the size, in bytes, of a file currently open for reading.
|
||
* If it is not possible to determine the actual file size, return 0.
|
||
* In all other cases, the returned value may over-estimate, but not
|
||
* under-estimate the size of the file. For example, linked files
|
||
* on DOS-11 will overestimate the size by 2 bytes/block.
|
||
*
|
||
* void (*closeFile)(void *filep);
|
||
*
|
||
* Close a file which is currently open for reading/writing.
|
||
*
|
||
* size_t (*readFile)(void *filep, void *buf, size_t buflen);
|
||
*
|
||
* Read a maximum of "buflen" bytes from the file into the buffer.
|
||
* If ASCII mode is selected by the "-a" switch (SWISSET('a')), the
|
||
* read request should terminate at record (line) boundaries and the
|
||
* buffer should be terminated with a <CRLF> character pair. Other
|
||
* file system dependent processing may occur in ASCII mode, e.g.
|
||
* DOS-11 masks characters down to 7-bits while RT-11 looks for ^Z
|
||
* characters indicating EOF. This routine returns the number of bytes
|
||
* read from the file, which may be less than the buffer size. If the
|
||
* file is positioned at EOF when the read request is issued or an
|
||
* error occurs it should return 0.
|
||
*
|
||
* size_t (*writeFile)(void *filep, void *buf, size_t buflen);
|
||
*
|
||
* Writes a maximum of "buflen" bytes from the buffer to the file.
|
||
* If ASCII mode is selected by the "-a" switch (SWISSET('a')),
|
||
* if the last 2 characters of the buffer are <CRLF> they should be
|
||
* replaced by the native line ending character(s) for the file
|
||
* system. This routine returns the number of bytes written to the
|
||
* file, 0 if an error occurs.
|
||
*
|
||
* void (*deleteFile)(void *filep, char *fname)
|
||
*
|
||
* Delete a file which is currently open for reading. This routine
|
||
* should perform a closeFile() operation to free up resources. "fname"
|
||
* is the filename used when opening the file, it may be used in those
|
||
* cases where it is not possible to delete an open file (e.g. Unix).
|
||
*
|
||
* void (*rewind)(struct mountedFS *mount)
|
||
*
|
||
* This function is only valid for magtape container files. Rewind the
|
||
* container file so that it is postiioned at beginning-of-tape.
|
||
*
|
||
* void (*eom)(struct mountedFS *mount)
|
||
*
|
||
* This function is only valid for magtape container files. Position the
|
||
* container file at the logical end-of-media so that subsequent write
|
||
* operations will append data to the tape.
|
||
*
|
||
* void (*skipforw)(struct mountedFS *mount, unsigned long count)
|
||
*
|
||
* This function is only valid for magtape container files. Skip formward
|
||
* over the specified number of files or until end-of-media is reached.
|
||
*
|
||
* void (*skiprev)(struct mountedFS *mount, unsigned long count)
|
||
*
|
||
* This function is only valid for magtape container files. Skip backwards
|
||
* over the specified number of files or until beginning-of-tape is
|
||
* reached.
|
||
*
|
||
*
|
||
* Mounting a file system creates a struct mountedFS which is provided to
|
||
* most routines. Each entry is described here:
|
||
*
|
||
* struct mountedFS *next;
|
||
*
|
||
* Pointer to the next mounted file system or NULL.
|
||
*
|
||
* char name[MAX_DEVLEN+1];
|
||
*
|
||
* The name provided by the user on the "mount" command. MAX_DEVLEN
|
||
* is defined as 16.
|
||
*
|
||
* struct FSdef *filesys;
|
||
*
|
||
* The file system used by this mount.
|
||
*
|
||
* size_t blocksz;
|
||
*
|
||
* Block size in use for this file system.
|
||
*
|
||
* uint16_t flags;
|
||
*
|
||
* Flags to control operation of the file system code:
|
||
*
|
||
* FS_READONLY
|
||
*
|
||
* The file system is mounted read-only and no changes will
|
||
* be allowed.
|
||
*
|
||
* FS_DEBUG
|
||
*
|
||
* Enables debug output. Such debug code may not be included
|
||
* in the default build.
|
||
*
|
||
* FILE *container;
|
||
*
|
||
* The open file handle for performing I/O on file system(s).
|
||
*
|
||
* union {} FSdata;
|
||
*
|
||
* Private region for use by the file system code.
|
||
*/
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <unistd.h>
|
||
#include <errno.h>
|
||
#include <ctype.h>
|
||
#include <string.h>
|
||
|
||
#include "fsio.h"
|
||
|
||
/*
|
||
* By default, fsio will use the GNU readline library. If you do not have
|
||
* it available or do not wish to use it, comment out the following line.
|
||
* You may also need to comment out the LIBS= line in Makefile.
|
||
*/
|
||
#define USE_READLINE
|
||
|
||
#ifdef USE_READLINE
|
||
#include <readline/readline.h>
|
||
#include <readline/history.h>
|
||
#endif
|
||
|
||
int verbose = 0, quiet = 0;
|
||
|
||
#define MAX_CMDLEN 512
|
||
|
||
static void doMount(void);
|
||
static void doUmount(void);
|
||
static void doNewfs(void);
|
||
static void doSet(void);
|
||
static void doInfo(void);
|
||
static void doDir(void);
|
||
static void doDump(void);
|
||
static void doCopy(void);
|
||
static void doType(void);
|
||
static void doDelete(void);
|
||
static void doStatus(void);
|
||
static void doDo(void);
|
||
static void doHelp(void);
|
||
static void doExit(void);
|
||
static void doRewind(void);
|
||
static void doEOM(void);
|
||
static void doSkipForw(void);
|
||
static void doSkipRev(void);
|
||
|
||
typedef void (*cmd_t)(void);
|
||
|
||
struct command {
|
||
char *name; /* Command name string */
|
||
char *switches; /* Command switches (getopt format) */
|
||
int minargs; /* Required argument count */
|
||
int maxargs; /* Maximum argument count */
|
||
int flags; /* Command flags */
|
||
cmd_t func; /* Command execution function */
|
||
} cmdTable[] = {
|
||
#ifdef DEBUG
|
||
{ "mount", OPTIONS("dfrt:x"), 3, 3, 0, doMount },
|
||
#else
|
||
{ "mount", OPTIONS("frt:x"), 3, 3, 0, doMount },
|
||
#endif
|
||
{ "umount", NULL, 1, 1, 0, doUmount },
|
||
{ "newfs", OPTIONS("e:t:"), 2, 2, 0, doNewfs },
|
||
{ "set", NULL, 2, MAX_CMDLEN, 0, doSet },
|
||
{ "info", NULL, 1, 1, 0, doInfo },
|
||
{ "dir", OPTIONS("fn"), 1, 1, 0, doDir },
|
||
{ "dump", OPTIONS("bcdnwx"), 1, 1, 0, doDump },
|
||
{ "copy", OPTIONS("ac:np"), 2, 2, 0, doCopy },
|
||
{ "type", OPTIONS("n"), 1, 1, 0, doType },
|
||
{ "delete", NULL, 1, 1, 0, doDelete },
|
||
{ "status", NULL, 0, 0, 0, doStatus },
|
||
{ "do", OPTIONS("q"), 1, 1, 0, doDo },
|
||
{ "help", NULL, 0, 0, 0, doHelp },
|
||
{ "exit", NULL, 0, 0, 0, doExit },
|
||
{ "quit", NULL, 0, 0, 0, doExit },
|
||
{ "rewind", NULL, 1, 1, 0, doRewind },
|
||
{ "eom", NULL, 1, 1, 0, doEOM },
|
||
{ "skipf", NULL, 2, 2, 0, doSkipForw },
|
||
{ "skipr", NULL, 2, 2, 0, doSkipRev },
|
||
{ NULL, NULL, 0, 0, 0, doExit }
|
||
};
|
||
|
||
/*
|
||
* Switches and values
|
||
*/
|
||
uint32_t swPresent;
|
||
char *swValue[26];
|
||
|
||
/*
|
||
* Command argument strings
|
||
*/
|
||
int args;
|
||
char **words, *wds[MAX_CMDLEN];
|
||
|
||
#ifdef DEBUG
|
||
FILE *DEBUGout = NULL;
|
||
#endif
|
||
|
||
struct mountedFS *mounts;
|
||
|
||
struct FSdef *fileSystems = NULL;
|
||
|
||
/*
|
||
* Tables for use with bitmap allocators
|
||
*/
|
||
uint16_t bits[16] = {
|
||
0000001, 0000002, 0000004, 0000010, 0000020, 0000040, 0000100, 0000200,
|
||
0000400, 0001000, 0002000, 0004000, 0010000, 0020000, 0040000, 0100000
|
||
};
|
||
|
||
uint16_t lowbits[16] = {
|
||
0000001, 0000003, 0000007, 0000017, 0000037, 0000077, 0000177, 0000377,
|
||
0000777, 0001777, 0003777, 0007777, 0017777, 0037777, 0077777, 0177777
|
||
};
|
||
|
||
uint16_t highbits[16] = {
|
||
0100000, 0140000, 0160000, 0170000, 0174000, 0176000, 0177000, 0177400,
|
||
0177600, 0177700, 0177740, 0177760, 0177770, 0177774, 0177776, 0177777
|
||
};
|
||
|
||
/*
|
||
* Table of # of zeroes in each byte value.
|
||
*/
|
||
uint8_t zeroes[256] = {
|
||
8, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 4,
|
||
7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3,
|
||
7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3,
|
||
6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
|
||
7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3,
|
||
6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
|
||
6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
|
||
5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1,
|
||
7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3,
|
||
6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
|
||
6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
|
||
5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1,
|
||
6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
|
||
5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1,
|
||
5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1,
|
||
4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0,
|
||
};
|
||
|
||
void FSioCommands(FILE *);
|
||
static void FSioExecute(char *);
|
||
|
||
extern struct mountedFS localMount;
|
||
|
||
extern struct FSdef localFS;
|
||
extern struct FSdef dos11FS;
|
||
extern struct FSdef rt11FS;
|
||
extern struct FSdef dosmtFS;
|
||
extern struct FSdef os8FS;
|
||
|
||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||
|
||
/*++
|
||
* F S i o I n i t
|
||
*
|
||
* Perform once-only initialization.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void FSioInit(void)
|
||
{
|
||
mounts = NULL;
|
||
|
||
/*
|
||
* Initialize the the local access device
|
||
*/
|
||
strcpy(localMount.name, "local");
|
||
localMount.filesys = &localFS;
|
||
localMount.blocksz = 0; /*** TODO ***/
|
||
localMount.container = NULL;
|
||
|
||
/*
|
||
* Add the local file access device.
|
||
*/
|
||
localMount.next = mounts;
|
||
mounts = &localMount;
|
||
|
||
/*
|
||
* Add supported file systems
|
||
*/
|
||
localFS.next = fileSystems;
|
||
fileSystems = &localFS;
|
||
|
||
dos11FS.next = fileSystems;
|
||
fileSystems = &dos11FS;
|
||
|
||
rt11FS.next = fileSystems;
|
||
fileSystems = &rt11FS;
|
||
|
||
dosmtFS.next = fileSystems;
|
||
fileSystems = &dosmtFS;
|
||
|
||
os8FS.next = fileSystems;
|
||
fileSystems = &os8FS;
|
||
}
|
||
|
||
/*++
|
||
* U s a g e
|
||
*
|
||
* Display a usage message and exit.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* Never returns
|
||
*
|
||
--*/
|
||
void Usage(void)
|
||
{
|
||
fprintf(stderr, "Usage: fsio [cmdFile]\n");
|
||
exit(1);
|
||
}
|
||
|
||
/*++
|
||
* m a i n
|
||
*
|
||
* Start routine for the fsio application.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* argc - # of supplied arguments
|
||
* argv - array of argument strings
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* Exit status
|
||
*
|
||
--*/
|
||
int main(
|
||
int argc,
|
||
char **argv
|
||
)
|
||
{
|
||
FILE *commands = stdin;
|
||
int ch;
|
||
|
||
FSioInit();
|
||
|
||
/*
|
||
* Process command line switches
|
||
*/
|
||
while ((ch = getopt(argc, argv, "qv")) != -1) {
|
||
switch (ch) {
|
||
case 'q':
|
||
quiet = 1;
|
||
break;
|
||
|
||
case 'v':
|
||
verbose = 1;
|
||
break;
|
||
|
||
default:
|
||
Usage();
|
||
}
|
||
}
|
||
|
||
argc -= optind;
|
||
argv += optind;
|
||
|
||
#if !defined(__linux__)
|
||
optreset = 1;
|
||
#endif
|
||
optind = 1;
|
||
|
||
if (argc <= 1) {
|
||
if (argc == 1) {
|
||
commands = fopen(argv[0], "r");
|
||
if (commands == NULL) {
|
||
fprintf(stderr, "Failed to open \"%s\": %s\n",
|
||
argv[0], strerror(errno));
|
||
exit(2);
|
||
}
|
||
}
|
||
FSioCommands(commands);
|
||
} else Usage();
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*++
|
||
* c h e c k D e v
|
||
*
|
||
* Check the validity of a device name. An error message will be generated
|
||
* if it is invalid.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* dev - pointer to device name
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if device name valid, 0 otherwise
|
||
*
|
||
--*/
|
||
static int checkDev(
|
||
char *dev
|
||
)
|
||
{
|
||
unsigned int i;
|
||
|
||
/*
|
||
* Remove any trailing ':' characters
|
||
*/
|
||
while ((strlen(dev) != 0) && (dev[strlen(dev) - 1] == ':'))
|
||
dev[strlen(dev) - 1] = '\0';
|
||
|
||
for (i = 0; i < strlen(dev); i++)
|
||
if (!isalpha(dev[i])) {
|
||
fprintf(stderr,
|
||
"%s: Device name contains non-alpha character\n", wds[0]);
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/*++
|
||
* f i n d M o u n t
|
||
*
|
||
* Given a full device + file specification, find the mount point from
|
||
* the device name, parse out the optional unit number and return the
|
||
* remaining file specification. If no device + file specification is
|
||
* present, assume the file specification is for the local file system.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* cmd - current command name
|
||
* dev - pointer to device name + file specification
|
||
* unit - return optional unit number here
|
||
* present - return unit present indicator here (0 or 1)
|
||
* fname - return remaining file specification here
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* Pointer to the mounted file system structure, NULL if not found, unit
|
||
* number invalid or other parse error
|
||
*
|
||
--*/
|
||
static struct mountedFS *findMount(
|
||
char *cmd,
|
||
char *dev,
|
||
uint8_t *unit,
|
||
uint8_t *present,
|
||
char **fname
|
||
)
|
||
{
|
||
struct mountedFS *fs = &localMount;
|
||
unsigned long unitno = 0;
|
||
uint8_t unitpresent = 0;
|
||
unsigned int i;
|
||
char *endptr, *ptr;
|
||
char origdev[33];
|
||
|
||
if ((ptr = strchr(dev, ':')) != NULL) {
|
||
*ptr++ = '\0';
|
||
|
||
strncpy(origdev, dev, sizeof(origdev)-1);
|
||
|
||
/*
|
||
* Determine validity of the device name and whether a unit number is
|
||
* present.
|
||
*/
|
||
for (i = 0; i < strlen(dev); i++)
|
||
if (!isalpha(dev[i])) {
|
||
if (isdigit(dev[i])) {
|
||
unitno = strtoul(&dev[i], &endptr, 8);
|
||
if (*endptr != '\0') {
|
||
fprintf(stderr,
|
||
"%s: Device \"%s\" unit number invalid\n", cmd, origdev);
|
||
return NULL;
|
||
}
|
||
|
||
if (unitno > 0377) {
|
||
fprintf(stderr,
|
||
"%s: Device \"%s\" unit number too large\n", cmd, origdev);
|
||
return NULL;
|
||
}
|
||
dev[i] ='\0';
|
||
unitpresent = 1;
|
||
break;
|
||
}
|
||
fprintf(stderr,
|
||
"%s: Device \"%s\" contains non-alpha character\n",
|
||
cmd, origdev);
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
* Search the mounted file system list.
|
||
*/
|
||
for (fs = mounts; fs != NULL; fs = fs->next)
|
||
if (strcmp(dev, fs->name) == 0) {
|
||
if ((unitpresent == 0) ||
|
||
((fs->filesys->flags & FS_UNITVALID) != 0))
|
||
break;
|
||
fprintf(stderr,
|
||
"%s: \"%s\" does not support unit numbers\n", cmd, fs->name);
|
||
return NULL;
|
||
}
|
||
|
||
if (fs == NULL)
|
||
fprintf(stderr, "%s: Device \"%s\" not mounted\n", cmd, origdev);
|
||
} else {
|
||
if (getenv("FSioForceLocal") != NULL) {
|
||
fprintf(stderr, "Local file system access requires use of \"local:\"\n");
|
||
return NULL;
|
||
}
|
||
ptr = dev;
|
||
}
|
||
|
||
/*
|
||
* No device present - assume local file system usage
|
||
*/
|
||
*unit = unitno & 0377;
|
||
*present = unitpresent;
|
||
*fname = ptr;
|
||
return fs;
|
||
}
|
||
|
||
/*++
|
||
* l o o k u p D e v
|
||
*
|
||
* Search the mounted device table for a specified name.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* dev - pointer to the device name
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* Pointer to the mounted file system structure, NULL if not found or
|
||
* unit number invalid
|
||
*
|
||
--*/
|
||
static struct mountedFS *lookupDev(
|
||
char *dev
|
||
)
|
||
{
|
||
struct mountedFS *fs;
|
||
|
||
for (fs = mounts; fs != NULL; fs = fs->next)
|
||
if (strcmp(dev, fs->name) == 0)
|
||
return fs;
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/*++
|
||
* l o o k u p F S
|
||
*
|
||
* Search the list of supported file systems for a specific type.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* fs - pointer to the file system type
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* Pointer to the file system definition, NULL if not found
|
||
*
|
||
--*/
|
||
static struct FSdef *lookupFS(
|
||
char *fs
|
||
)
|
||
{
|
||
struct FSdef *filesys;
|
||
|
||
for (filesys = fileSystems; filesys != NULL; filesys = filesys->next)
|
||
if (strcmp(fs, filesys->fstype) == 0)
|
||
return filesys;
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/*++
|
||
* d o M o u n t
|
||
*
|
||
* Mount command processing routine.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doMount(void)
|
||
{
|
||
struct FSdef *filesys;
|
||
FILE *container;
|
||
int status;
|
||
char *mode = SWISSET('r') ? "r" : "r+";
|
||
|
||
if (checkDev(words[0]) != 0) {
|
||
if (lookupDev(words[0]) == NULL) {
|
||
if ((filesys = lookupFS(words[2])) != NULL) {
|
||
if (filesys->mount == NULL) {
|
||
fprintf(stderr,
|
||
"mount: \"%s\" is not a mountable filesystem\n", words[2]);
|
||
return;
|
||
}
|
||
|
||
if ((container = fopen(words[1], mode)) != NULL) {
|
||
struct mountedFS *mount;
|
||
|
||
if ((mount = malloc(sizeof(struct mountedFS))) != NULL) {
|
||
memset(mount, 0, sizeof(struct mountedFS));
|
||
|
||
strcpy(mount->name, words[0]);
|
||
mount->filesys = filesys;
|
||
mount->blocksz = filesys->blocksz;
|
||
mount->flags = SWISSET('r') ? FS_READONLY : 0;
|
||
#ifdef DEBUG
|
||
if (SWISSET('d')) {
|
||
mount->flags |= FS_DEBUG;
|
||
|
||
if (DEBUGout == NULL) {
|
||
char *dbg = getenv("FSioDebugLog");
|
||
|
||
if (dbg != NULL)
|
||
DEBUGout = fopen(dbg, "a");
|
||
}
|
||
|
||
if (DEBUGout == NULL)
|
||
DEBUGout = stdout;
|
||
}
|
||
#endif
|
||
mount->container = container;
|
||
mount->skip = 0;
|
||
|
||
/*
|
||
* Verify that the container holds a valid file system
|
||
*/
|
||
if ((status = (*filesys->mount)(mount)) > 0) {
|
||
mount->next = mounts;
|
||
mounts = mount;
|
||
return;
|
||
}
|
||
free(mount);
|
||
if (status == 0)
|
||
fprintf(stderr,
|
||
"mount: \"%s\" does not contain a valid file system\n",
|
||
words[1]);
|
||
} else fprintf(stderr, "mount: out of memory\n");
|
||
fclose(container);
|
||
} else fprintf(stderr, "mount: failed to open \"%s\"\n", words[1]);
|
||
} else fprintf(stderr, "mount: \"%s\" is not a supported file system type\n", words[2]);
|
||
} else fprintf(stderr, "mount: \"%s\" already mounted\n", words[0]);
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* d o U m o u n t
|
||
*
|
||
* Umount command processing routine.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doUmount(void)
|
||
{
|
||
struct mountedFS *mount, **ptr = &mounts;
|
||
|
||
if (checkDev(words[0]) != 0) {
|
||
if ((mount = lookupDev(words[0])) != NULL) {
|
||
if (mount->filesys->umount == NULL) {
|
||
fprintf(stderr, "umount: \"%s\" may not be unmounted\n", words[0]);
|
||
return;
|
||
}
|
||
|
||
for (ptr = &mounts; *ptr != NULL; ptr = &((*ptr)->next))
|
||
if (*ptr == mount) {
|
||
*ptr = mount->next;
|
||
(*mount->filesys->umount)(mount);
|
||
fclose(mount->container);
|
||
free(mount);
|
||
return;
|
||
}
|
||
fprintf(stderr, "Mounted file system list corrupted\n");
|
||
exit(10);
|
||
} else fprintf(stderr, "umount: \"%s\" is not mounted\n", words[0]);
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* d o N e w f s
|
||
*
|
||
* Create a new container file and build an empty file system on it.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doNewfs(void)
|
||
{
|
||
struct FSdef *filesys;
|
||
struct mountedFS mount;
|
||
char ch = 0;
|
||
int status;
|
||
size_t size = 0;
|
||
|
||
if ((filesys = lookupFS(words[1])) != NULL) {
|
||
if ((filesys->newfs == NULL) && ((filesys->flags & FS_EMPTYFILE) == 0)) {
|
||
fprintf(stderr,
|
||
"newfs: \"%s\" does not support the newfs command\n", words[1]);
|
||
return;
|
||
}
|
||
|
||
if (access(words[0], F_OK) == 0) {
|
||
fprintf(stderr, "newfs: \"%s\" already exists\n", words[0]);
|
||
return;
|
||
}
|
||
|
||
memset(&mount, 0, sizeof(mount));
|
||
|
||
/*
|
||
* Get the required container file size.
|
||
*/
|
||
if (filesys->size != NULL)
|
||
size = (*filesys->size)();
|
||
|
||
if ((mount.container = fopen(words[0], "w+")) != NULL) {
|
||
off_t offset = size - 1;
|
||
|
||
mount.skip = 0;
|
||
|
||
/*
|
||
* If an empty file is valid, we are all done
|
||
*/
|
||
if ((filesys->flags & FS_EMPTYFILE) != 0)
|
||
return;
|
||
|
||
if ((fseeko(mount.container, offset + mount.skip, SEEK_SET) == 0) &&
|
||
(fwrite(&ch, 1, 1, mount.container) == 1)) {
|
||
mount.filesys = filesys;
|
||
mount.blocksz = filesys->blocksz;
|
||
|
||
status = (*filesys->newfs)(&mount, size);
|
||
|
||
fclose(mount.container);
|
||
if (status == 0)
|
||
unlink(words[0]);
|
||
} else {
|
||
fprintf(stderr, "newfs: failed to extend \"%s\"\n", words[0]);
|
||
fclose(mount.container);
|
||
unlink(words[0]);
|
||
}
|
||
} else fprintf(stderr, "newfs: failed to create \"%s\"\n", words[0]);
|
||
} else fprintf(stderr, "newfs: \"%s\" is not a supported file system type\n", words[1]);
|
||
}
|
||
|
||
/*++
|
||
* d o S e t
|
||
*
|
||
* Set command processing routine.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doSet(void)
|
||
{
|
||
struct mountedFS *mount;
|
||
char *fname;
|
||
uint8_t unit, present;
|
||
|
||
if ((mount = findMount("set", words[0], &unit, &present, &fname)) != NULL) {
|
||
if (*fname == '\0') {
|
||
if (mount->filesys->set != NULL)
|
||
(*mount->filesys->set)(mount, unit, present);
|
||
else fprintf(stderr, "set: \"%s\" does not support \"set\" command\n",
|
||
mount->filesys->fstype);
|
||
} else fprintf(stderr, "set: Does not expect a file name\n");
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* d o I n f o
|
||
*
|
||
* Info command processing routine.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doInfo(void)
|
||
{
|
||
struct mountedFS *mount;
|
||
char *fname;
|
||
uint8_t unit, present;
|
||
|
||
if ((mount = findMount("info", words[0], &unit, &present, &fname)) != NULL) {
|
||
if (*fname == '\0')
|
||
(*mount->filesys->info)(mount, unit, present);
|
||
else fprintf(stderr, "info: Does not expect a file name\n");
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* d o D i r
|
||
*
|
||
* Dir command processing routine.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doDir(void)
|
||
{
|
||
struct mountedFS *mount;
|
||
char *fname;
|
||
uint8_t unit, present;
|
||
|
||
if ((mount = findMount("dir", words[0], &unit, &present, &fname)) != NULL) {
|
||
(*mount->filesys->dir)(mount, unit, fname);
|
||
return;
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* d o D u m p
|
||
*
|
||
* Dump command processing routine.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doDump(void)
|
||
{
|
||
struct mountedFS *mount;
|
||
char *fname;
|
||
void *file;
|
||
uint8_t unit, present;
|
||
|
||
if ((mount = findMount("dump", words[0], &unit, &present, &fname)) != NULL) {
|
||
if ((file = (*mount->filesys->openFileR)(mount, unit, fname)) != NULL) {
|
||
unsigned int offset = 0, datasz = 2;
|
||
uint8_t data1;
|
||
uint16_t data2;
|
||
uint32_t data4;
|
||
char output[128];
|
||
|
||
if (SWISSET('b') || SWISSET('c'))
|
||
datasz = 1;
|
||
else if (SWISSET('d'))
|
||
datasz = 4;
|
||
else if (SWISSET('w'))
|
||
datasz = 2;
|
||
|
||
output[0] = '\0';
|
||
|
||
switch (datasz) {
|
||
case 1:
|
||
while ((*mount->filesys->readFile)(file, &data1, 1) != 0) {
|
||
if (SWISSET('b'))
|
||
sprintf(&output[strlen(output)],
|
||
SWISSET('x') ? " 0x%02X" : " 0%03o", data1);
|
||
else sprintf(&output[strlen(output)], " %s ", Ascii[data1 & 0177]);
|
||
|
||
if ((offset & 07) == 07) {
|
||
if (mount->blocksz != 0)
|
||
printf("%s%011o %s\n",
|
||
((offset & ~07) % mount->blocksz) == 0 ? "\n" : "",
|
||
offset & ~07, output);
|
||
else printf("%011o %s\n", offset & ~07, output);
|
||
output[0] = '\0';
|
||
}
|
||
offset++;
|
||
}
|
||
|
||
if (output[0] != '\0')
|
||
printf("%011o %s\n", offset & ~07, output);
|
||
break;
|
||
|
||
case 2:
|
||
while ((*mount->filesys->readFile)(file, &data2, 2) != 0) {
|
||
sprintf(&output[strlen(output)],
|
||
SWISSET('x') ? " 0x%04X" : " %07o", data2);
|
||
|
||
if ((offset & 017) == 016) {
|
||
if (mount->blocksz != 0)
|
||
printf("%s%011o %s\n",
|
||
((offset & ~017) % mount->blocksz) == 0 ? "\n" : "",
|
||
offset & ~017, output);
|
||
else printf("%011o %s\n", offset & ~017, output);
|
||
output[0] = '\0';
|
||
}
|
||
offset += 2;
|
||
data2 = 0;
|
||
}
|
||
|
||
if (output[0] != '\0')
|
||
printf("%011o %s\n", offset & ~07, output);
|
||
break;
|
||
|
||
case 4:
|
||
while ((*mount->filesys->readFile)(file, &data4, 4) != 0) {
|
||
sprintf(&output[strlen(output)],
|
||
SWISSET('x') ? " 0x%08X" : " %011o", data2);
|
||
|
||
if ((offset & 017) == 014) {
|
||
if (mount->blocksz != 0)
|
||
printf("%s%011o %s\n",
|
||
((offset & ~017) % mount->blocksz) == 0 ? "\n" : "",
|
||
offset & ~017, output);
|
||
else printf("%011o %s\n", offset & ~017, output);
|
||
output[0] = '\0';
|
||
}
|
||
offset += 4;
|
||
data2 = 0;
|
||
}
|
||
|
||
if (output[0] != '\0')
|
||
printf("%011o %s\n", offset & ~07, output);
|
||
break;
|
||
}
|
||
/*
|
||
* Close the file
|
||
*/
|
||
(*mount->filesys->closeFile)(file);
|
||
} else fprintf(stderr, "dump: \"%s\" no such file\n", fname);
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* d o C o p y
|
||
*
|
||
* Copy command processing routine.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
#define BFRSIZ 512
|
||
|
||
static void doCopy(void)
|
||
{
|
||
struct mountedFS *mountSrc, *mountDest;
|
||
struct FSdef *fsSrc, *fsDest;
|
||
uint8_t unitSrc, unitDest, presentSrc, presentDest;
|
||
char *fnameSrc, *fnameDest;
|
||
void *fileSrc, *fileDest;
|
||
char *endptr;
|
||
|
||
mountSrc = findMount("copy", words[0], &unitSrc, &presentSrc, &fnameSrc);
|
||
mountDest = findMount("copy", words[1], &unitDest, &presentDest, &fnameDest);
|
||
|
||
if ((mountSrc != NULL) && (mountDest != NULL)) {
|
||
fsSrc = mountSrc->filesys;
|
||
fsDest = mountDest->filesys;
|
||
|
||
if (mountSrc == mountDest) {
|
||
if ((fsDest->flags & FS_1OPENFILE) != 0) {
|
||
fprintf(stderr,
|
||
"copy: \"%s\" does not allow simultaneous read/write access\n",
|
||
mountDest->name);
|
||
return;
|
||
}
|
||
}
|
||
|
||
if ((mountDest->flags & FS_READONLY) != 0) {
|
||
fprintf(stderr, "copy: \"%s\" is mounted read-only\n", mountDest->name);
|
||
return;
|
||
}
|
||
fsSrc = mountSrc->filesys;
|
||
fsDest = mountDest->filesys;
|
||
|
||
if ((fileSrc = (*fsSrc->openFileR)(mountSrc, unitSrc, fnameSrc)) != NULL) {
|
||
off_t size = (*fsSrc->fileSize)(fileSrc);
|
||
unsigned long contig;
|
||
|
||
if (SWISSET('c')) {
|
||
contig = strtoul(SWGETVAL('c'), &endptr, 10);
|
||
if (*endptr != '\0') {
|
||
fprintf(stderr, "copy: Invalid character in '-c' argument\n");
|
||
(*fsSrc->closeFile)(fileSrc);
|
||
return;
|
||
}
|
||
if (contig != 0)
|
||
size = MAX(size, (long)(contig * mountDest->blocksz));
|
||
}
|
||
|
||
if ((fileDest = (*fsDest->openFileW)(mountDest, unitDest, fnameDest, size)) != NULL) {
|
||
char buf[BFRSIZ];
|
||
size_t len;
|
||
|
||
while ((len = (*fsSrc->readFile)(fileSrc, buf, BFRSIZ)) != 0) {
|
||
if ((*fsDest->writeFile)(fileDest, buf, len) == 0) {
|
||
fprintf(stderr, "copy: Error writing \"%s\"\n", fnameDest);
|
||
break;
|
||
}
|
||
}
|
||
(*fsDest->closeFile)(fileDest);
|
||
(*fsSrc->closeFile)(fileSrc);
|
||
} else {
|
||
fprintf(stderr,
|
||
"copy: failed to open \"%s\" for write\n", fnameDest);
|
||
(*fsSrc->closeFile)(fileSrc);
|
||
}
|
||
} else fprintf(stderr,
|
||
"copy: failed to open \"%s\" for read\n", fnameSrc);
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* d o T y p e
|
||
*
|
||
* Type command processing routine.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doType(void)
|
||
{
|
||
struct mountedFS *mount;
|
||
char *fname;
|
||
void *file;
|
||
uint8_t unit, present;
|
||
|
||
/*
|
||
* Force ASCII mode
|
||
*/
|
||
SWSET('a');
|
||
|
||
if ((mount = findMount("type", words[0], &unit, &present, &fname)) != NULL) {
|
||
if ((file = (*mount->filesys->openFileR)(mount, unit, fname)) != NULL) {
|
||
char buf[BFRSIZ];
|
||
size_t len, i;
|
||
|
||
while ((len = (*mount->filesys->readFile)(file, buf, BFRSIZ)) != 0) {
|
||
for (i = 0; i < len; i++)
|
||
if (buf[i] != '\0')
|
||
putchar(buf[i]);
|
||
}
|
||
|
||
/*
|
||
* Close the file
|
||
*/
|
||
(*mount->filesys->closeFile)(file);
|
||
} else fprintf(stderr, "type: \"%s\" no such file\n", fname);
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* d o D e l e t e
|
||
*
|
||
* Delete command processing routine.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doDelete(void)
|
||
{
|
||
struct mountedFS *mount;
|
||
char *fname;
|
||
void *file;
|
||
uint8_t unit, present;
|
||
|
||
if ((mount = findMount("delete", words[0], &unit, &present, &fname)) != NULL) {
|
||
if (mount->filesys->deleteFile != NULL) {
|
||
if ((mount->flags & FS_READONLY) == 0) {
|
||
if ((file = (*mount->filesys->openFileR)(mount, unit, fname)) != NULL)
|
||
(*mount->filesys->deleteFile)(file, fname);
|
||
else fprintf(stderr, "delete: \"%s\" no such file\n", fname);
|
||
} else fprintf(stderr, "delete: \"%s\" is mounted read-only\n", mount->name);
|
||
} else fprintf(stderr, "delete: Function not supported\n");
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* d o S t a t u s
|
||
*
|
||
* Display the current status of all mounted file systems.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doStatus(void)
|
||
{
|
||
if (mounts != NULL) {
|
||
struct mountedFS *mount;
|
||
|
||
for (mount = mounts; mount != NULL; mount = mount->next)
|
||
printf("%-20s%s\n", mount->name, mount->filesys->fstype);
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* d o D o
|
||
*
|
||
* Execute commands from a file.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doDo(void)
|
||
{
|
||
uint8_t noecho = SWISSET('q');
|
||
FILE *cmdFile;
|
||
char buf[MAX_CMDLEN];
|
||
|
||
if ((cmdFile = fopen(words[0], "r")) != NULL) {
|
||
for (;;) {
|
||
int len;
|
||
|
||
if (fgets(buf, sizeof(buf), cmdFile) == NULL) {
|
||
fclose(cmdFile);
|
||
return;
|
||
}
|
||
|
||
if (!noecho)
|
||
printf("fsio> %s", buf);
|
||
|
||
/*
|
||
* Remove any trailing newline
|
||
*/
|
||
len = strlen(buf);
|
||
if (buf[len - 1] == '\n')
|
||
buf[len - 1] = '\0';
|
||
|
||
/*
|
||
* Parse the command line and execute any command found
|
||
*/
|
||
FSioExecute(buf);
|
||
}
|
||
} else fprintf(stderr, "do: failed to open \"%s\"\n", words[0]);
|
||
}
|
||
|
||
/*++
|
||
* d o H e l p
|
||
*
|
||
* Help command processing routine.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doHelp(void)
|
||
{
|
||
struct FSdef *filesys;
|
||
|
||
printf(
|
||
"fsio allows manipulating files within file system container files\n"
|
||
"supported by various emulators such as SIMH.\n\n"
|
||
"fsio is executed by the command:\n\n"
|
||
" fsio [cmdfile]\n\n"
|
||
"If cmdfile is present, fsio will read commands from the command file\n"
|
||
"otherwise it will prompt for input:\n\n"
|
||
" fsio>\n\n"
|
||
"A common command format is used:\n\n"
|
||
" verb [switches] arg1 arg2 ...\n\n"
|
||
"The following commands are supported:\n\n"
|
||
" mount [-r] [-t type] dev[:] container fstype\n\n"
|
||
"The file system container file is made available to fsio (via the dev\n"
|
||
"specifier). fstype specifies the type of the container file system.\n"
|
||
"If -r is specified, the file system will be read-only.\n"
|
||
"In some cases (e.g. OS/8) fsio is unable to determine the type of the\n"
|
||
"underlying disk so it must be specified using \"-t type\"\n\n"
|
||
" umount dev[:]\n\n"
|
||
"Remove all knowledge of the container file from fsio.\n\n"
|
||
" newfs [-t type] container fstype\n\n"
|
||
"Create a new container file with an empty file system. The \"-t type\"\n"
|
||
"switch may be used to control the size of the container file, this\n"
|
||
"switch is file-system dependent.\n\n"
|
||
" set dev: args ...\n\n"
|
||
"Set parameter(s) on a mounted file system.\n\n"
|
||
" info dev:\n\n"
|
||
"Display some internal information about a mounted file system.\n\n"
|
||
" dir [-fn] dev:filespec\n\n"
|
||
"List the contents of the specified directory/file(s). Wildcards\n"
|
||
"may be specified in the format expected by the specified file system.\n"
|
||
"By default a \"brief\" format will be displayed, if -f is specified,\n"
|
||
"a \"full\" format directory will be displayed. If -n is specified on\n"
|
||
"a magtape-based filesystem, the tape will not be rewound before\n"
|
||
"starting the listing.\n\n"
|
||
" dump [-bcdnwx] dev:filespec\n\n"
|
||
"Dump the contents of the specified file (no wildcards allowed) in some\n"
|
||
"human-readable format. The switches control the format; -b bytes, -c\n"
|
||
"characters (ASCII), -w 16-bit words and -d 32-bit double words.\n"
|
||
"By default, bytes, words and double words are dumped in octal, use -x\n"
|
||
"for hex format. If -n is specified on a magtape-based filesystem\n"
|
||
"the tape will not be rewound before looking for the specified file.\n\n"
|
||
" copy [-anc blocks] dev1:src dev2:dest\n\n"
|
||
"Copy a file between file systems. If -a is specified the copy will be\n"
|
||
"performed in ASCII mode which will translate end-of-line characters.\n"
|
||
"If \"-c blocks\" is specified and the destination file system supports\n"
|
||
"contiguous files, the specified # of contiguous file system blocks will\n"
|
||
"be allocated before starting the transfer. If -n is specified on a\n"
|
||
"magtape-based filesystem the tape will not be rewound before looking\n"
|
||
"for the source file.\n\n"
|
||
" type [-n] dev:src\n\n"
|
||
"Type the contents of the file on the terminal. This is equivalent to\n"
|
||
"the command:\n\n"
|
||
"\tcopy -a dev:src /dev/tty\n\n"
|
||
"If -n is specified on a magtape-based filesystem the tape will not be\n"
|
||
"rewound before looking for the file\n\n"
|
||
" delete dev:file\n\n"
|
||
"Delete the specified file from the container file system.\n\n"
|
||
" status\n\n"
|
||
"Display a list of the currently mounted file systems.\n\n"
|
||
" do [-q] cmdFile\n\n"
|
||
"Echo and execute commands from a file. If -q is present suppress the echo.\n\n"
|
||
" help\n\n"
|
||
"Display this help text.\n\n"
|
||
" exit\n"
|
||
" quit\n\n"
|
||
"Terminate execution.\n\n"
|
||
"The following commands are only accepted by magtape-based filesystems:\n\n"
|
||
" rewind dev:\n\n"
|
||
"Position the magtape device to the start of the tape.\n\n"
|
||
" eom dev:\n\n"
|
||
"Position the magtape past the end of the last file already on the tape.\n\n"
|
||
" skipf dev: n\n\n"
|
||
"Skip forward over n files (n > 0).\n\n"
|
||
" skipr dev: n\n\n"
|
||
"Skip backwards over n files (n > 0).\n\n"
|
||
"The special device name \"local:\" may be used to specify files in the\n"
|
||
"local host file system.\n\n"
|
||
"The following container file systems are supported:\n\n");
|
||
|
||
for (filesys = fileSystems; filesys != NULL; filesys = filesys->next)
|
||
printf("%s", filesys->descr);
|
||
}
|
||
|
||
/*++
|
||
* d o E x i t
|
||
*
|
||
* Exit (and Quit) command processing routine.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doExit(void)
|
||
{
|
||
struct mountedFS *mount;
|
||
|
||
for (mount = mounts; mount != NULL; mount = mount->next)
|
||
if (mount != &localMount)
|
||
fclose(mount->container);
|
||
|
||
#ifdef DEBUG
|
||
if ((DEBUGout != NULL) && (DEBUGout != stdout))
|
||
fclose(DEBUGout);
|
||
#endif
|
||
|
||
exit(0);
|
||
}
|
||
|
||
/*++
|
||
* d o R e w i n d
|
||
*
|
||
* Rewind a tape container file, positioning it a=t beginning-of-tape.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doRewind(void)
|
||
{
|
||
struct mountedFS *mount;
|
||
char *fname;
|
||
uint8_t unit, present;
|
||
|
||
if ((mount = findMount("rewind", words[0], &unit, &present, &fname)) != NULL) {
|
||
if ((mount->filesys->flags & FS_TAPE) == 0) {
|
||
fprintf(stderr, "rewind: Command only valid on magtapes\n");
|
||
return;
|
||
}
|
||
|
||
if (*fname == '\0')
|
||
(*mount->filesys->rewind)(mount);
|
||
else fprintf(stderr, "rewind: Does not expect a file name\n");
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* d o E O M
|
||
*
|
||
* Position the tape at the end-of-media.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doEOM(void)
|
||
{
|
||
struct mountedFS *mount;
|
||
char *fname;
|
||
uint8_t unit, present;
|
||
|
||
if ((mount = findMount("eom", words[0], &unit, &present, &fname)) != NULL) {
|
||
if ((mount->filesys->flags & FS_TAPE) == 0) {
|
||
fprintf(stderr, "eom: Command only valid on magtapes\n");
|
||
return;
|
||
}
|
||
|
||
if (*fname == '\0')
|
||
(*mount->filesys->eom)(mount);
|
||
else fprintf(stderr, "eom: Does not expect a file name\n");
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* d o S k i p F o r w
|
||
*
|
||
* Skip forward over the specified number of files or until end-of-media is
|
||
* reached.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doSkipForw(void)
|
||
{
|
||
struct mountedFS *mount;
|
||
char *fname;
|
||
uint8_t unit, present;
|
||
|
||
if ((mount = findMount("skipf", words[0], &unit, &present, &fname)) != NULL) {
|
||
if ((mount->filesys->flags & FS_TAPE) == 0) {
|
||
fprintf(stderr, "skipf: Command only valid on magtapes\n");
|
||
return;
|
||
}
|
||
|
||
if (*fname == '\0') {
|
||
char *endptr;
|
||
unsigned long count = strtoul(words[1], &endptr, 10);
|
||
|
||
if (*endptr != '\0') {
|
||
fprintf(stderr, "skipf: Invalid character in count\n");
|
||
return;
|
||
}
|
||
|
||
if (count != 0)
|
||
(*mount->filesys->skipforw)(mount, count);
|
||
} else fprintf(stderr, "skipf: Does not expect a file name\n");
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* d o S k i p R e v
|
||
*
|
||
* Skip backwards over the specified number of files or beginning-of-tape is
|
||
* reached.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* None
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void doSkipRev(void)
|
||
{
|
||
struct mountedFS *mount;
|
||
char *fname;
|
||
uint8_t unit, present;
|
||
|
||
if ((mount = findMount("skipf", words[0], &unit, &present, &fname)) != NULL) {
|
||
if ((mount->filesys->flags & FS_TAPE) == 0) {
|
||
fprintf(stderr, "skipr: Command only valid on magtapes\n");
|
||
return;
|
||
}
|
||
|
||
if (*fname == '\0') {
|
||
char *endptr;
|
||
unsigned long count = strtoul(words[1], &endptr, 10);
|
||
|
||
if (*endptr != '\0') {
|
||
fprintf(stderr, "skipr: Invalid character in count\n");
|
||
return;
|
||
}
|
||
|
||
if (count != 0)
|
||
(*mount->filesys->skiprev)(mount, count);
|
||
} else fprintf(stderr, "skipr: Does not expect a file name\n");
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* F S i o E x e c u t e
|
||
*
|
||
* Parse a command line and execute any command found
|
||
*
|
||
* Inputs:
|
||
*
|
||
* in - pointer to the command line (zero terminated)
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
static void FSioExecute(
|
||
char *in
|
||
)
|
||
{
|
||
char quote;
|
||
|
||
words = wds;
|
||
args = 0;
|
||
|
||
/*
|
||
* Split the command line into individual words
|
||
*/
|
||
while (*in != '\0') {
|
||
switch (*in) {
|
||
case ' ':
|
||
case '\t':
|
||
in++;
|
||
continue;
|
||
|
||
case '\'':
|
||
case '\"':
|
||
quote = *in++;
|
||
words[args++] = in;
|
||
while (*in != quote) {
|
||
if (*in == '\0') {
|
||
fprintf(stderr, "Missing terminating quote - %c%s\n",
|
||
quote, words[args - 1]);
|
||
return;
|
||
}
|
||
in++;
|
||
}
|
||
*in++ = '\0';
|
||
break;
|
||
|
||
case '#':
|
||
if (args == 0)
|
||
return;
|
||
/* FALLTHROUGH */
|
||
|
||
default:
|
||
words[args++] = in++;
|
||
while ((*in != ' ') && (*in != '\t') && (*in != '\0'))
|
||
in++;
|
||
|
||
if (*in != '\0')
|
||
*in++ = '\0';
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (args != 0) {
|
||
struct command *cmds = cmdTable;
|
||
int i, len, idx = -1;
|
||
|
||
len = strlen(words[0]);
|
||
|
||
for (cmds = cmdTable, i = 0; cmds->name != NULL; cmds++, i++) {
|
||
if (strncmp(cmds->name, words[0], len) == 0) {
|
||
if (idx != -1) {
|
||
idx = -1;
|
||
break;
|
||
}
|
||
idx = i;
|
||
}
|
||
}
|
||
|
||
if (idx != -1) {
|
||
char *switches = cmdTable[idx].switches;
|
||
|
||
swPresent = 0;
|
||
for (i = 0; i < 26; i++)
|
||
swValue[i] = NULL;
|
||
|
||
/*
|
||
* Parse any switches associated with the command
|
||
*/
|
||
if (switches != NULL) {
|
||
int ch;
|
||
char *ptr;
|
||
|
||
while ((ch = getopt(args, words, switches)) != -1) {
|
||
if ((ch == '?') || ((ptr = strchr(switches, ch)) == NULL)) {
|
||
#if !defined(__linux__)
|
||
optreset = 1;
|
||
#endif
|
||
optind = 1;
|
||
return;
|
||
}
|
||
SWSET(ch);
|
||
|
||
if (ptr[1] == ':')
|
||
SWSETVAL(ch, optarg);
|
||
}
|
||
args -= optind;
|
||
words += optind;
|
||
|
||
/*
|
||
* Reset getopt() for subsequent uses.
|
||
*/
|
||
#if !defined(__linux__)
|
||
optreset = 1;
|
||
#endif
|
||
optind = 1;
|
||
} else args--, words++;
|
||
|
||
if ((args < cmdTable[idx].minargs) || (args > cmdTable[idx].maxargs)) {
|
||
fprintf(stderr, "%s: Too %s arguments\n",
|
||
wds[0], args < cmdTable[idx].minargs ? "few" : "many");
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* Execute the command.
|
||
*/
|
||
(*cmdTable[idx].func)();
|
||
} else fprintf(stderr, "Unknown command \"%s\"\n", words[0]);
|
||
}
|
||
}
|
||
|
||
/*++
|
||
* F S i o C o m m a n d s
|
||
*
|
||
* Read and process commands from the specified input stream.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* commands - commands are read from this stream
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* None
|
||
*
|
||
--*/
|
||
void FSioCommands(
|
||
FILE *commands
|
||
)
|
||
{
|
||
#ifdef USE_READLINE
|
||
char *buf, bufr[MAX_CMDLEN];
|
||
|
||
for (;;) {
|
||
if (commands == stdin) {
|
||
if ((buf = readline("fsio> ")) == NULL)
|
||
return;
|
||
|
||
/*
|
||
* Don't add empty lines to history.
|
||
*/
|
||
if (*buf)
|
||
add_history(buf);
|
||
} else {
|
||
int len;
|
||
|
||
if (fgets(bufr, sizeof(bufr), commands) == NULL)
|
||
return;
|
||
|
||
buf = bufr;
|
||
|
||
if (verbose)
|
||
printf("fsio> %s", bufr);
|
||
|
||
/*
|
||
* Remove any trailing newline
|
||
*/
|
||
len = strlen(buf);
|
||
if (buf[len - 1] == '\n')
|
||
buf[len - 1] = '\0';
|
||
}
|
||
|
||
/*
|
||
* Parse the command line and execute any command found
|
||
*/
|
||
FSioExecute(buf);
|
||
|
||
if (commands == stdin)
|
||
free(buf);
|
||
}
|
||
#else
|
||
char buf[MAX_CMDLEN];
|
||
|
||
for (;;) {
|
||
int len;
|
||
|
||
if (isatty(fileno(commands)))
|
||
fputs("fsio> ", stdout);
|
||
|
||
if (fgets(buf, sizeof(buf), commands) == NULL)
|
||
return;
|
||
|
||
if (verbose && !isatty(fileno(commands)))
|
||
printf("fsio> %s", buf);
|
||
|
||
/*
|
||
* Remove any trailing newline
|
||
*/
|
||
len = strlen(buf);
|
||
if (buf[len - 1] == '\n')
|
||
buf[len - 1] = '\0';
|
||
|
||
/*
|
||
* Parse the command line and execute any command found
|
||
*/
|
||
FSioExecute(buf);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
/*++
|
||
* F S i o R e a d B l o b
|
||
*
|
||
* Read an arbitrary sized blob of binary data from the container file. The
|
||
* caller is responsible for making sure that the buffer has sufficient
|
||
* space for the blob.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* offset - offset in the container file to start the read
|
||
* size - size of the data to read (in bytes)
|
||
* buf - pointer to the buffer to receive the data
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The buffer will be overwrittn by the contents of the blob from the
|
||
* container file system
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if read was successful, 0 otherwise
|
||
*
|
||
--*/
|
||
int FSioReadBlob(
|
||
struct mountedFS *mount,
|
||
off_t offset,
|
||
unsigned int size,
|
||
void *buf
|
||
)
|
||
{
|
||
if (fseeko(mount->container, offset, SEEK_SET) == 0)
|
||
return fread(buf, size, 1, mount->container);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*++
|
||
* F S i o W r i t e B l o b
|
||
*
|
||
* Write an arbitrary sized blob of binary to from the container file.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* offset - offset in the container file to start the write
|
||
* size - size of the data to write (in bytes)
|
||
* buf - pointer to the buffer with the data
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if write was successful, 0 otherwise
|
||
*
|
||
--*/
|
||
int FSioWriteBlob(
|
||
struct mountedFS *mount,
|
||
off_t offset,
|
||
unsigned int size,
|
||
void *buf
|
||
)
|
||
{
|
||
if (fseeko(mount->container, offset, SEEK_SET) == 0)
|
||
return fwrite(buf, size, 1, mount->container);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*++
|
||
* F S i o R e a d B l o c k
|
||
*
|
||
* Read a specified block from the container file. The caller is responsible
|
||
* for making sure that the buffer has sufficient space for the block.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* block - logical block # in the range 0 - N
|
||
* buf - pointer to the buffer to receive the data
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The buffer will be overwritten by the contents of the block from
|
||
* the container file system
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if read was successful, 0 otherwise
|
||
*
|
||
--*/
|
||
int FSioReadBlock(
|
||
struct mountedFS *mount,
|
||
unsigned int block,
|
||
void *buf
|
||
)
|
||
{
|
||
off_t offset = block * mount->blocksz;
|
||
|
||
if (fseeko(mount->container, offset + mount->skip, SEEK_SET) == 0)
|
||
return fread(buf, mount->blocksz, 1, mount->container);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*++
|
||
* F S i o W r i t e B l o c k
|
||
*
|
||
* Write a specified block to the container file.
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* block - logical block # in the range 0 - N
|
||
* buf - pointer to the buffer containing the data
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if write was successful, 0 otherwise
|
||
*
|
||
--*/
|
||
int FSioWriteBlock(
|
||
struct mountedFS *mount,
|
||
unsigned int block,
|
||
void *buf
|
||
)
|
||
{
|
||
off_t offset = block * mount->blocksz;
|
||
|
||
if (fseeko(mount->container, offset + mount->skip, SEEK_SET) == 0)
|
||
return fwrite(buf, mount->blocksz, 1, mount->container);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*++
|
||
* F S i o R e a d S e c t o r
|
||
*
|
||
* Read a sector of a specified size from the container. The caller is
|
||
* responsible for making sure that the buffer has sufficient space for
|
||
* the sector. This routine is used when file system blocks are constructed
|
||
* from a number of sectors which are interleaved on the physical disk (e.g.
|
||
* RX02 floppies).
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* sector - logical sector # in the range 0 - N
|
||
* size - size of each sector (in bytes)
|
||
* buf - pointer to the buffer to receive the data
|
||
*
|
||
* Outputs:
|
||
*
|
||
* The buffer will be overwritten by the contents of the sector from
|
||
* the container file system
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if read was successful, 0 otherwise
|
||
*
|
||
--*/
|
||
int FSioReadSector(
|
||
struct mountedFS *mount,
|
||
unsigned int sector,
|
||
unsigned int size,
|
||
void *buf
|
||
)
|
||
{
|
||
off_t offset = sector * size;
|
||
|
||
if (fseeko(mount->container, offset + mount->skip, SEEK_SET) == 0)
|
||
return fread(buf, size, 1, mount->container);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*++
|
||
* F S i o W r i t e S e c t o r
|
||
*
|
||
* Write a sector of a specified size to the container. This routine is used
|
||
* when file system blocks are constructed from a number of sectors which are
|
||
* interleaved on the physical disk (e.g. RX02 floppies).
|
||
*
|
||
* Inputs:
|
||
*
|
||
* mount - pointer to a mounted file system descriptor
|
||
* sector - logical sector # in the range 0 - N
|
||
* size - size of each sector (in bytes)
|
||
* buf - pointer to the buffer with the data
|
||
*
|
||
* Outputs:
|
||
*
|
||
* None
|
||
*
|
||
* Returns:
|
||
*
|
||
* 1 if write was successful, 0 otherwise
|
||
*
|
||
--*/
|
||
int FSioWriteSector(
|
||
struct mountedFS *mount,
|
||
unsigned int sector,
|
||
unsigned int size,
|
||
void *buf
|
||
)
|
||
{
|
||
off_t offset = sector * size;
|
||
|
||
if (fseeko(mount->container, offset + mount->skip, SEEK_SET) == 0)
|
||
return fwrite(buf, size, 1, mount->container);
|
||
|
||
return 0;
|
||
}
|