home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fred Fish Collection 1.5
/
ffcollection-1-5-1992-11.iso
/
ff_disks
/
300-399
/
ff350.lzh
/
TrackUtils
/
src
/
tfile.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-04-16
|
38KB
|
1,342 lines
/*
* TFILE.C
*
* (C) Copyright Eddy Carroll, January 1990
*
* This is a little utility which simply creates a "fake" file on a disk,
* which makes AmigaDos think that all the blocks on a given range of
* tracks are in use. I.e. it allows you to reserve a section of the disk
* for your own messing around, without fear of AmigaDos stomping over you
* at some later stage.
*
* Usage: tfile <pathname> <start> <end>
*
* <pathname> The full pathname of the filename AmigaDOS will think
* the blocks are stored in. This also specifies the device
* on which the blocks are to be reserved.
*
* <start> The starting track to reserve blocks on.
*
* <end> The ending track to reserve blocks on.
*
* For example, to reserve tracks 70 to 79 on the disk in DF0:, you might
* give the command:
*
* tfile df0:devs/Tracks70-79 70 79
*
* which will create the file devs/Tracks70-79 on the disk in DF0: If you
* try and actually access this file, you will find it only contains a brief
* message -- this is a safeguard to stop AmigaDOS trying to make sense
* of whatever data is actually on tracks 70 to 79.
*
* Tfile is primarily intended for use with the companion program tcopy,
* which allows tracks from a small device such as RAD: to be stored on
* part of a larger device like a floppy disk, and then read back in at
* a later time (and at high speed).
*
*
* Note: The following assumptions are hardwired into the code --
*
* -- Long's are at least 32 bits long
* -- Blocks are 512 bytes long
* -- Either the old or fast filing system is in use
*/
#define TRUE 1
#define FALSE 0
#define LASTCHAR(s) (s[strlen(s)-1])
#ifndef LATTICE_50
#include "system.h"
#endif
#include "dosheaders.h"
#define MIN(x,y) ((x) < (y) ? (x) : (y))
extern struct DosLibrary *DOSBase;
typedef struct IORequest IOREQ;
typedef struct MsgPort MSGPORT;
typedef struct Process PROC;
typedef struct StandardPacket STDPKT;
void inhibit(MSGPORT *devport, int mode); /* Called by cleanup() */
/*
* This is the message that gets stored in the dummy file when
* the disk being written to is an OFS disk. Max 488 characters please.
*/
char FileMessage[] = "\
This file was created using TFile (C) Copyright Eddy Carroll 1990.\n\n\
TFile creates special files which don't contain data, but instead prevent\n\
AmigaDOS from overwriting the information on some tracks on this disk.\n\n";
/*
* Structure representing a disk device
*/
typedef struct {
char *devname; /* Name of exec device for this disk */
int isfloppy; /* True if device is trackdisk.device */
int isffs; /* True if device is using FFS */
int isknown; /* True if filesystem is known */
ULONG unit; /* Unit number of above exec device */
ULONG blocksize; /* Number of bytes per block */
ULONG blockspertrack; /* Number of blocks/sectors per track */
ULONG surfaces; /* Number of tracks per cylinder */
ULONG lowcyl; /* Starting cylinder of disk on device */
ULONG numcyl; /* Number of cylinders on this disk */
ULONG numblocks; /* Number of blocks on this disk */
ULONG reserved; /* Number of blocks reserved at start */
/* (included in numblocks count) */
} DISKDEV;
/*
* Global variables
* ----------------
*/
BPTR filehandle; /* Filehandle used when creating dummy file */
BPTR lock; /* Lock on dummy file used to store tracks */
LONG filekey; /* AmigaDOS block of dummy file header */
char *pathname; /* Full AmigaDOS path of file to create */
char devname[300]; /* AmigaDOS device we are operating on */
DISKDEV diskdev[1]; /* Description of disk device we are using */
struct IOStdReq *req; /* Standard request for source device */
MSGPORT *reqport; /* Message port for replies from disk dev. */
MSGPORT *diskport; /* Message port of process of disk device */
int devopen; /* True if disk exec device is open */
int inhibited; /* True if destination device is inhibited */
void *readbuf; /* Buffer to read a single disk block into */
void *writebuf; /* Buffer to write a single disk block from */
ULONG *bitmap; /* Array of longwords holding bitmap */
ULONG *bitmapblocks; /* Array of block numbers for disk bitmap */
ULONG bitmapsize; /* Size of bitmap in bytes */
ULONG bitmapblocksize; /* Size of bitmap blocks array in bytes */
ULONG numbitmapblocks; /* Number of blocks storing bitmap on disk */
struct FileHeaderBlock *headers; /* Array of file headers/extensions */
struct DataBlock *datablock; /* Datablock used if accessing OFS */
struct RootBlock *rootblock; /* Rootblock of current disk */
ULONG headersize; /* Size of array of file headers in bytes */
ULONG datasize; /* Size of datablock in bytes */
ULONG rootsize; /* Size of rootblock in bytes */
ULONG datablocknum; /* Block # to store front data block at */
/*
* print()
* -------
* Outputs a message to stdout
*/
void print(char *s)
{
Write(Output(), s, strlen(s));
}
#define print2(s1,s2) (print(s1),print(s2))
#define print3(s1,s2,s3) (print(s1),print(s2),print(s3))
/*
* numtostr()
* ----------
* Simple little function which returns a pointer to a static string
* representation of the passed in number.
*/
char *numtostr(int n)
{
static char s[20];
int i = 19;
s[19] = '\0';
if (n)
while (n) {
s[--i] = '0' + (n % 10);
n /= 10;
}
else
s[--i] = '0';
return(&s[i]);
}
/*
* cleanup()
* ---------
* Closes all opened resources, and exits with specified error code.
*/
void cleanup(int code)
{
if (devopen) {
if (diskdev->isfloppy) { /* Turn off drive motor if floppy disk */
req->io_Command = TD_MOTOR;
req->io_Length = 0;
DoIO(req);
}
CloseDevice((IOREQ *)req);
}
if (inhibited)
inhibit(diskport, FALSE);
if (readbuf)
FreeMem(readbuf, diskdev->blocksize);
if (writebuf)
FreeMem(writebuf, diskdev->blocksize);
if (req)
DeleteStdIO(req);
if (reqport)
DeletePort(reqport);
if (lock)
UnLock(lock);
if (filehandle)
Close(filehandle);
if (bitmap)
FreeMem(bitmap, bitmapsize);
if (bitmapblocks)
FreeMem(bitmapblocks, bitmapblocksize);
if (headers)
FreeMem(headers, headersize);
if (datablock)
FreeMem(datablock, datasize);
if (rootblock)
FreeMem(rootblock, rootsize);
exit(code);
}
/*
* chkabort()
* ----------
* A replacement for Lattice's chkabort(), which doesn't carry all
* the extra baggage. If CTRL-C is detected, this function never
* returns but instead calls cleanup. Since Lattice's exit() code
* may call chkabort(), a check is made to ensure that cleanup()
* only gets called once, otherwise there would be a problem if the
* user pressed Ctrl-C twice in quick succession.
*/
void chkabort(void)
{
static int gotctrlc = FALSE;
if (!gotctrlc && (SetSignal(0,0) & SIGBREAKF_CTRL_C)) {
gotctrlc = TRUE;
print("^C\n");
cleanup(10);
}
}
/*
* GetVolume()
* -----------
* This function searches the device list looking for a physical device
* that currently contains the specified disk volume. When it finds it,
* it fills in the DISKDEV structure with information about the physical
* device which can then be used to access it at the block level.
*/
int GetVolume(char *devname, DISKDEV *dev)
{
struct RootNode *rootnode;
struct DosInfo *dosinfo;
struct DeviceNode *devnode;
struct FileSysStartupMsg *startup;
struct DosEnvec *dosenvec;
unsigned char *p;
int namelen = strlen(devname);
if (LASTCHAR(devname) != ':') /* Device names must end with ':' */
return (FALSE);
/*
* First of all, find the device
*/
rootnode = (struct RootNode *)DOSBase->dl_Root;
dosinfo = (struct DosInfo *)BADDR(rootnode->rn_Info);
devnode = (struct DeviceNode *)BADDR(dosinfo->di_DevInfo);
Forbid(); /* Make sure list doesn't change while we scan it */
while (devnode != NULL) {
p = (unsigned char *)BADDR(devnode->dn_Name)+1;
if (!strnicmp(devname, p, namelen-1)) { /* Don't compare the ':' */
/*
* Matched name successfully. Now check if it's a device.
* Note that we carry on searching if it's not a device
* (rather than returning FALSE immediately) since there
* may be a volume called RAD: as well as a device called
* RAD:, for example.
*/
if (devnode->dn_Type == DLT_DEVICE) {
if (devnode->dn_Startup < 20) /* Is there a startup bit? */
goto notfound; /* If not, not disk device */
/* Eek! A GOTO! */
startup = (struct FileSysStartupMsg *)
BADDR(devnode->dn_Startup);
if (dev) {
dev->devname = ((char *)BADDR(startup->fssm_Device))+1;
dev->isfloppy = (!strcmp(TD_NAME, dev->devname));
dev->unit = startup->fssm_Unit;
}
if (startup->fssm_Environ < 20)
goto notfound;
/* Another GOTO! The world will end in 5 seconds... */
dosenvec = (struct DosEnvec *)BADDR(startup->fssm_Environ);
if (dev) {
dev->isffs = dosenvec->de_DosType == 0x444F5301;
/*
* Since dosenvec->de_DosType does NOT equal
* 0x444F5300 for standard OFS AmigaDOS disks,
* I can't check it to see if the filing system
* is "known". Hence, for now, just assume its
* always known (since it can currently only be
* OFS or FFS, and I can't think of any other way
* of checking).
*/
dev->isknown = 1;
dev->blocksize = dosenvec->de_SizeBlock << 2;
dev->blockspertrack = dosenvec->de_BlocksPerTrack;
dev->surfaces = dosenvec->de_Surfaces;
dev->lowcyl = dosenvec->de_LowCyl;
dev->numcyl = (dosenvec->de_HighCyl -
dosenvec->de_LowCyl) + 1;
dev->numblocks = dev->blockspertrack * dev->surfaces
* dev->numcyl;
dev->reserved = dosenvec->de_Reserved;
}
Permit();
return (TRUE);
}
}
devnode = (struct DeviceNode *)BADDR(devnode->dn_Next);
}
notfound:
Permit();
return (FALSE);
}
/*
* help()
* ------
* Prints out a help message about trackfile
*/
void help(void)
{
print3(
"Trackfile (C) Copyright Eddy Carroll, January 1990. Freely distributable.\n",
"Creates an AmigaDos file which reserves specified tracks on a disk.\n\n",
"Usage: tfile <pathname> <start> <end>\n\n"); print3(
" <pathname> The full AmigaDOS path of the dummy file that will be \
created\n",
" to hold the reserved tracks, to stop AmigaDOS using them.\n\n",
" <start> The starting track to be reserved on the disk.\n\n" ); print(
" <end> The ending track to be reserved on the disk.\n\n" );
}
/*
* opendev()
* ---------
* Opens the disk device, allocates stdreqs etc. Two buffers are
* also allocated, one for reading a single block and one for
* writing a single block.
*/
void opendev(void)
{
ULONG memflags = MEMF_PUBLIC;
if (diskdev->isfloppy)
memflags |= MEMF_CHIP;
reqport = (MSGPORT *)CreatePort(0,0);
if (!reqport) {
print("Couldn't allocate message port\n");
cleanup(10);
}
readbuf = AllocMem(diskdev->blocksize, memflags);
if (!readbuf) {
print("Couldn't allocate memory for read buffer\n");
cleanup(10);
}
writebuf = AllocMem(diskdev->blocksize, memflags);
if (!writebuf) {
print("Couldn't allocate memory for write buffer\n");
cleanup(10);
}
req = CreateStdIO(reqport);
if (!req) {
print("Couldn't allocate IO request - memory is low!\n");
cleanup(10);
}
if (OpenDevice(diskdev->devname, diskdev->unit, (IOREQ *)req, 0L)) {
print3("Can't open device ", diskdev->devname, "\n");
cleanup(10);
}
devopen = TRUE;
}
/*
* readblock() & writeblock()
* ----------- ------------
*
* These two functions read or write a block from the disk.
* See the definition of transferblocks() below for more information.
*/
#define BLOCK_READ 0
#define BLOCK_WRITE 1
#define readblock(buffer, s) transferblocks(buffer, s, 1, BLOCK_READ)
#define writeblock(buffer, s) transferblocks(buffer, s, 1, BLOCK_WRITE)
/*
* transferblocks()
* ----------------
* Transfers n blocks to/from the disk into the buffer indicated,
* starting at block s. Note that if you are using a floppy drive, the
* buffer MUST be allocated in CHIP ram! You can ensure this by
* including MEMF_CHIP in the flags to AllocMem() if diskdev->isfloppy
* is true. Zero is returned if an error occurred, else non-zero.
* Up to three retries are done before failure is accepted.
*
* The direction of transfer is BLOCK_READ to read blocks in, or
* BLOCK_WRITE to write blocks out.
*/
int transferblocks(void *buffer, int s, int n, int direction)
{
int retry, parstart;
/*
* Because AmigaDOS works with partitions, Block 0 of an AmigaDOS
* device may in fact be block 12345 of an exec device, depending
* where the partition starts. Hence, before trying to access an
* Amiga-DOS numbered block, we need to add in the block at which
* the current partition starts.
*/
parstart = diskdev->lowcyl * diskdev->blockspertrack * diskdev->surfaces;
/*
* Now read in blocks from disk
*/
for (retry = 0; retry < 3; retry++) {
if (direction == BLOCK_READ)
req->io_Command = CMD_READ;
else
req->io_Command = CMD_WRITE;
req->io_Length = diskdev->blocksize * n;
req->io_Offset = diskdev->blocksize * (s + parstart);
req->io_Data = buffer;
if (!DoIO((IOREQ *)req))
break; /* Succeeded, so break out of loop */
}
if (retry == 3)
return (0);
else
return (1);
}
/*
* SendPacket()
* ------------
* ``Sort of'' simulates the ARP SendPacket() routine which sends
* a packet to AmigaDos, and gets a reply if appropriate. What is
* passed in is the action to be executed (one of the ACTION_xxx
* definitions in dosextens.h), a pointer to a longword array of 7
* arguments to be passed to the device, and the msgport of the device
* as returned by DeviceProc("DF0:") for example. If result is non-NULL
* then it should be a pointer to a two element array of ULONGs, and it
* fills in the 0th element with the primary result, and the the
* 1st element with the secondary result.
*/
int SendPacket(ULONG action, void *args, MSGPORT *devport, ULONG *result)
{
PROC *proc = (PROC *)FindTask(NULL);
STDPKT *packet;
packet = (STDPKT *)AllocMem(sizeof(STDPKT), MEMF_CLEAR | MEMF_PUBLIC);
if (!packet)
return (FALSE);
packet->sp_Msg.mn_Node.ln_Name = (char *)&packet->sp_Pkt;
packet->sp_Pkt.dp_Link = &packet->sp_Msg;
packet->sp_Pkt.dp_Port = &proc->pr_MsgPort;
packet->sp_Pkt.dp_Type = action;
memcpy(&packet->sp_Pkt.dp_Arg1, args, sizeof(ULONG) * 7);
/*
* Okay, we've done the necessary magic to create an AmigaDos
* packet lookalike (thanks to Matt Dillon in Transactor V1.1).
* Now we send the message to the Dos process, and get the reply.
* Then our message will be filled in with the response from the
* Dos process.
*/
PutMsg(devport, (struct Message *)packet);
WaitPort(&proc->pr_MsgPort);
GetMsg(&proc->pr_MsgPort);
if (result) {
result[0] = packet->sp_Pkt.dp_Res1;
result[1] = packet->sp_Pkt.dp_Res2;
}
FreeMem(packet, sizeof(STDPKT));
return (TRUE);
}
/*
* inhibit()
* ---------
* This function inhibits (if mode is TRUE) or uninhibits (if mode is
* FALSE) the specified device.
*/
void inhibit(MSGPORT *devport, int mode)
{
ULONG pktargs[7];
int i;
pktargs[0] = mode; /* Select inhibit or opposite */
for (i = 1; i < 7; i++) /* Clear other arguments */
pktargs[i] = 0;
if (!SendPacket(ACTION_INHIBIT, pktargs, devport, NULL)) {
print("Couldn't send inhibit packet to device\n");
cleanup(10);
}
}
/*
* flushdisk()
* -----------
* This function flushes the specified DOS device, ensuring that all
* dirty buffers are written to disk and the bitmap is up to date.
*/
void flushdisk(MSGPORT *devport)
{
ULONG pktargs[7];
int i;
for (i = 0; i < 7; i++) /* Clear arguments */
pktargs[i] = 0;
if (!SendPacket(ACTION_FLUSH, pktargs, devport, NULL)) {
print("Couldn't send flush packet to device\n");
cleanup(10);
}
}
/*
* fixchecksum()
* -------------
* Calculates the checksum for the specified block, and stores it
* in the indicated place, which will usually be a pointer into the
* block itself. The block is assumed to be 512 bytes long.
*/
void fixchecksum(void *block, ULONG *checksum)
{
ULONG *blk = block, *start, *end;
ULONG sum = 0;
end = blk + 128;
*checksum = 0; /* Clear checksum so it won't affect count */
for (start = blk; start < end; start++)
sum += *start;
*checksum = -sum; /* Real checksum is complement of total */
}
/*
* readrootblock()
* ---------------
* This function simply reads in the root block of the disk into
* the global 'root' structure for the rest of the program to access.
*/
void readrootblock(void)
{
ULONG rootblocknum = diskdev->numblocks / 2;
ULONG memflags = MEMF_PUBLIC;
if (diskdev->isfloppy)
memflags |= MEMF_CHIP;
rootsize = 512;
rootblock = AllocMem(rootsize, memflags);
if (!rootblock) {
print("Out of memory allocating space for root block\n");
cleanup(10);
}
if (!readblock(rootblock, rootblocknum)) {
print("Error reading root block from disk\n");
cleanup(10);
}
}
#ifdef DEBUG
/*
* writerootblock()
* ----------------
* This function writes the rootblock back to disk.
* Temporarily, it zaps the "validated" flag to force a validation
* to occur.
*/
void writerootblock(void)
{
ULONG rootblocknum = diskdev->numblocks / 2;
rootblock->BitmapFlag = 0;
fixchecksum(rootblock, &rootblock->Checksum);
writeblock(rootblock, rootblocknum);
}
#endif
/*
* readbitmap()
* ------------
*
* This function creates a memory copy of the disks's bitmap, by
* reading in all the disk's bitmap blocks. Two arrays are created;
* The first holds the bitmap data itself, the second is a longword
* array holding the block numbers that the bitmap is stored on, for
* use when it is being written out again.
*
* If an error occurs, the function prints a message and exits to dos.
* It is assumed that the root block has been read in to 'root' before
* calling this function.
*/
void readbitmap(void)
{
int i, end;
ULONG nextblock;
ULONG *block = readbuf;
/*
* First let's calculate the size of each array. The size of the
* bitmap array is simply the number of blocks on the disk divided
* by 8 (since a single byte can represent 8 blocks). The size of
* the array to hold the locations of the bitmap blocks is the
* number of blocks divided by 4064, since each bitmap block
* can represent 4064 disk blocks (with 32 bits left over for
* the block checksum).
*/
/*
* To calculate the number of DOS-usable blocks on the disk, we need
* to subtract the number of blocks reserved at the start of the
* partition. In addition, we add on 4063 so that the result after
* division will be rounded up to the next integral block number.
*/
numbitmapblocks = (diskdev->numblocks + 4063 - diskdev->reserved) / 4064;
bitmapsize = numbitmapblocks * 508; /* 4 bytes used for checksum */
bitmapblocksize = numbitmapblocks * 4;
/*
* Now, allocate the arrays
*/
bitmap = AllocMem(bitmapsize, 0L);
if (!bitmap) {
print("Can't allocate memory to store disk bitmap.\n");
cleanup(10);
}
bitmapblocks = AllocMem(bitmapblocksize, 0L);
if (!bitmapblocks) {
print("Can't allocate memory to store disk bitmap sector numbers.\n");
cleanup(10);
}
/*
* Now, read in all the sector numbers from the root block
*/
end = MIN(numbitmapblocks, 25);
for (i = 0; i < end; i++)
bitmapblocks[i] = rootblock->BitmapKeys[i];
/*
* If we are using a large disk partition (larger than 52 Megs or so)
* we also need to read in the bitmap extension block(s). Each
* extension block contains 127 sector numbers.
*
* Note: Since I am unsure of the extension bitmap format used,
* this code is being commented out for now, and a check has been
* included in main() to ensure no disk big enough to have an
* bitmap extension is allowed to get here; when I find out the
* format, it will be trivial to add support for it here, since the
* rest of the program just deals with an array of bitmap sector
* numbers (and the bitmap array itself of course).
*
* For the code, I'm assuming the bitmap is stored back to front
* (i.e. first sector last), based on Betty Clay's article in
* Transactor. I want to verify this first though before letting
* it loose on the unsuspecting public (after all, a bug that screws
* up a 50 Mb disk is not going to go down well with people who
* have 50 Mb disks... :-)
*/
nextblock = rootblock->BitmapExtend;
#ifdef FOUND_OUT_HOW_THE_BITMAP_IS_STORED
while (i < numbitmapblocks) {
int p = 127;
if (!readblock(block, nextblock)) {
print("Error reading bitmap extension block from disk\n");
cleanup(10);
}
nextblock = block[0];
end = MIN(i + 127, numbitmapblocks);
while (i < end)
bitmapblocks[i++] = blocks[p--];
}
#endif
/*
* Now I know what sectors the bitmap itself is stored on, so
* I can read it in to memory.
*/
for (i = 0; i < numbitmapblocks; i++) {
if (!readblock(block, bitmapblocks[i])) {
print("Error reading in bitmap from disk\n");
cleanup(10);
}
/*
* Remember, bitmap is pointer to ULONG so we use longword
* offsets when copying the bitmap data into the bitmap array.
*/
memcpy(bitmap + (127 * i), block + 1, 508);
}
}
/*
* writebitmap()
* -------------
* Writes the disk bitmap back to disk, storing it on the sectors
* it was read in from earlier.
*/
void writebitmap(void)
{
ULONG *diskblock = (ULONG *)writebuf;
int i;
for (i = 0; i < numbitmapblocks; i++) {
/*
* For each bitmap block, copy the 127 longwords into write
* buffer, correct checksum, then write to disk.
*/
memcpy(diskblock + 1, bitmap + (i * 127), 508);
fixchecksum(diskblock, &diskblock[0]);
if (!writeblock(diskblock, bitmapblocks[i])) {
print(
"Error writing updated bitmap to disk! Copy important files to a new disk!\n");
cleanup(10);
}
}
}
#ifdef DEBUG
/*
* dumpbitmap()
* ------------
* A quick hack function to display the bitmap on screen, to allow
* visual confirmation that it was read in correctly, altered
* properly etc.
*/
void dumpbitmap(void)
{
int t, s, c, blocknum, i;
static char buf[300];
for (c = 0; c < diskdev->numcyl; c++) {
for (t = 0; t < diskdev->surfaces; t++) {
blocknum = (((c * diskdev->surfaces) + t)
* diskdev->blockspertrack);
chkabort();
print3(numtostr(c),",",numtostr(t));
print3(": ",numtostr(blocknum),"; ");
i = 0;
for (s = 0; s < diskdev->blockspertrack; s++) {
blocknum = (((c * diskdev->surfaces) + t)
* diskdev->blockspertrack) + s;
if (blocknum < diskdev->reserved ||
blocknum >= diskdev->numblocks)
buf[i++] = 'O'; /* Block reserved */
else {
blocknum = blocknum - diskdev->reserved;
if (bitmap[blocknum >> 5] & (1 << (blocknum & 31)))
buf[i++] = '-'; /* Block unused */
else
buf[i++] = 'O'; /* Block in use */
}
}
buf[i] = '\0';
print2(buf, "\n");
}
}
}
#endif
/*
* filltracks()
* ------------
* Tries to mark the specified cylinders as used in the memory copy
* of the disk bitmap. 1 is returned if successful, 0 if any of the
* blocks in the cylinders were already in use.
*/
int filltracks(int lowcyl, int highcyl)
{
long startblock, endblock; /* Adjusted block numbers */
long startblockindex, endblockindex; /* Indexes into bitmap array */
long startoffset, endoffset; /* Bit offsets into bitmap word */
long lastblock; /* Last bitmap block on disk */
int i, warn = 0;
lastblock = diskdev->numblocks - diskdev->reserved;
startblock = lowcyl * diskdev->surfaces * diskdev->blockspertrack
- diskdev->reserved;
if (startblock < 0) {
warn = 1;
startblock = 0;
}
if (startblock >= lastblock) {
warn = 1;
startblock = lastblock;
}
startblockindex = startblock >> 5;
startoffset = startblock & 31;
endblock = (highcyl + 1) * diskdev->surfaces * diskdev->blockspertrack
- diskdev->reserved;
if (endblock < 0) {
warn = 1;
endblock = 0;
}
if (endblock > lastblock) {
warn = 1;
endblock = lastblock;
}
endblockindex = endblock >> 5;
endoffset = endblock & 31;
if (warn)
print("(Warning: Track range includes reserved portion of disk.)\n");
if (startblockindex == endblockindex) {
/*
* Start and stop in same bitmap word, so set the bits manually
*/
for (i = startoffset; i < endoffset; i++) {
if ((bitmap[startblockindex] & (1 << i)) == 0)
return (0); /* Block already in use */
else
bitmap[startblockindex] &= ~(1 << i); /* Clear bit in bitmap */
}
return (1);
}
/*
* Allocate first few blocks a bit at a time up to end of longword
*/
for (i = startoffset; i < 32; i++) {
if ((bitmap[startblockindex] & (1 << i)) == 0)
return (0); /* Block already in use */
else
bitmap[startblockindex] &= ~(1 << i); /* Clear bit in bitmap */
}
/*
* Now allocate inbetween blocks a longword at a time for speed
*/
for (i = startblockindex + 1; i < endblockindex; i++) {
if (bitmap[i] != 0xffffffff)
return (0); /* Block already in use */
else
bitmap[i] = 0; /* Set all blocks used */
}
/*
* Finally, allocate last few blocks a bit at a time
*/
for (i = 0; i < endoffset; i++) {
if ((bitmap[endblockindex] & (1 << i)) == 0)
return (0); /* Block already in use */
else
bitmap[endblockindex] &= ~(1 << i); /* Clear bit in bitmap */
}
return (1);
}
/*
* allocblock()
* ------------
* Tries to allocate a block as close to block `key' as possible,
* from the remaining free blocks in the disk bitmap. The allocation
* algorithm is as follows:
*
* - Find the nearest free block before the key
* - Find the nearest free block after the key
* - Allocate whichever one is closest
*
* If the allocation was successful, the number of the new block is
* stored in newkey.
*/
int allocblock(ULONG key, ULONG *newkey)
{
ULONG curblock = key - diskdev->reserved;
ULONG endblock = diskdev->numblocks - diskdev->reserved;
ULONG endindex = endblock >> 5;
ULONG useblock;
long i, j, tp; /* Must be signed */
long curindex = curblock >> 5;
long curoffset = curblock & 31;
int backblock = -1, forblock = -1;
/*
* Find nearest block before current key. Remember that in the
* bitmap, a `1' means the block is free, a `0' means its allocated.
*
* First of all, search back within current bitmap word
*/
tp = bitmap[curindex];
for (i = curoffset; i >= 0; i--) {
if (tp & (1 << i)) {
backblock = (curindex << 5) + i;
break;
}
}
if (backblock < 0) {
/*
* Didn't find room in initial word, so carry on search
*/
for (i = curindex - 1; i >= 0; i--) {
if (bitmap[i] != 0) {
tp = bitmap[i];
/*
* Found free block word; now find what bit it is;
* Note that because of the way we entered this
* loop (with tp != 0), termination of the next
* loop is guaranteed.
*/
for (j = 31; (tp & (1 << j)) == 0; j--)
;
backblock = (i << 5) + j;
break;
}
}
}
/*
* Now find nearest block after current key
*/
tp = bitmap[curindex];
for (i = curoffset; i < 32; i++) {
if (tp & (1 << i)) {
forblock = (curindex << 5) + i;
break;
}
}
if (forblock < 0) {
/*
* Didn't find room in initial word, so carry on search
*/
for (i = curindex + 1; i <= endindex; i--) {
if (bitmap[i] != 0) {
tp = bitmap[i];
/*
* Found free room; now find what bit it is;
* Note that because of the way we entered this
* loop (with tp != 0), termination of the next
* loop is guaranteed.
*/
for (j = 0; (tp & (1 << j)) == 0; j++)
;
forblock = (i << 5) + j;
break;
}
}
}
/*
* Now, forblock and backblock hold block numbers which are free
* in the bitmap (or -1 if none was found). We just have to
* work out which one to use.
*/
if (forblock < 0) {
if (backblock < 0)
return (0);
else
useblock = backblock;
} else {
if (backblock < 0)
useblock = forblock;
else {
if ((key - backblock) < (forblock - key))
useblock = backblock;
else
useblock = forblock;
}
}
/*
* At this stage, useblock is the block number to allocate and
* return.
*/
bitmap[useblock >> 5] &= ~(1 << (useblock & 31));
*newkey = useblock + diskdev->reserved;
/*
* Finally, a brief sanity check to ensure the number is in range
* It SHOULD be but it doesn't do any harm to check, in case we
* ran off the end of the bitmap or something...
*/
if (*newkey < diskdev->numblocks)
return (1);
else
return (0);
}
/*
* createfileheaders()
* -------------------
* Allocates an array of file headers and extension blocks, and
* fills them in with the appropriate values to make AmigaDOS think
* a file exists on the tracks the user indictated. The key is
* the block number of the file header which is to be modified to
* hold the dummy file.
*
* As well as allocating the blocks on the tracks to be reserved,
* an additional `frontend' data block is stuck at the front of the
* file containing a brief message. This stops DOS from getting
* confused if the user tries to access the file. Under the FFS it
* isn't needed, but under the OFS, a series crash results. To stop
* the code getting overly complicated, the FFS gets it too.
*
* After the headers have been initialised, they are written back to
* the disk and hey presto, a new file is born :-)
*/
void createfileheaders(int key, int lowcyl, int highcyl)
{
static char msgbuf[488];
ULONG lastblock, startblock, endblock, numblocks, filesize;
ULONG curblock;
ULONG memflags = MEMF_PUBLIC | MEMF_CLEAR;
int i, j, numheaders;
lastblock = diskdev->numblocks;
startblock = lowcyl * diskdev->surfaces * diskdev->blockspertrack;
if (startblock < diskdev->reserved)
startblock = diskdev->reserved;
endblock = (highcyl + 1) * diskdev->surfaces * diskdev->blockspertrack;
if (endblock > lastblock)
endblock = lastblock;
numblocks = 1 + endblock - startblock; /* 1 extra for front data block */
/*
* Now, we know how many blocks the file will have (we've been
* careful not to count blocks which are reserved at the start
* or end of the partition). Next, let's calculate how many
* bytes our pretend file will have. This depends on whether
* the filing system is OFS or FFS.
*/
if (diskdev->isffs)
filesize = numblocks * 512;
else
filesize = numblocks * 488;
/*
* Now allocate the file header/extensions to hold the file.
* Each header/extension holds 72 block keys.
*/
numheaders = (numblocks + 71) / 72;
headersize = numheaders * 512;
if (diskdev->isfloppy)
memflags |= MEMF_CHIP;
headers = AllocMem(headersize, memflags);
if (!headers) {
print("Error allocating memory for file header/extension blocks\n");
cleanup(10);
}
/*
* Read in file header block, for file to be modified.
*/
if (!readblock(headers, key)) {
print("Error reading file header block from disk\n");
cleanup(10);
}
/*
* Now fill in the details for all the file extension blocks.
*/
curblock = startblock - 1; /* Adjust to allow for front data block */
for (i = 0; i < numheaders; i++) {
int end = MIN(72, endblock - curblock);
for (j = 0; j < end; j++)
headers[i].DataBlocks[71-j] = curblock++;
}
for (i = 1; i < numheaders; i++) {
headers[i].Type = Type_List;
headers[i].SecondaryType = SecType_File;
if (!allocblock(headers[i-1].OwnKey, &headers[i].OwnKey)) {
print("Not enough room on disk to store dummy file.\n");
cleanup(10);
}
headers[i-1].Extension = headers[i].OwnKey;
headers[i].HighSeq = 72;
headers[i].DataSize = 72;
headers[i].Parent = key;
}
headers[numheaders-1].Extension = 0;
headers[numheaders-1].HighSeq = numblocks % 72;
headers[numheaders-1].DataSize = numblocks % 72;
headers[0].FileSize = filesize;
headers[0].HighSeq = numblocks;
/*
* Now, allocate and save the dummy data block containing the
* brief message. Under the FFS, users can actually access the
* raw track data by seeking to (offset + 512), where offset is
* the desired byte offset from the first reserved cylinder.
*/
datasize = 512;
datablock = AllocMem(datasize, memflags);
if (!datablock) {
print("Out of memory allocating temporary disk data block.\n");
cleanup(10);
}
if (!allocblock(key, &datablocknum)) {
print("Not enough room on disk to store dummy file.\n");
cleanup(10);
}
/*
* Now generate message containing track data.
*/
strcpy(msgbuf, FileMessage);
if (lowcyl == highcyl) {
strcat(msgbuf, "This file contains disk track ");
strcat(msgbuf, numtostr(lowcyl));
} else {
strcat(msgbuf, "This file contains disk tracks ");
strcat(msgbuf, numtostr(lowcyl));
strcat(msgbuf, " to ");
strcat(msgbuf, numtostr(highcyl));
}
strcat(msgbuf, "\n\n");
if (diskdev->isffs) {
/*
* For FFS, just put raw data into block.
*/
strcpy((char *)datablock, msgbuf);
} else {
/*
* For OFS, fill in appropriate header details
*/
datablock->Header = key;
datablock->Type = Type_Data;
datablock->SeqNum = 1;
datablock->NextData = 0;
datablock->DataSize = strlen(msgbuf);
strcpy((char *)datablock->Data, msgbuf);
fixchecksum(datablock, &datablock->Checksum);
}
if (!writeblock(datablock, datablocknum)) {
print("Error writing file data block to disk\n");
cleanup(10);
}
/*
* Now fix up datablock pointers in file header
*/
headers[0].FirstBlock = datablocknum;
headers[0].DataBlocks[71] = datablocknum;
/*
* All the data blocks have been allocated correctly; now
* fix up the data checksums for each block. When the checksum
* is correct, all the longwords in the block will add up to zero
* (no carry when overflow occurs).
*/
for (i = 0; i < numheaders; i++)
fixchecksum(&headers[i], &headers[i].Checksum);
/*
* Finally, write all the file blocks back to disk. Note that
* they are written back to disk in reverse order. This way, the
* file header itself is the last thing to get written, so if an
* error occurs before hand, it won't be left in an inconsistent
* state.
*/
chkabort(); /* Give user a final chance to abort */
for (i = numheaders - 1; i >= 0; i--) {
if (!writeblock(&headers[i], headers[i].OwnKey)) {
print("Error writing file headers to disk\n");
cleanup(10);
}
}
}
/*
* mainline()
* ----------
*/
void main(argc,argv)
int argc;
char **argv;
{
unsigned int start, end; /* Cylinder numbers */
char *p;
#define NONUM(x) (!isdigit(argv[x][0]))
if (argc != 4 || NONUM(2) || NONUM(3)) {
help();
cleanup(10);
}
/*
* Strip off trailing dir and filenames to get device name
*/
pathname = argv[1];
strcpy(devname, pathname);
for (p = devname; *p && *p != ':'; p++)
;
if (*p != ':') {
print("The pathname must include both a device and a filename.\n");
cleanup(10);
}
p++; /* Skip over colon at end of device name */
*p = '\0';
if (strlen(pathname) <= strlen(devname)) {
print("The pathname must include both a device and a filename.\n");
cleanup(10);
}
start = atoi(argv[2]);
end = atoi(argv[3]);
if (!GetVolume(devname, diskdev)) {
print2(devname, " is not a valid disk device\n");
cleanup(10);
}
if (start > end) {
print("Start track is greater than end track.\n");
cleanup(10);
}
if (end >= diskdev->numcyl) {
print3("Maximum end track on ", devname, " is ");
print2(numtostr(diskdev->numcyl - 1), ".\n");
cleanup(10);
}
if (!diskdev->isknown) {
print(
"Sorry, only Old Filing System and Fast Filing System are supported.\n");
cleanup(10);
}
if (diskdev->blocksize != 512) {
print(
"Sorry, only devices with 512 byte blocks can be handled at present.\n");
cleanup(10);
}
if (diskdev->numblocks > (25 * 4064)) {
print("Sorry, this version doesn't support drives > 50 Mb\n");
cleanup(10);
}
diskport = (MSGPORT *)DeviceProc(devname);
if (!diskport) {
print3("Can't locate process for device ", devname, "\n");
cleanup(10);
}
/*
* Everything seems in order, so lets try and create the dummy file.
* After creating it, we flush the disk buffers etc. to ensure that
* the disk bitmap is current.
*/
filehandle = Open(pathname, MODE_NEWFILE);
if (!filehandle) {
print3("Couldn't open file ", pathname, ".\n");
cleanup(10);
}
Close(filehandle); filehandle = NULL;
lock = Lock(pathname, ACCESS_READ);
if (!lock) {
print3("Strangely, file ", pathname, " can no longer be accessed.\n");
cleanup(314); /* pi -- let's be weird */
}
filekey = ((struct FileLock *)BTOC(lock))->fl_Key;
flushdisk(diskport);
/*
* Assuming everthing is still okay, the next step is to read in
* the root block and do some checks to make sure we can do the
* allocation.
*/
opendev();
inhibit(diskport, TRUE);
inhibited = TRUE;
readrootblock();
if (rootblock->BitmapFlag != -1) {
print3("tfile: Bitmap for ", devname, " is not up to date.\n");
cleanup(10);
}
/*
* Read in bitmap, and mark all the blocks on the desired tracks
* as in-use. If any of these blocks are already in use, then
* abort with an error.
*/
readbitmap();
if (!filltracks(start, end)) {
print3("Tracks ",numtostr(start)," to ");
print2(numtostr(end)," contain AmigaDOS data.\n");
cleanup(10);
}
/*
* Now allocate file header blocks to store the AmigaDOS pointers
* to each block in the reserved tracks in. The memory copy of
* the bitmap is modified to reflect these additional blocks.
* Then the new blocks are written back to disk, and the
* file header of the existing file is updated to reflect the
* additional data blocks.
*/
createfileheaders(filekey,start,end);
/*
* Everything is now complete, so all that remains to be done
* is to write back the new bitmap, update the root block and
* exit.
*/
writebitmap();
cleanup(0);
}