463535c87d
git-svn-id: https://svn.code.sf.net/p/freedos/svn/kernel/branches/UNSTABLE@1025 6ac86273-5f31-0410-b378-82cca8765d1b
1111 lines
29 KiB
C
1111 lines
29 KiB
C
/***************************************************************
|
|
|
|
sys.c
|
|
DOS-C
|
|
|
|
sys utility for DOS-C
|
|
|
|
Copyright (c) 1991
|
|
Pasquale J. Villani
|
|
All Rights Reserved
|
|
|
|
This file is part of DOS-C.
|
|
|
|
DOS-C is free software; you can redistribute it and/or modify it under the
|
|
terms of the GNU General Public License as published by the Free Software
|
|
Foundation; either version 2, or (at your option) any later version.
|
|
|
|
DOS-C is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
DOS-C; see the file COPYING. If not, write to the Free Software Foundation,
|
|
675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
***************************************************************/
|
|
|
|
#define DEBUG
|
|
/* #define DDEBUG */
|
|
|
|
#define SYS_VERSION "v3.4"
|
|
|
|
#include <stdlib.h>
|
|
#include <dos.h>
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#ifdef __TURBOC__
|
|
#include <mem.h>
|
|
#else
|
|
#include <memory.h>
|
|
#endif
|
|
#include <string.h>
|
|
#ifdef __TURBOC__
|
|
#include <dir.h>
|
|
#endif
|
|
#define SYS_MAXPATH 260
|
|
#include "portab.h"
|
|
#include "algnbyte.h"
|
|
#include "device.h"
|
|
#include "dcb.h"
|
|
#include "xstructs.h"
|
|
#include "date.h"
|
|
#include "../hdr/time.h"
|
|
#include "fat.h"
|
|
|
|
/* These definitions deliberately put here instead of
|
|
* #including <stdio.h> to make executable MUCH smaller
|
|
* using [s]printf from prf.c!
|
|
*/
|
|
extern int VA_CDECL printf(const char * fmt, ...);
|
|
extern int VA_CDECL sprintf(char * buff, const char * fmt, ...);
|
|
|
|
#include "fat12com.h"
|
|
#include "fat16com.h"
|
|
#ifdef WITHFAT32
|
|
#include "fat32chs.h"
|
|
#include "fat32lba.h"
|
|
#endif
|
|
|
|
#ifndef __WATCOMC__
|
|
|
|
#include <io.h>
|
|
|
|
#else
|
|
|
|
long filelength(int __handle);
|
|
#pragma aux filelength = \
|
|
"mov ax, 0x4202" \
|
|
"xor cx, cx" \
|
|
"xor dx, dx" \
|
|
"int 0x21" \
|
|
"push ax" \
|
|
"push dx" \
|
|
"mov ax, 0x4200" \
|
|
"xor cx, cx" \
|
|
"xor dx, dx" \
|
|
"int 0x21" \
|
|
"pop dx" \
|
|
"pop ax" \
|
|
parm [bx] \
|
|
modify [cx] \
|
|
value [dx ax];
|
|
|
|
extern int unlink(const char *pathname);
|
|
|
|
/* some non-conforming functions to make the executable smaller */
|
|
int open(const char *pathname, int flags, ...)
|
|
{
|
|
int handle;
|
|
int result = (flags & O_CREAT ?
|
|
_dos_creat(pathname, _A_NORMAL, &handle) :
|
|
_dos_open(pathname, flags & (O_RDONLY | O_WRONLY | O_RDWR),
|
|
&handle));
|
|
|
|
return (result == 0 ? handle : -1);
|
|
}
|
|
|
|
int read(int fd, void *buf, unsigned count)
|
|
{
|
|
unsigned bytes;
|
|
int result = _dos_read(fd, buf, count, &bytes);
|
|
|
|
return (result == 0 ? bytes : -1);
|
|
}
|
|
|
|
int write(int fd, const void *buf, unsigned count)
|
|
{
|
|
unsigned bytes;
|
|
int result = _dos_write(fd, buf, count, &bytes);
|
|
|
|
return (result == 0 ? bytes : -1);
|
|
}
|
|
|
|
#define close _dos_close
|
|
|
|
int stat(const char *file_name, struct stat *buf)
|
|
{
|
|
struct find_t find_tbuf;
|
|
UNREFERENCED_PARAMETER(buf);
|
|
|
|
return _dos_findfirst(file_name, _A_NORMAL | _A_HIDDEN | _A_SYSTEM, &find_tbuf);
|
|
}
|
|
|
|
/* WATCOM's getenv is case-insensitive which wastes a lot of space
|
|
for our purposes. So here's a simple case-sensitive one */
|
|
char *getenv(const char *name)
|
|
{
|
|
char **envp, *ep;
|
|
const char *np;
|
|
char ec, nc;
|
|
|
|
for (envp = environ; (ep = *envp) != NULL; envp++) {
|
|
np = name;
|
|
do {
|
|
ec = *ep++;
|
|
nc = *np++;
|
|
if (nc == 0) {
|
|
if (ec == '=')
|
|
return ep;
|
|
break;
|
|
}
|
|
} while (ec == nc);
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
BYTE pgm[] = "SYS";
|
|
|
|
void put_boot(int, char *, char *, int, int);
|
|
BOOL check_space(COUNT, ULONG);
|
|
BOOL get_full_path(BYTE * srcPath, BYTE * rootPath, BYTE * filename, BYTE *source);
|
|
BOOL copy(BYTE *source, COUNT drive, BYTE * filename);
|
|
|
|
#define SEC_SIZE 512
|
|
#define COPY_SIZE 0x7e00
|
|
|
|
struct bootsectortype {
|
|
UBYTE bsJump[3];
|
|
char OemName[8];
|
|
UWORD bsBytesPerSec;
|
|
UBYTE bsSecPerClust;
|
|
UWORD bsResSectors;
|
|
UBYTE bsFATs;
|
|
UWORD bsRootDirEnts;
|
|
UWORD bsSectors;
|
|
UBYTE bsMedia;
|
|
UWORD bsFATsecs;
|
|
UWORD bsSecPerTrack;
|
|
UWORD bsHeads;
|
|
ULONG bsHiddenSecs;
|
|
ULONG bsHugeSectors;
|
|
UBYTE bsDriveNumber;
|
|
UBYTE bsReserved1;
|
|
UBYTE bsBootSignature;
|
|
ULONG bsVolumeID;
|
|
char bsVolumeLabel[11];
|
|
char bsFileSysType[8];
|
|
};
|
|
|
|
struct bootsectortype32 {
|
|
UBYTE bsJump[3];
|
|
char OemName[8];
|
|
UWORD bsBytesPerSec;
|
|
UBYTE bsSecPerClust;
|
|
UWORD bsResSectors;
|
|
UBYTE bsFATs;
|
|
UWORD bsRootDirEnts;
|
|
UWORD bsSectors;
|
|
UBYTE bsMedia;
|
|
UWORD bsFATsecs;
|
|
UWORD bsSecPerTrack;
|
|
UWORD bsHeads;
|
|
ULONG bsHiddenSecs;
|
|
ULONG bsHugeSectors;
|
|
ULONG bsBigFatSize;
|
|
UBYTE bsFlags;
|
|
UBYTE bsMajorVersion;
|
|
UWORD bsMinorVersion;
|
|
ULONG bsRootCluster;
|
|
UWORD bsFSInfoSector;
|
|
UWORD bsBackupBoot;
|
|
ULONG bsReserved2[3];
|
|
UBYTE bsDriveNumber;
|
|
UBYTE bsReserved3;
|
|
UBYTE bsExtendedSignature;
|
|
ULONG bsSerialNumber;
|
|
char bsVolumeLabel[11];
|
|
char bsFileSystemID[8];
|
|
};
|
|
|
|
/*
|
|
* globals needed by put_boot & check_space
|
|
*/
|
|
enum {FAT12 = 12, FAT16 = 16, FAT32 = 32} fs; /* file system type */
|
|
/* static */ struct xfreespace x; /* we make this static to be 0 by default -
|
|
this avoids FAT misdetections */
|
|
|
|
#define SBOFFSET 11
|
|
#define SBSIZE (sizeof(struct bootsectortype) - SBOFFSET)
|
|
#define SBSIZE32 (sizeof(struct bootsectortype32) - SBOFFSET)
|
|
|
|
/* essentially - verify alignment on byte boundaries at compile time */
|
|
struct VerifyBootSectorSize {
|
|
char failure1[sizeof(struct bootsectortype) == 62 ? 1 : -1];
|
|
char failure2[sizeof(struct bootsectortype) == 62 ? 1 : 0];
|
|
/* (Watcom has a nice warning for this, by the way) */
|
|
};
|
|
|
|
int FDKrnConfigMain(int argc, char **argv);
|
|
|
|
/* FreeDOS sys, we default to our kernel and load segment, but
|
|
if not found (or explicitly given) support OEM DOS variants
|
|
(such as DR-DOS or a FreeDOS kernel mimicing other DOSes).
|
|
PC-DOS requires particular steps from the boot loader, which
|
|
we do not currently fullfill, so it will not boot and MS-DOS
|
|
uses different names, so it also will not work.
|
|
*/
|
|
#define FDKERNEL "KERNEL.SYS"
|
|
#define OEMKERNEL "IBMBIO.COM"
|
|
#define OEMDOS "IBMDOS.COM"
|
|
#define FDLOADSEG 0x60
|
|
#define OEMLOADSEG 0x70
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
COUNT drive; /* destination drive */
|
|
COUNT drivearg = 0; /* drive argument position */
|
|
COUNT srcarg = 0; /* source argument position */
|
|
BYTE *bsFile = NULL; /* user specified destination boot sector */
|
|
unsigned srcDrive; /* source drive */
|
|
BYTE srcPath[SYS_MAXPATH]; /* user specified source drive and/or path */
|
|
BYTE rootPath[4]; /* alternate source path to try if not '\0' */
|
|
BYTE srcFile[SYS_MAXPATH]; /* full path+name of [kernel] file to copy */
|
|
WORD slen;
|
|
int argno = 0;
|
|
int bootonly = 0;
|
|
int both = 0;
|
|
char *kernel_name = FDKERNEL;
|
|
int load_segment = FDLOADSEG;
|
|
int altkern = 0; /* use OEM kernel values instead of FD ones */
|
|
|
|
printf("FreeDOS System Installer " SYS_VERSION ", " __DATE__ "\n\n");
|
|
|
|
if (argc > 1 && memicmp(argv[1], "CONFIG", 6) == 0)
|
|
{
|
|
exit(FDKrnConfigMain(argc, argv));
|
|
}
|
|
|
|
for(argno = 1; argno < argc; argno++)
|
|
{
|
|
char *argp = argv[argno];
|
|
|
|
if (argp[1] == ':' && argp[2] == '\0' && drivearg <= srcarg)
|
|
drivearg = argno;
|
|
|
|
if (srcarg == 0)
|
|
{
|
|
srcarg = argno;
|
|
}
|
|
else if (memicmp(argp, "BOTH", 4) == 0 && !both)
|
|
{
|
|
both = 1;
|
|
}
|
|
else if (argp[0] == '/') /* optional switch */
|
|
{
|
|
argp++; /* skip past the '/' character */
|
|
|
|
if (memicmp(argp, "OEM", 3) == 0)
|
|
{
|
|
altkern = 1; /* kernel is split into 2 files and update BS name */
|
|
kernel_name = OEMKERNEL;
|
|
load_segment = OEMLOADSEG;
|
|
}
|
|
else if (memicmp(argp, "BOOTONLY", 8) == 0)
|
|
{
|
|
bootonly = 1;
|
|
}
|
|
else if (argno + 1 < argc) /* two part options, /SWITCH VALUE */
|
|
{
|
|
argno++;
|
|
if (toupper(*argp) == 'K') /* set Kernel name */
|
|
{
|
|
kernel_name = argv[argno];
|
|
}
|
|
else if (toupper(*argp) == 'L') /* set Load segment */
|
|
{
|
|
load_segment = (int)strtol(argv[argno], NULL, 16);
|
|
}
|
|
}
|
|
}
|
|
else if (drivearg != argno)
|
|
{
|
|
if (bsFile == NULL)
|
|
{
|
|
bsFile = argp;
|
|
}
|
|
else
|
|
{
|
|
drivearg = 0;
|
|
break;
|
|
}
|
|
}
|
|
} /* for() */
|
|
|
|
if (drivearg == 0)
|
|
{
|
|
printf(
|
|
"Usage: \n"
|
|
"%s [source] drive: [bootsect [BOTH]] [/BOOTONLY] [/OEM] [/K name] [/L segm]\n"
|
|
" source = A:,B:,C:\\KERNEL\\BIN\\,etc., or current directory if not given\n"
|
|
" drive = A,B,etc.\n"
|
|
" bootsect = name of 512-byte boot sector file image for drive:\n"
|
|
" to write to *instead* of real boot sector\n"
|
|
" BOTH : write to *both* the real boot sector and the image file\n"
|
|
" /BOOTONLY: do *not* copy kernel / shell, only update boot sector or image\n"
|
|
" /OEM : indicates kernel is IBMIO.SYS/IBMDOS.SYS loaded at 0x70\n"
|
|
" /K name : name of kernel to use instead of KERNEL.SYS\n"
|
|
" /L segm : hex load segment to use instead of 60\n"
|
|
"%s CONFIG /help\n", pgm, pgm);
|
|
exit(1);
|
|
}
|
|
drive = toupper(argv[drivearg][0]) - 'A';
|
|
|
|
if (drive < 0 || drive >= 26)
|
|
{
|
|
printf("%s: drive %c must be A:..Z:\n", pgm,
|
|
*argv[(argc == 3 ? 2 : 1)]);
|
|
exit(1);
|
|
}
|
|
|
|
srcPath[0] = '\0';
|
|
if (drivearg > srcarg && srcarg)
|
|
{
|
|
strncpy(srcPath, argv[srcarg], SYS_MAXPATH - 12);
|
|
/* leave room for COMMAND.COM\0 */
|
|
srcPath[SYS_MAXPATH - 13] = '\0';
|
|
/* make sure srcPath + "file" is a valid path */
|
|
slen = strlen(srcPath);
|
|
if ((srcPath[slen - 1] != ':') &&
|
|
((srcPath[slen - 1] != '\\') || (srcPath[slen - 1] != '/')))
|
|
{
|
|
srcPath[slen] = '\\';
|
|
slen++;
|
|
srcPath[slen] = '\0';
|
|
}
|
|
}
|
|
|
|
/* Get source drive */
|
|
if ((strlen(srcPath) > 1) && (srcPath[1] == ':')) /* src specifies drive */
|
|
srcDrive = toupper(*srcPath) - 'A';
|
|
else /* src doesn't specify drive, so assume current drive */
|
|
{
|
|
#ifdef __TURBOC__
|
|
srcDrive = (unsigned) getdisk();
|
|
#else
|
|
_dos_getdrive(&srcDrive);
|
|
srcDrive--;
|
|
#endif
|
|
}
|
|
|
|
/* Don't try root if src==dst drive or source path given */
|
|
if ((drive == srcDrive)
|
|
|| (*srcPath
|
|
&& ((srcPath[1] != ':') || ((srcPath[1] == ':') && srcPath[2]))))
|
|
*rootPath = '\0';
|
|
else
|
|
sprintf(rootPath, "%c:\\", 'A' + srcDrive);
|
|
|
|
|
|
/* unless we are only setting boot sector, verify kernel file exists */
|
|
if (!bootonly)
|
|
{
|
|
/* if FDKERNEL not found and an explicit kernel name was not specified,
|
|
then see if OEM kernel available and switch to it instead. */
|
|
if (!get_full_path(srcPath, rootPath, kernel_name, srcFile) && (kernel_name == FDKERNEL))
|
|
{
|
|
if (!get_full_path(srcPath, rootPath, OEMKERNEL, srcFile))
|
|
{
|
|
printf("\n%s: failed to find kernel file %s\n", pgm, kernel_name);
|
|
exit(1);
|
|
}
|
|
else /* else OEM kernel found, so switch modes */
|
|
{
|
|
altkern = 1;
|
|
kernel_name = OEMKERNEL;
|
|
load_segment = OEMLOADSEG;
|
|
}
|
|
}
|
|
}
|
|
|
|
printf("Processing boot sector...\n");
|
|
put_boot(drive, bsFile, kernel_name, load_segment, both);
|
|
|
|
if (!bootonly)
|
|
{
|
|
if (!copy(srcFile, drive, kernel_name))
|
|
{
|
|
printf("\n%s: cannot copy \"%s\"\n", pgm, kernel_name);
|
|
exit(1);
|
|
} /* copy kernel */
|
|
|
|
if (altkern)
|
|
{
|
|
if ( (!get_full_path(srcPath, rootPath, OEMDOS, srcFile)) ||
|
|
(!copy(srcFile, drive, OEMDOS)) )
|
|
{
|
|
printf("\n%s: cannot copy \"%s\"\n", pgm, OEMDOS);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/* copy command.com, 1st try source path, then try %COMSPEC% */
|
|
if (!get_full_path(srcPath, rootPath, "COMMAND.COM", srcFile))
|
|
{
|
|
char *comspec = getenv("COMSPEC");
|
|
if ( (comspec == NULL) ||
|
|
(!get_full_path(comspec, NULL, "COMMAND.COM", srcFile)) )
|
|
{
|
|
printf("\n%s: failed to find command interpreter (shell) file %s\n", pgm, "COMMAND.COM");
|
|
exit(1);
|
|
}
|
|
printf("%s: Using shell from %COMSPEC% \"%s\"\n", pgm, comspec);
|
|
}
|
|
if (!copy(srcFile, drive, "COMMAND.COM"))
|
|
{
|
|
printf("\n%s: cannot copy \"%s\"\n", pgm, "COMMAND.COM");
|
|
exit(1);
|
|
} /* copy shell */
|
|
}
|
|
|
|
printf("\nSystem transferred.\n");
|
|
return 0;
|
|
}
|
|
|
|
#ifdef DDEBUG
|
|
VOID dump_sector(unsigned char far * sec)
|
|
{
|
|
COUNT x, y;
|
|
char c;
|
|
|
|
for (x = 0; x < 32; x++)
|
|
{
|
|
printf("%03X ", x * 16);
|
|
for (y = 0; y < 16; y++)
|
|
{
|
|
printf("%02X ", sec[x * 16 + y]);
|
|
}
|
|
for (y = 0; y < 16; y++)
|
|
{
|
|
c = sec[x * 16 + y];
|
|
if (isprint(c))
|
|
printf("%c", c);
|
|
else
|
|
printf(".");
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef __WATCOMC__
|
|
|
|
int absread(int DosDrive, int nsects, int foo, void *diskReadPacket);
|
|
#pragma aux absread = \
|
|
"push bp" \
|
|
"int 0x25" \
|
|
"sbb ax, ax" \
|
|
"popf" \
|
|
"pop bp" \
|
|
parm [ax] [cx] [dx] [bx] \
|
|
modify [si di] \
|
|
value [ax];
|
|
|
|
int abswrite(int DosDrive, int nsects, int foo, void *diskReadPacket);
|
|
#pragma aux abswrite = \
|
|
"push bp" \
|
|
"int 0x26" \
|
|
"sbb ax, ax" \
|
|
"popf" \
|
|
"pop bp" \
|
|
parm [ax] [cx] [dx] [bx] \
|
|
modify [si di] \
|
|
value [ax];
|
|
|
|
int fat32readwrite(int DosDrive, void *diskReadPacket, unsigned intno);
|
|
#pragma aux fat32readwrite = \
|
|
"mov ax, 0x7305" \
|
|
"mov cx, 0xffff" \
|
|
"int 0x21" \
|
|
"sbb ax, ax" \
|
|
parm [dx] [bx] [si] \
|
|
modify [cx dx si] \
|
|
value [ax];
|
|
|
|
void reset_drive(int DosDrive);
|
|
#pragma aux reset_drive = \
|
|
"push ds" \
|
|
"inc dx" \
|
|
"mov ah, 0xd" \
|
|
"int 0x21" \
|
|
"mov ah,0x32" \
|
|
"int 0x21" \
|
|
"pop ds" \
|
|
parm [dx] \
|
|
modify [ax bx];
|
|
|
|
void truename(char far *dest, const char *src);
|
|
#pragma aux truename = \
|
|
"mov ah,0x60" \
|
|
"int 0x21" \
|
|
parm [es di] [si];
|
|
|
|
int generic_block_ioctl(unsigned drive, unsigned cx, unsigned char *par);
|
|
#pragma aux generic_block_ioctl = \
|
|
"mov ax, 0x440d" \
|
|
"int 0x21" \
|
|
"sbb ax, ax" \
|
|
value [ax] \
|
|
parm [bx] [cx] [dx]; /* BH must be 0 for lock! */
|
|
|
|
#else
|
|
|
|
#ifndef __TURBOC__
|
|
|
|
int2526readwrite(int DosDrive, void *diskReadPacket, unsigned intno)
|
|
{
|
|
union REGS regs;
|
|
|
|
regs.h.al = (BYTE) DosDrive;
|
|
regs.x.bx = (short)diskReadPacket;
|
|
regs.x.cx = 0xffff;
|
|
|
|
int86(intno, ®s, ®s);
|
|
|
|
return regs.x.cflag;
|
|
}
|
|
|
|
#define absread(DosDrive, foo, cx, diskReadPacket) \
|
|
int2526readwrite(DosDrive, diskReadPacket, 0x25)
|
|
|
|
#define abswrite(DosDrive, foo, cx, diskReadPacket) \
|
|
int2526readwrite(DosDrive, diskReadPacket, 0x26)
|
|
|
|
#endif
|
|
|
|
int fat32readwrite(int DosDrive, void *diskReadPacket, unsigned intno)
|
|
{
|
|
union REGS regs;
|
|
|
|
regs.x.ax = 0x7305;
|
|
regs.h.dl = DosDrive;
|
|
regs.x.bx = (short)diskReadPacket;
|
|
regs.x.cx = 0xffff;
|
|
regs.x.si = intno;
|
|
intdos(®s, ®s);
|
|
|
|
return regs.x.cflag;
|
|
} /* fat32readwrite */
|
|
|
|
void reset_drive(int DosDrive)
|
|
{
|
|
union REGS regs;
|
|
|
|
regs.h.ah = 0xd;
|
|
intdos(®s, ®s);
|
|
regs.h.ah = 0x32;
|
|
regs.h.dl = DosDrive + 1;
|
|
intdos(®s, ®s);
|
|
} /* reset_drive */
|
|
|
|
int generic_block_ioctl(unsigned drive, unsigned cx, unsigned char *par)
|
|
{
|
|
union REGS regs;
|
|
|
|
regs.x.ax = 0x440d;
|
|
regs.x.cx = cx;
|
|
regs.x.dx = (unsigned)par;
|
|
regs.x.bx = drive; /* BH must be 0 for lock! */
|
|
intdos(®s, ®s);
|
|
return regs.x.cflag;
|
|
} /* generic_block_ioctl */
|
|
|
|
void truename(char *dest, const char *src)
|
|
{
|
|
union REGS regs;
|
|
struct SREGS sregs;
|
|
|
|
regs.h.ah = 0x60;
|
|
sregs.es = FP_SEG(dest);
|
|
regs.x.di = FP_OFF(dest);
|
|
sregs.ds = FP_SEG(src);
|
|
regs.x.si = FP_OFF(src);
|
|
intdosx(®s, ®s, &sregs);
|
|
} /* truename */
|
|
|
|
#endif
|
|
|
|
int MyAbsReadWrite(int DosDrive, int count, ULONG sector, void *buffer,
|
|
int write)
|
|
{
|
|
struct {
|
|
unsigned long sectorNumber;
|
|
unsigned short count;
|
|
void far *address;
|
|
} diskReadPacket;
|
|
|
|
diskReadPacket.sectorNumber = sector;
|
|
diskReadPacket.count = count;
|
|
diskReadPacket.address = buffer;
|
|
|
|
if ((!write && absread(DosDrive, -1, -1, &diskReadPacket) == -1)
|
|
|| (write && abswrite(DosDrive, -1, -1, &diskReadPacket) == -1))
|
|
{
|
|
#ifdef WITHFAT32
|
|
return fat32readwrite(DosDrive + 1, &diskReadPacket, write);
|
|
#else
|
|
return 0xff;
|
|
#endif
|
|
}
|
|
return 0;
|
|
} /* MyAbsReadWrite */
|
|
|
|
#ifdef __WATCOMC__
|
|
|
|
unsigned getextdrivespace(void far *drivename, void *buf, unsigned buf_size);
|
|
#pragma aux getextdrivespace = \
|
|
"mov ax, 0x7303" \
|
|
"stc" \
|
|
"int 0x21" \
|
|
"sbb ax, ax" \
|
|
parm [es dx] [di] [cx] \
|
|
value [ax];
|
|
|
|
#else /* !defined __WATCOMC__ */
|
|
|
|
unsigned getextdrivespace(void *drivename, void *buf, unsigned buf_size)
|
|
{
|
|
union REGS regs;
|
|
struct SREGS sregs;
|
|
|
|
regs.x.ax = 0x7303; /* get extended drive free space */
|
|
|
|
sregs.es = FP_SEG(buf);
|
|
regs.x.di = FP_OFF(buf);
|
|
sregs.ds = FP_SEG(drivename);
|
|
regs.x.dx = FP_OFF(drivename);
|
|
|
|
regs.x.cx = buf_size;
|
|
|
|
intdosx(®s, ®s, &sregs);
|
|
return regs.x.ax == 0x7300 || regs.x.cflag;
|
|
} /* getextdrivespace */
|
|
|
|
#endif /* defined __WATCOMC__ */
|
|
|
|
#ifdef __WATCOMC__
|
|
/*
|
|
* If BIOS has got LBA extensions, after the Int 13h call BX will be 0xAA55.
|
|
* If extended disk access functions are supported, bit 0 of CX will be set.
|
|
*/
|
|
BOOL haveLBA(void); /* return TRUE if we have LBA BIOS, FALSE otherwise */
|
|
#pragma aux haveLBA = \
|
|
"mov ax, 0x4100" /* IBM/MS Int 13h Extensions - installation check */ \
|
|
"mov bx, 0x55AA" \
|
|
"mov dl, 0x80" \
|
|
"int 0x13" \
|
|
"xor ax, ax" \
|
|
"cmp bx, 0xAA55" \
|
|
"jne quit" \
|
|
"and cx, 1" \
|
|
"xchg cx, ax" \
|
|
"quit:" \
|
|
modify [bx cx] \
|
|
value [ax];
|
|
#else
|
|
|
|
BOOL haveLBA(void)
|
|
{
|
|
union REGS r;
|
|
r.x.ax = 0x4100;
|
|
r.x.bx = 0x55AA;
|
|
r.h.dl = 0x80;
|
|
int86(0x13, &r, &r);
|
|
return r.x.bx == 0xAA55 && r.x.cx & 1;
|
|
}
|
|
#endif
|
|
|
|
void correct_bpb(struct bootsectortype *default_bpb,
|
|
struct bootsectortype *oldboot)
|
|
{
|
|
/* don't touch partitions (floppies most likely) that don't have hidden
|
|
sectors */
|
|
if (default_bpb->bsHiddenSecs == 0)
|
|
return;
|
|
#ifdef DEBUG
|
|
printf("Old boot sector values: sectors/track: %u, heads: %u, hidden: %lu\n",
|
|
oldboot->bsSecPerTrack, oldboot->bsHeads, oldboot->bsHiddenSecs);
|
|
printf("Default and new boot sector values: sectors/track: %u, heads: %u, "
|
|
"hidden: %lu\n", default_bpb->bsSecPerTrack, default_bpb->bsHeads,
|
|
default_bpb->bsHiddenSecs);
|
|
#endif
|
|
|
|
oldboot->bsSecPerTrack = default_bpb->bsSecPerTrack;
|
|
oldboot->bsHeads = default_bpb->bsHeads;
|
|
oldboot->bsHiddenSecs = default_bpb->bsHiddenSecs;
|
|
}
|
|
|
|
void put_boot(int drive, char *bsFile, char *kernel_name, int load_seg, int both)
|
|
{
|
|
#ifdef WITHFAT32
|
|
struct bootsectortype32 *bs32;
|
|
#endif
|
|
struct bootsectortype *bs;
|
|
static unsigned char oldboot[SEC_SIZE], newboot[SEC_SIZE];
|
|
static unsigned char default_bpb[0x5c];
|
|
|
|
#ifdef DEBUG
|
|
printf("Reading old bootsector from drive %c:\n", drive + 'A');
|
|
#endif
|
|
|
|
/* lock drive */
|
|
generic_block_ioctl(drive + 1, 0x84a, NULL);
|
|
|
|
reset_drive(drive);
|
|
/* suggestion: allow reading from a boot sector or image file here */
|
|
if (MyAbsReadWrite(drive, 1, 0, oldboot, 0) != 0)
|
|
{
|
|
printf("can't read old boot sector for drive %c:\n", drive + 'A');
|
|
exit(1);
|
|
}
|
|
|
|
#ifdef DDEBUG
|
|
printf("Old Boot Sector:\n");
|
|
dump_sector(oldboot);
|
|
#endif
|
|
|
|
bs = (struct bootsectortype *)&oldboot;
|
|
|
|
if (bs->bsBytesPerSec != SEC_SIZE)
|
|
{
|
|
printf("Sector size is not 512 but %d bytes - not currently supported!\n",
|
|
bs->bsBytesPerSec);
|
|
exit(1); /* Japan?! */
|
|
}
|
|
|
|
{
|
|
/* see "FAT: General Overview of On-Disk Format" v1.02, 5.V.1999
|
|
* (http://www.nondot.org/sabre/os/files/FileSystems/FatFormat.pdf)
|
|
*/
|
|
ULONG fatSize, totalSectors, dataSectors, clusters;
|
|
UCOUNT rootDirSectors;
|
|
|
|
bs32 = (struct bootsectortype32 *)&oldboot;
|
|
rootDirSectors = (bs->bsRootDirEnts * DIRENT_SIZE /* 32 */
|
|
+ bs32->bsBytesPerSec - 1) / bs32->bsBytesPerSec;
|
|
fatSize = bs32->bsFATsecs ? bs32->bsFATsecs : bs32->bsBigFatSize;
|
|
totalSectors = bs32->bsSectors ? bs32->bsSectors : bs32->bsHugeSectors;
|
|
dataSectors = totalSectors
|
|
- bs32->bsResSectors - (bs32->bsFATs * fatSize) - rootDirSectors;
|
|
clusters = dataSectors / bs32->bsSecPerClust;
|
|
|
|
if (clusters < FAT_MAGIC) /* < 4085 */
|
|
fs = FAT12;
|
|
else if (clusters < FAT_MAGIC16) /* < 65525 */
|
|
fs = FAT16;
|
|
else
|
|
fs = FAT32;
|
|
}
|
|
|
|
/* bit 0 set if function to use current BPB, clear if Device
|
|
BIOS Parameter Block field contains new default BPB
|
|
bit 1 set if function to use track layout fields only
|
|
must be clear if CL=60h
|
|
bit 2 set if all sectors in track same size (should be set) (RBIL) */
|
|
default_bpb[0] = 4;
|
|
|
|
if (fs == FAT32)
|
|
{
|
|
printf("FAT type: FAT32\n");
|
|
/* get default bpb (but not for floppies) */
|
|
if (drive >= 2 &&
|
|
generic_block_ioctl(drive + 1, 0x4860, default_bpb) == 0)
|
|
correct_bpb((struct bootsectortype *)(default_bpb + 7 - 11), bs);
|
|
|
|
#ifdef WITHFAT32 /* copy one of the FAT32 boot sectors */
|
|
memcpy(newboot, haveLBA() ? fat32lba : fat32chs, SEC_SIZE);
|
|
#else
|
|
printf("SYS hasn't been compiled with FAT32 support.\n"
|
|
"Consider using -DWITHFAT32 option.\n");
|
|
exit(1);
|
|
#endif
|
|
}
|
|
else
|
|
{ /* copy the FAT12/16 CHS+LBA boot sector */
|
|
printf("FAT type: FAT1%c\n", fs + '0' - 10);
|
|
if (drive >= 2 &&
|
|
generic_block_ioctl(drive + 1, 0x860, default_bpb) == 0)
|
|
correct_bpb((struct bootsectortype *)(default_bpb + 7 - 11), bs);
|
|
memcpy(newboot, fs == FAT16 ? fat16com : fat12com, SEC_SIZE);
|
|
}
|
|
|
|
/* Copy disk parameter from old sector to new sector */
|
|
#ifdef WITHFAT32
|
|
if (fs == FAT32)
|
|
memcpy(&newboot[SBOFFSET], &oldboot[SBOFFSET], SBSIZE32);
|
|
else
|
|
#endif
|
|
memcpy(&newboot[SBOFFSET], &oldboot[SBOFFSET], SBSIZE);
|
|
|
|
bs = (struct bootsectortype *)&newboot;
|
|
|
|
/* originally OemName was "FreeDOS", changed for better compatibility */
|
|
memcpy(bs->OemName, "FRDOS4.1", 8);
|
|
|
|
#ifdef WITHFAT32
|
|
if (fs == FAT32)
|
|
{
|
|
bs32 = (struct bootsectortype32 *)&newboot;
|
|
/* put 0 for A: or B: (force booting from A:), otherwise use DL */
|
|
bs32->bsDriveNumber = drive < 2 ? 0 : 0xff;
|
|
/* the location of the "0060" segment portion of the far pointer
|
|
in the boot sector is just before cont: in boot*.asm.
|
|
This happens to be offset 0x78 (=0x3c * 2) for FAT32 and
|
|
offset 0x5c (=0x2e * 2) for FAT16 */
|
|
/* i.e. BE CAREFUL WHEN YOU CHANGE THE BOOT SECTORS !!! */
|
|
((int *)newboot)[0x3C] = load_seg;
|
|
#ifdef DEBUG
|
|
printf(" FAT starts at sector %lx + %x\n",
|
|
bs32->bsHiddenSecs, bs32->bsResSectors);
|
|
#endif
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* put 0 for A: or B: (force booting from A:), otherwise use DL */
|
|
bs->bsDriveNumber = drive < 2 ? 0 : 0xff;
|
|
((int *)newboot)[0x2E] = load_seg;
|
|
}
|
|
|
|
#ifdef DEBUG /* add an option to display this on user request? */
|
|
printf("Root dir entries = %u\n", bs->bsRootDirEnts);
|
|
|
|
printf("FAT starts at sector (%lu + %u)\n",
|
|
bs->bsHiddenSecs, bs->bsResSectors);
|
|
printf("Root directory starts at sector (PREVIOUS + %u * %u)\n",
|
|
bs->bsFATsecs, bs->bsFATs);
|
|
#endif
|
|
{
|
|
int i = 0;
|
|
memset(&newboot[0x1f1], ' ', 11);
|
|
while (kernel_name[i] && kernel_name[i] != '.')
|
|
{
|
|
if (i < 8)
|
|
newboot[0x1f1+i] = toupper(kernel_name[i]);
|
|
i++;
|
|
}
|
|
if (kernel_name[i] == '.')
|
|
{
|
|
/* copy extension */
|
|
int j = 0;
|
|
i++;
|
|
while (kernel_name[i+j] && j < 3)
|
|
{
|
|
newboot[0x1f9+j] = toupper(kernel_name[i+j]);
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/* there's a zero past the kernel name in all boot sectors */
|
|
printf("Boot sector kernel name set to %s\n", &newboot[0x1f1]);
|
|
printf("Boot sector load segment set to %Xh\n", load_seg);
|
|
#endif
|
|
|
|
#ifdef DDEBUG
|
|
printf("\nNew Boot Sector:\n");
|
|
dump_sector(newboot);
|
|
#endif
|
|
|
|
if ((bsFile == NULL) || both)
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
printf("writing new bootsector to drive %c:\n", drive + 'A');
|
|
#endif
|
|
|
|
/* write newboot to a drive */
|
|
if (MyAbsReadWrite(drive, 1, 0, newboot, 1) != 0)
|
|
{
|
|
printf("Can't write new boot sector to drive %c:\n", drive + 'A');
|
|
exit(1);
|
|
}
|
|
} /* if write boot sector */
|
|
|
|
if (bsFile != NULL)
|
|
{
|
|
int fd;
|
|
|
|
#ifdef DEBUG
|
|
printf("writing new bootsector to file %s\n", bsFile);
|
|
#endif
|
|
|
|
/* write newboot to bsFile */
|
|
if ((fd = /* suggestion: do not trunc - allows to write to images */
|
|
open(bsFile, O_RDWR | O_TRUNC | O_CREAT | O_BINARY,
|
|
S_IREAD | S_IWRITE)) < 0)
|
|
{
|
|
printf(" %s: can't create\"%s\"\nDOS errnum %d", pgm, bsFile, errno);
|
|
exit(1);
|
|
}
|
|
if (write(fd, newboot, SEC_SIZE) != SEC_SIZE)
|
|
{
|
|
printf("Can't write %u bytes to %s\n", SEC_SIZE, bsFile);
|
|
close(fd);
|
|
unlink(bsFile);
|
|
exit(1);
|
|
}
|
|
close(fd);
|
|
} /* if write boot sector file */
|
|
reset_drive(drive);
|
|
|
|
/* unlock_drive */
|
|
generic_block_ioctl(drive + 1, 0x86a, NULL);
|
|
} /* put_boot */
|
|
|
|
|
|
/*
|
|
* Returns TRUE if `drive` has at least `bytes` free space, FALSE otherwise.
|
|
* put_sector() must have been already called to determine file system type.
|
|
*/
|
|
BOOL check_space(COUNT drive, ULONG bytes)
|
|
{
|
|
#ifdef WITHFAT32
|
|
if (fs == FAT32)
|
|
{
|
|
char *drivename = "A:\\";
|
|
drivename[0] = 'A' + drive;
|
|
getextdrivespace(drivename, &x, sizeof(x));
|
|
return x.xfs_freeclusters > (bytes / (x.xfs_clussize * x.xfs_secsize));
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#ifdef __TURBOC__
|
|
struct dfree df;
|
|
getdfree(drive + 1, &df);
|
|
return (ULONG)df.df_avail * df.df_sclus * df.df_bsec >= bytes;
|
|
#else
|
|
struct _diskfree_t df;
|
|
_dos_getdiskfree(drive + 1, &df);
|
|
return (ULONG)df.avail_clusters * df.sectors_per_cluster
|
|
* df.bytes_per_sector >= bytes;
|
|
#endif
|
|
}
|
|
} /* check_space */
|
|
|
|
|
|
/* returns TRUE if file exists, FALSE otherwise
|
|
1st checks for srcPath + filename, if that does not exist then
|
|
will check for rootPath + filename
|
|
On failure, source is undefined. source must be >= SYS_MAXPATH bytes
|
|
*/
|
|
BOOL get_full_path(BYTE * srcPath, BYTE * rootPath, BYTE * filename, BYTE *source)
|
|
{
|
|
struct stat fstatbuf;
|
|
|
|
strcpy(source, srcPath);
|
|
if (rootPath != NULL) /* trick for comspec, append filename if srcPath doesn't include it */
|
|
strcat(source, filename);
|
|
|
|
/* check if file exists in source directory */
|
|
if (stat(source, &fstatbuf))
|
|
{
|
|
/* not found in source path, so try root of source drive */
|
|
if ((rootPath != NULL) && (*rootPath) /* && (errno == ENOENT) */ )
|
|
{
|
|
sprintf(source, "%s%s", rootPath, filename);
|
|
if (stat(source, &fstatbuf))
|
|
return FALSE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
} /* get_full_path */
|
|
|
|
BYTE copybuffer[COPY_SIZE];
|
|
|
|
/* copies file (path+filename specified by srcFile) to drive:\filename */
|
|
BOOL copy(BYTE *source, COUNT drive, BYTE * filename)
|
|
{
|
|
static BYTE dest[SYS_MAXPATH];
|
|
unsigned ret;
|
|
int fdin, fdout;
|
|
ULONG copied = 0;
|
|
|
|
printf("\nCopying %s...\n", source);
|
|
|
|
truename(dest, source);
|
|
strcpy(source, dest);
|
|
sprintf(dest, "%c:\\%s", 'A' + drive, filename);
|
|
if (stricmp(source, dest) == 0)
|
|
{
|
|
printf("%s: source and destination are identical: skipping \"%s\"\n",
|
|
pgm, source);
|
|
return TRUE;
|
|
}
|
|
|
|
if ((fdin = open(source, O_RDONLY | O_BINARY)) < 0)
|
|
{
|
|
printf("%s: failed to open \"%s\"\n", pgm, source);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!check_space(drive, filelength(fdin)))
|
|
{
|
|
printf("%s: Not enough space to transfer %s\n", pgm, filename);
|
|
close(fdin);
|
|
exit(1);
|
|
}
|
|
|
|
if ((fdout =
|
|
open(dest, O_RDWR | O_TRUNC | O_CREAT | O_BINARY,
|
|
S_IREAD | S_IWRITE)) < 0)
|
|
{
|
|
printf(" %s: can't create\"%s\"\nDOS errnum %d", pgm, dest, errno);
|
|
close(fdin);
|
|
return FALSE;
|
|
}
|
|
|
|
while ((ret = read(fdin, copybuffer, COPY_SIZE)) > 0)
|
|
{
|
|
if (write(fdout, copybuffer, ret) != ret)
|
|
{
|
|
printf("Can't write %u bytes to %s\n", ret, dest);
|
|
close(fdout);
|
|
unlink(dest);
|
|
break;
|
|
}
|
|
copied += ret;
|
|
}
|
|
|
|
{
|
|
#if defined __WATCOMC__ || defined _MSC_VER /* || defined __BORLANDC__ */
|
|
unsigned short date, time;
|
|
_dos_getftime(fdin, &date, &time);
|
|
_dos_setftime(fdout, date, time);
|
|
#elif defined __TURBOC__
|
|
struct ftime ftime;
|
|
getftime(fdin, &ftime);
|
|
setftime(fdout, &ftime);
|
|
#endif
|
|
}
|
|
|
|
close(fdin);
|
|
close(fdout);
|
|
|
|
#ifdef __SOME_OTHER_COMPILER__
|
|
{
|
|
#include <utime.h>
|
|
struct utimbuf utimb;
|
|
|
|
utimb.actime = /* access time */
|
|
utimb.modtime = fstatbuf.st_mtime; /* modification time */
|
|
utime(dest, &utimb);
|
|
};
|
|
#endif
|
|
|
|
printf("%lu Bytes transferred", copied);
|
|
|
|
return TRUE;
|
|
} /* copy */
|
|
|