home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fred Fish Collection 1.5
/
ffcollection-1-5-1992-11.iso
/
ff_disks
/
200-299
/
ff236.lzh
/
DiskHandler
/
dos1_2.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-08-09
|
45KB
|
1,344 lines
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* |_o_o|\\ Copyright (c) 1987 The Software Distillery. All Rights Reserved */
/* |. o.| || This program may not be distributed without the permission of */
/* | . | || the authors: BBS: */
/* | o | || John Toebes Dave Baker John Mainwaring */
/* | . |// */
/* ====== */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Change History */
/* 10-JUN-88 JAT - Corrected code which handles file extension blocks */
/* the AmigaDos technical reference manual is incorrect in */
/* its description of the HighSeq and DataBlockCount fields*/
/* */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* AmigaDos 1.2 support routines */
#include "handler.h"
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: IsSameName */
/* Purpose: Compare a BSTR to a string/length for a directory match */
/* NOTE: Case is insensitive */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int IsSameName(bstr, name, len)
register char * bstr;
register char * name;
register int len;
{
register char c1, c2;
/* Make sure they are the same length */
if (len != *bstr++)
{
BUG(("Length mismatch B=%ld n=%ld\n", *--bstr, len));
return(0);
}
/* Compare all the characters (ignoring case) */
while(len--)
{
/* Get the next characters to compare */
c1 = *name++; c2 = *bstr++;
/* Convert to lower case */
if ((c1 >= 'A') && (c1 <= 'Z')) c1 += ('a'-'A');
if ((c2 >= 'A') && (c2 <= 'Z')) c2 += ('a'-'A');
/* and make sure they match */
if (c1 != c2) return(0);
}
/* Seems to work so let it pass... */
return(1);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: NextKey */
/* Purpose: Find next key in directory rootkey starting at position key. */
/* Note: Key == 0 searches for first key. Traverses all hash chains. */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
KEY NextKey(global, key, info, rootkey)
GLOBAL global;
struct FileInfoBlock *info;
KEY key, rootkey;
{
KEY limit;
int hashval;
struct DirBlock *block;
limit = 0;
if (key == 0)
hashval = 0;
else
{
/* Note that we at least can count on the directory entries being in */
/* ascending order. This has two effects. First it means that we are*/
/* not running wild with back and forth seeks. Secondly it means that*/
/* if something is deleted, we can figure out what entries we already */
/* gave them by just a simple key comparison */
/* Already doing a directory, might as well continue on */
/* first locate the last block they looked at */
if ((block = (struct DirBlock *)GetBlock(global, key)) == NULL)
return(0);
hashval = hash(info->fib_FileName+1, info->fib_FileName[0]);
/* Now make sure it is the same block that we examined last time */
/* Make sure it is a directory entry belonging to the same parent */
/* it isn't safe to compare the name as case might have changed */
/* although we could compare the hash for the name. */
if ((block->db_Type != T_SHORT) ||
(block->db_Parent != rootkey) ||
(hash(block->db_Name+1, block->db_Name[0]) != hashval))
{
/* Well, something is certainly wrong here. We have to go back */
/* and start at the beginning of the current hash chain */
limit = key;
key = 0;
}
else
{
/* If there is nothing on the chain then tell them to look at the */
/* next hash value chain */
key = block->db_HashChain;
hashval++;
}
}
/* did we find something or will we have to look again */
if (key == 0)
{
/* OK, now we need to go to the root and search the hash chains */
if ((block = (struct DirBlock *)GetBlock(global,rootkey)) == NULL)
/* Funny, couldn't find the parent directory so just fail the */
/* search for the next key */
return(0);
while(hashval < HASHTABLESIZE)
{
if (key = block->db_HashTable[hashval++])
{
if (key > limit)
/* Had a hit on the key so return it. */
break;
/* Walk down the list until we hit an entry greater than the */
/* limit or the end of the list */
while((key != 0) && (key < limit))
{
if ((block = (struct DirBlock *)GetBlock(global, key)) == NULL)
return(0);
key = block->db_HashChain;
}
/* Relocate the parent block */
limit = 0;
if ((block = (struct DirBlock *)GetBlock(global,rootkey)) == NULL)
/* Funny, couldn't find the parent directory so just fail the */
/* search for the next key */
return(0);
}
}
}
/* if we didn't get anything useful, key is 0 */
return (key);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: ParentKey */
/* Purpose: Find key of parent directory of input key */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
KEY ParentKey(global, key)
GLOBAL global;
KEY key;
{
struct DirBlock *block;
if (key == 0 ||
(block = (struct DirBlock *)GetBlock(global,key)) == NULL)
return(0);
return(block->db_Parent);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: hash */
/* Purpose: Compute the hash value of a name */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int hash(name, len)
unsigned char *name; /* pointer to str to hash on */
int len;
{
int c;
int hashval;
/* Now compute a hashvalue for the result */
hashval = len;
while(len--)
{
c = *name++;
if (c >= 'a' && c <= 'z')
c = c - 'a' + 'A';
hashval = ((hashval * 13 + c ) & 0x7ff);
}
return(hashval % HASHTABLESIZE);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: Parse */
/* Purpose: Separate out the next node in a file name */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int parse(name, len, buf)
char **name;
int *len;
register char *buf;
{
register int left;
register int c;
register int curlen;
curlen = 0;
left = *len;
memset(buf, 0, 32);
while((--left >= 0) && ((c = *(*name)++) != '/') )
{
if (c == ':')
{
/* got a volume name terminator. We need to throw everything away */
curlen = 0;
buf[++curlen] = c;
break;
}
else
buf[++curlen] = c;
}
/* update our parse length */
if (left < 0)
*len = 0;
else
*len = left;
buf[0] = curlen;
BUGLSTR("Parsed Name:", buf+1, curlen);
/* Now compute a hashvalue for the result */
return(hash(buf+1, curlen));
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: GetProtect */
/* Purpose: Return the protection bits associated with a file entry */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int GetProtect(global,key)
GLOBAL global;
KEY key;
{
struct DirBlock *block;
block = (struct DirBlock *)GetBlock(global,key);
if (block == NULL)
return(FIBF_WRITE|FIBF_READ|FIBF_DELETE|FIBF_EXECUTE);
return(block->db_Protect);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: SetProtect */
/* Purpose: Set the protection bits for a file */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int SetProtect(global, key, prot)
GLOBAL global;
KEY key;
long prot;
{
struct DirBlock *block;
if (key == 0 ||
(block = (struct DirBlock *)ModBlock(global,key)) == NULL)
return(DOS_FALSE);
block->db_Protect = prot;
return(DOS_TRUE);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: GetType */
/* Purpose: Determine the type of a file entry */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int GetType(global, key)
GLOBAL global;
KEY key;
{
struct DirBlock *block;
block = (struct DirBlock *)GetBlock(global,key);
if (block == NULL)
return(0); /* A truely invalid type */
BUG(("GetType: addr %08lx for key %ld\n", block, key));
return(block->db_SecondaryType);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: SetCommentStr */
/* Purpose: Set the comment string for a file */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int SetCommentStr(global, key, comment)
GLOBAL global;
KEY key;
char *comment;
{
struct DirBlock *block;
if (key == 0 ||
(block = (struct DirBlock *)ModBlock(global,key)) == NULL)
return(DOS_FALSE);
memcpy((char *)block->db_Comment, comment, (*comment)+1 );
return(DOS_TRUE);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: GetInfo */
/* Purpose: Copy the file information for a dos info block */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int GetInfo(global, key, info)
GLOBAL global;
KEY key;
register struct FileInfoBlock *info;
{
register struct DirBlock *block;
char *p;
/* get the block associated with the key */
if ((key == 0) ||
((block = (struct DirBlock *)GetBlock(global,key)) == NULL))
return(DOS_FALSE);
/* It would be prudent here to check the type of block and see that it */
/* Is a true file entry block */
/* Now copy over the information they want */
info->fib_DiskKey = key;
info->fib_EntryType =
info->fib_DirEntryType = block->db_SecondaryType;
/* Set the entry type to 2 to make dir happy */
if (info->fib_EntryType == 1)
info->fib_EntryType =
info->fib_DirEntryType = 2;
p = (char *)block->db_Name;
memcpy(info->fib_FileName, p, (*p)+1);
info->fib_Protection = block->db_Protect;
info->fib_Size = block->db_Size;
info->fib_NumBlocks = (block->db_Size + (BLOCKSIZE-1))/BLOCKSIZE;
info->fib_Date.ds_Days = block->db_Days;
info->fib_Date.ds_Minute = block->db_Minutes;
info->fib_Date.ds_Tick = block->db_Ticks;
p = (char *)block->db_Comment;
memcpy(info->fib_Comment, p, (*p)+1 );
#if 0
BUG(("Done setting up info for key %ld\n",info->fib_DiskKey));
BUG(("Entry=%ld Protect=%ld Size=%ld Blocks=%ld\n", info->fib_DirEntryType, info->fib_Protection, info->fib_Size, info->fib_NumBlocks));
BUG(("Day=%ld Min=%ld Tick=%ld\n", info->fib_Date.ds_Days, info->fib_Date.ds_Minute, info->fib_Date.ds_Tick));
BUGBSTR("Name :", info->fib_FileName);
BUGBSTR("Comment :", info->fib_Comment);
#endif
return(DOS_TRUE);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: FindDir */
/* Purpose: Find a Directory associated with a file and parse out the name */
/* of the file from the file string */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
KEY FindDir(global, key, namep, lenp)
GLOBAL global;
KEY key;
char **namep;
int *lenp;
{
int len;
char *p, *t;
register struct DirBlock *block;
char namebuf[32];
int hashval;
len = *lenp;
p = *namep + len - 1;
/* Skip to the last colon or / in the name */
while ((*p != ':') && (*p != '/') && len) { p--; len--; }
*lenp -= len;
t = p;
if (len) t++;
/* Are they attempting to access '.' or '..' ? */
if (*t == '.' &&
((*lenp == 1) ||
((*lenp == 2) && (*(t+1) == '.'))))
{
/* Yes, so treat it as part of the directory */
len += *lenp;
*lenp = 0;
}
BUGLSTR("Locate: Path is :", *namep, len);
/* Now run through the string searching the directory */
while(len && key)
{
/* first go to the root of the directory they asked for */
block = (struct DirBlock *)GetBlock(global,key);
if (block == NULL)
{
BUG(("FindDir: Error accessing block %ld\n", key));
return(0);
}
if ((block->db_SecondaryType != ST_ROOT) &&
(block->db_SecondaryType != ST_USERDIR))
{
/* they want to traverse a file as a directory - give them hell */
global->pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
return(0);
}
/* break down the next subcomponent of the name */
hashval = parse(namep, &len, namebuf);
BUGBSTR("Parsed Name :", namebuf);
BUG(("Hashval = %ld\n", hashval));
/* Did they specify the root directory ? */
if ((namebuf[0] == 1) && (namebuf[1] == ':'))
{
key = global->Root;
BUG(("Moving to root: %ld\n", key));
}
else if ((namebuf[0] == 0) ||
((namebuf[0] == 2) && (namebuf[1]=='.') && (namebuf[2] == '.')))
{
key = block->db_Parent;
BUG(("Moving to paremt: %ld\n", key));
}
else if ((namebuf[0] != 1) || (namebuf[1] != '.'))
{
/* Search the current block for the given name */
for( key = block->db_HashTable[hashval]; key != 0;
key = block->db_HashChain)
{
block = (struct DirBlock *)GetBlock(global,key);
if (block == NULL)
{
BUG(("FindDir: Can't get block %ld\n", key));
return(0);
}
BUGBSTR("Block name:", block->db_Name);
if (IsSameName(block->db_Name, (char *)namebuf+1, namebuf[0]))
break;
}
}
}
if (key == 0)
global->pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
BUG(("locate: key is %ld\n", key));
return(key);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: FindEntry */
/* Purpose: Locate an entry in a directory */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
KEY FindEntry(global, key, name, len)
GLOBAL global;
KEY key;
char *name;
int len;
{
int hashval;
struct DirBlock *block;
BUGLSTR("FindEntry: Name is ", name, len);
if (len == 0)
{
BUG(("Length is zero, returning key=%ld\n", key));
return(key);
}
hashval = hash(name, len);
/* first go to the root of the directory they asked for */
if ((block = (struct DirBlock *)GetBlock(global,key)) == NULL)
return(0);
BUG(("Starting search for hash=%ld from key=%ld\n", hashval, key));
if ((block->db_SecondaryType != ST_ROOT) &&
(block->db_SecondaryType != ST_USERDIR))
{
BUG(("Secondary type %ld bad for key %ld\n", block->db_SecondaryType, key));
/* they want to traverse a file as a directory - give them hell */
global->pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
return(0);
}
global->prevkey = 0;
/* Search the current block for the given name */
for( key = block->db_HashTable[hashval]; key != 0;
key = block->db_HashChain)
{
if ((block = (struct DirBlock *)GetBlock(global,key)) == NULL)
return(0);
BUGBSTR("Block name:", block->db_Name);
if (IsSameName(block->db_Name, name, len))
break;
global->prevkey = key;
}
if (!key)
global->pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
return(key);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: MakeEntry */
/* Purpose: Create a new entry for a file */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
KEY MakeEntry(global, key, name, len, type)
GLOBAL global;
KEY key;
char *name;
int len;
int type;
{
int hashval;
KEY newkey, nextkey;
struct DirBlock *block;
LONG *zeroptr;
zeroptr = 0;
BUGLSTR("MakeEntry: name ", name, len);
hashval = hash(name, len);
/* first go to the root of the directory they asked for */
block = (struct DirBlock *)GetBlock(global,key);
/* When all else fails pop up to let it be handled */
if (block == NULL) return(0);
if ((block->db_SecondaryType != ST_ROOT) &&
(block->db_SecondaryType != ST_USERDIR))
{
/* they want to traverse a file as a directory - give them hell */
global->pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
return(0);
}
/* Get us a block to put the newly created item */
if (!(newkey = AllocateBlock(global, key, type)))
{
BUG(("MakeEntry:Unable to allocate a block %ld\n", newkey));
global->pkt->dp_Res2 = ERROR_DISK_FULL;
return(0);
}
/* try to put block into right place in parent directory */
if ((nextkey = LinkDir(global, key, newkey, hashval)) == -1)
{
FreeBlock(global, newkey);
return(0); /* invalid key - admission of failure */
}
BUG(("MakeEntry: new entry %d points to successor %d\n", newkey, nextkey));
/* Find the newly created block and fill it in */
block = (struct DirBlock *)ModBlock(global, newkey);
if (block == NULL)
{
/* Actually getting an error here is quite fatal because we already */
/* modified the directory entry. We need to figure out a way to back */
/* out of the creation. The only situation that should cause this is */
/* if the allocated block gets flushed out to disk and we are unable */
/* to read it in. In this case it is quite likely that the media is */
/* not usable. However we eventually may have to handle this case */
FreeBlock(global, newkey);
return(0);
}
memset((char *)block, 0, sizeof(struct DirBlock));
BUG(("MakeEntry: New entry %ld at %08lx\n", newkey, block));
block->db_HashChain = nextkey;
/* Fill in the remainder of the information */
block->db_Type = T_SHORT;
block->db_OwnKey = newkey;
block->db_SecondaryType = type;
memcpy(((char *)block->db_Name)+1, name, len);
*(char *)&block->db_Name[0] = len;
block->db_Parent = key;
SetDateInfo(global, block);
return(newkey);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: LocateEntry */
/* Purpose: Find an file entry */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
KEY LocateEntry(global, key, name)
GLOBAL global;
KEY key;
char *name;
{
int len;
len = *name++;
BUGLSTR("LocateEntry: Full Name is:", name, len);
if (!(key = FindDir(global, key, &name, &len)))
return(0);
BUGLSTR("LocateEntry: name ", name, len);
return(FindEntry(global, key, name, len));
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: CreateEntry */
/* Purpose: Create a new file entry */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
KEY CreateEntry(global, key, name, mode, type)
GLOBAL global;
KEY key;
char *name;
int mode;
int type;
{
int len;
KEY newkey;
struct DirBlock *block;
len = *name++;
BUGLSTR("CreateEntry: Name ", name, len);
if (!(key = FindDir(global, key, &name, &len)))
return(0);
BUGLSTR("CreateEntry: Parsed Name ", name, len);
if (newkey = FindEntry(global, key, name, len))
{
/* Entry already exists, may need to blow it away */
if (mode == NEWFILE)
{
global->pkt->dp_Res2 = ERROR_OBJECT_EXISTS;
return(0);
}
#if 0
/* Although logical course of action, the current filing system seems */
/* to not exactly work this way. */
if (GetProtect(global, newkey) & FIBF_DELETE)
{
global->pkt->dp_Res2 = ERROR_DELETE_PROTECTED;
return(0);
}
#endif
if (GetProtect(global, newkey) & FIBF_WRITE)
{
global->pkt->dp_Res2 = ERROR_WRITE_PROTECTED;
return(0);
}
if ((GetType(global, newkey) == ST_USERDIR) || (type == ST_USERDIR))
{
global->pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
return(0);
}
FreeDataChain(global, newkey);
/* Also we need to update the name information in the file */
/* This allows them to change the case of the name. Note that by */
/* doing this in place we preserve all the comment information and */
/* attributes of the old entry. Also since the entry doesn't move on */
/* disk we can keep it in the same plae on the hash chain. */
block = (struct DirBlock *)ModBlock(global, newkey);
if (block != NULL)
{
memcpy(((char *)block->db_Name)+1, name, len);
*(char *)&block->db_Name[0] = len;
/* Clear the archive bit so backup programs know it has been modified */
block->db_Protect &= ~FIBF_ARCHIVE;
}
}
else
{
/* We have a brand new file here, so make us an entry for it */
newkey = MakeEntry(global, key, name, len, type);
BUG(("CreateEntry: newkey is %ld\n", newkey));
}
return(newkey);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: FreeDataChain */
/* Purpose: Truncate the data chain for a file */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int FreeDataChain(global, basekey)
GLOBAL global;
KEY basekey;
{
KEY key, savekey;
long i;
struct FileBlock *block;
key = basekey;
while(key)
{
if ((block = (struct FileBlock *)GetBlock(global, key)) == NULL)
return(0);
for (i = 0; i < block->fb_HighSeq; i++)
FreeBlock(global, block->fb_DataBlock[(BLOCKSPERENTRY-1)-i]);
savekey = block->fb_Extension;
BUG(("FreeDataChain: Base:%ld Key:%ld next:%ld\n", basekey, key, savekey));
if (key != basekey)
FreeBlock(global, key);
key = savekey;
}
if ((block = (struct FileBlock *)ModBlock(global, basekey)) == NULL)
{
return(0);
}
block->fb_FirstData = block->fb_DataBlockCount = block->fb_HighSeq = 0;
block->fb_Extension = 0;
block->fb_Size = 0;
return(1);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: SeekDataChain */
/* Purpose: Locate a block in a file data chain */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
KEY SeekDataChain(global, efh, num)
GLOBAL global;
struct EFileHandle *efh;
long num;
{
struct FileBlock *block;
KEY key;
long off;
long entryblock;
if (num-- <= 0)
{
BUG(("Attempt to seek to %ld on %08lx\n", num+1, efh));
global->pkt->dp_Res2 = ERROR_SEEK_ERROR;
return(0);
}
entryblock = num/BLOCKSPERENTRY;
BUG(("SeekDataChain: num=%ld entry=%ld cur=%ld\n",num,entryblock,efh->efh_CurExtBlock));
if (entryblock != efh->efh_CurExtBlock)
{
/* Are we seeking forward? */
BUG(("Forward seek\n"));
if (entryblock > efh->efh_CurExtBlock)
{
entryblock -= efh->efh_CurExtBlock;
key = efh->efh_ExtBlockKey;
efh->efh_CurExtBlock += entryblock;
}
else
{
BUG(("Must seek from beginning\n"));
/* Need to seek from beginning to target */
efh->efh_CurExtBlock = entryblock;
key = efh->efh_Lock->fl_Key;
}
while(key && entryblock--)
{
if ((block = (struct FileBlock *)GetBlock(global, key)) == NULL)
return(0);
key = block->fb_Extension;
}
efh->efh_ExtBlockKey = key;
}
else
key = efh->efh_ExtBlockKey;
if (key)
{
off = num - (efh->efh_CurExtBlock * BLOCKSPERENTRY);
if ((block = (struct FileBlock *)GetBlock(global, key)) == NULL)
return(0);
if (off >= block->fb_HighSeq)
{
BUG(("SeekDataChain: %ld Out of range %ld for %ld\n", off, block->fb_HighSeq, off));
key = 0;
}
else
key = block->fb_DataBlock[(BLOCKSPERENTRY-1) - off];
}
BUG(("SeekDataChain: Returning block %ld\n", key));
return(key);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: AppendDataChain */
/* Purpose: Allocate a new block to extend a file data chain */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
KEY AppendDataChain(global, efh, highseq)
GLOBAL global;
struct EFileHandle *efh;
long highseq;
{
KEY key, blockkey, newkey;
struct FileBlock *block;
struct DataBlock *data;
KEY firstdata, filekey;
filekey = efh->efh_Lock->fl_Key;
if ((block = (struct FileBlock *)GetBlock(global, filekey)) == NULL)
{
BUG(("Couldn't locate the base node for key %ld\n", filekey))
return(0);
}
firstdata = block->fb_FirstData;
if (highseq)
{
key = SeekDataChain(global, efh, highseq);
if (key == 0)
{
/* Too far past end of file, issue an error */
BUG(("Append Error: efh:%08lx seq=%ld\n", efh, highseq));
global->pkt->dp_Res1 = DOS_FALSE;
global->pkt->dp_Res2 = ERROR_SEEK_ERROR;
return(0);
}
}
else
key = filekey;
/* Get the block to put the data into */
if ((newkey = AllocateBlock(global, key, T_DATA)) == 0)
return(0);
BUG(("Append: New data block #%ld at %ld Ext = %ld\n", highseq, newkey, efh->efh_ExtBlockKey));
if ((block = (struct FileBlock *)
ModBlock(global, efh->efh_ExtBlockKey)) == NULL)
goto appenderr;
if (block->fb_HighSeq != BLOCKSPERENTRY)
{
/* Will fit in the current file chain so put it there and go on */
block->fb_DataBlock[(BLOCKSPERENTRY-1)-block->fb_HighSeq] = newkey;
block->fb_HighSeq++;
}
else
{
/* need to allocate another extension chain */
BUG(("Need to allocate extension\n"));
if ((blockkey =
AllocateBlock(global, efh->efh_ExtBlockKey, T_SHORT)) == 0)
goto appenderr;
/* Succeeded so initialize the new extension chain */
if ((block = (struct FileBlock *)
ModBlock(global, efh->efh_ExtBlockKey)) == NULL)
goto initerr;
block->fb_Extension = blockkey;
if ((block = (struct FileBlock *)ModBlock(global, blockkey)) == NULL)
{
/* odds of this situation are extremely low since allocateblock */
/* was supposed to set up the sitution, but let's be safe */
initerr:
FreeBlock(global, blockkey);
appenderr:
FreeBlock(global, newkey);
return(0);
}
memset((char *)block, 0, sizeof(struct FileBlock));
block->fb_Type = T_LIST;
block->fb_OwnKey = blockkey;
block->fb_DataBlockCount = 0;
block->fb_HighSeq = 1;
block->fb_FirstData = firstdata;
block->fb_Parent = filekey;
block->fb_Extension = 0;
block->fb_DataBlock[BLOCKSPERENTRY-1] = newkey;
block->fb_SecondaryType = ST_FILE;
efh->efh_CurExtBlock++;
efh->efh_ExtBlockKey = blockkey;
}
/* Go back and put the forward chain links in */
if ((block = (struct FileBlock *)ModBlock(global, key)) == NULL)
{
BUG(("Unable to mod previous block %ld\n", key));
return(0);
}
block->fb_FirstData = newkey;
/* Now lastly we need to set up the new data block */
if ((data = (struct DataBlock *)ModBlock(global, newkey)) == NULL)
{
BUG(("Unable to locate the new data block\n"));
return(0);
}
memset((char *)data, 0, sizeof(struct DataBlock));
data->db_Type = T_DATA;
data->db_Header = filekey;
data->db_SeqNum = highseq+1;
data->db_DataSize = 0; /* Since there is nothing in it yet */
data->db_NextData = 0; /* This is the last block in the file */
return(newkey);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: PUTDATA */
/* Purpose: Stuff file data into a data block */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void PUTDATA(block, pos, data, len)
char *block;
long pos;
char *data;
long len;
{
struct DataBlock *db = (struct DataBlock *)block;
memcpy(((char *)db->db_Data) + pos, data, len);
if ((len+pos) > db->db_DataSize)
db->db_DataSize = len+pos;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: GETDATA */
/* Purpose: Extract file data from a data block */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void GETDATA(data, len, block, pos)
char *data;
long len;
char *block;
long pos;
{
struct DataBlock *db = (struct DataBlock *)block;
memcpy(data, ((char *)db->db_Data) + pos, len);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: GetFileSize */
/* Purpose: Determine the current length of a file */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
long GetFileSize(global, efh)
GLOBAL global;
EFH efh;
{
struct DirBlock *db;
if ((db = (struct DirBlock *)GetBlock(global, efh->efh_Lock->fl_Key)) == NULL)
/* Couldn't read the directory entry so let them know */
return(-1);
return(db->db_Size);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: UpdateFile */
/* Purpose: Update the directory entry to indicate the current file length */
/* The date time stamp is also updated */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int UpdateFile(global, efh, blknum, blkpos)
GLOBAL global;
EFH efh;
long blknum;
long blkpos;
{
struct DirBlock *db;
BUG(("UpdateFile: %ld to %ld/%ld\n", efh->efh_Lock->fl_Key, blknum, blkpos));
if ((db = (struct DirBlock *)ModBlock(global, efh->efh_Lock->fl_Key)) == NULL)
/* Couldn't read the directory entry so let them know */
return(0);
db->db_Size = ((blknum-1)*BLOCKSIZE) + blkpos;
/* Clear the archive bit so the backup programs know it has been modified */
db->db_Protect &= ~FIBF_ARCHIVE;
SetDateInfo(global, db);
return(1);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: BlockKey */
/* Purpose: Return the key associated with a specific block in a file. */
/* If the block is one past the end of the file it will extend */
/* the file by one block */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
KEY BlockKey(global, efh, seq, writemode)
GLOBAL global;
EFH efh;
long seq;
int writemode;
{
KEY key;
key = SeekDataChain(global, efh, seq);
/* If we didn't find an already existing block when we are attempting to */
/* write to the block we want to append to the end of the file. Note that */
/* the test of seq is just an additional paranoid protection */
if (writemode && (key == 0) && seq)
{
BUG(("BlockKey: Extending file to %ld\n", seq));
key = AppendDataChain(global, efh, seq-1);
}
BUG(("BlockKey: Returning %ld\n", key));
return(key);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: SetDateInfo */
/* Purpose: Set the modified date/time stamp on the current entry */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void SetDateInfo(global, db)
GLOBAL global;
struct DirBlock *db;
{
/* Note that this presumes DOS is around to do the data stamping */
#if 0
DateStamp((long *)&db->db_Days);
#else
GetDateStamp(global, (struct DateStamp *)&db->db_Days); /* Just to experiment */
#endif
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: RenameDisk */
/* Purpose: Change the current disk label */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int RenameDisk(global, name)
GLOBAL global;
char *name;
{
struct RootBlock *root;
int len;
if ((global->volume == NULL) ||
((root = (struct RootBlock *)global->Root) == NULL))
return(DOS_FALSE);
/* Make sure we don't get a name to big */
len = *name++;
if (len >= NAMESIZE)
len = NAMESIZE - 1;
root->rb_Name[0] = len;
memcpy(root->rb_Name+1, name, len);
return(DOS_TRUE);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: DeleteEntry */
/* Purpose: Remove an directory entry */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int DeleteEntry(global, key, name)
GLOBAL global;
KEY key;
char *name;
{
int len;
int i;
KEY parent, nextkey;
struct DirBlock *block;
len = *name++;
/* First see if the entry really exists */
if (!(parent = FindDir(global, key, &name, &len)))
return(DOS_FALSE);
/* Ok, we got the directory, is the file or entry there ? */
if (!(key = FindEntry(global, parent, name, len)))
return(DOS_FALSE);
/* Make sure they haven't protected it against deletion */
if (GetProtect(global, key) & FIBF_DELETE)
{
global->pkt->dp_Res2 = ERROR_DELETE_PROTECTED;
return(DOS_FALSE);
}
/* Gee, it is there, now we need to go through and delete it */
/* First we get rid of the data chain for the file */
switch(GetType(global, key)) {
case ST_USERDIR:
if ((block = (struct DirBlock *)GetBlock(global, key)) == NULL)
return(DOS_FALSE);
/* Make sure the directory is empty first */
for (i= 0; i < HASHTABLESIZE; i++)
if (block->db_HashTable[i] != 0)
{
global->pkt->dp_Res2 = ERROR_DIRECTORY_NOT_EMPTY;
return(DOS_FALSE);
}
break;
case ST_FILE:
if (!FreeDataChain(global, key))
{
BUG(("Unable to release the data chain for the file %ld\n", key));
return(DOS_FALSE);
}
break;
default:
BUG(("What type of a thing is this they are deleting\n"));
global->pkt->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
return(DOS_FALSE);
/* break; compiler doesn't really like this */
}
/* Now we want to remove the entry from the directory */
/* to do this we want to find either the parent or the previous entry */
/* When we called FindEntry, it filled in global->prevkey with the */
/* previous hash chain entry if we were in the list OR it is 0 which */
/* means that we need to unlink it from our parent */
/* First we need to get the next entry in the hash chain */
if ((block = (struct DirBlock *)ModBlock(global, key)) == NULL)
return(DOS_FALSE);
/* Just to be nice, mark it as deleted */
block->db_Type |= T_DELETED;
nextkey = block->db_HashChain;
if (global->prevkey)
{
if ((block = (struct DirBlock *)ModBlock(global, global->prevkey)) == NULL)
return(DOS_FALSE);
block->db_HashChain = nextkey;
if ((block = (struct DirBlock *)ModBlock(global, parent)) == NULL)
return(DOS_FALSE);
}
else
{
if ((block = (struct DirBlock *)ModBlock(global, parent)) == NULL)
return(DOS_FALSE);
block->db_HashTable[hash(name, len)] = nextkey;
}
/* We are now at the parent block, so update the date time information on it */
SetDateInfo(global, block);
/* Also, mark the directory block for the file as empty */
FreeBlock(global, key);
return(DOS_TRUE);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: RenameEntry */
/* Purpose: Implement most of ActRenameEntry. Check for valid parameters */
/* then relink entry block to new directory location. */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int RenameEntry(global, fromkey, tokey, fromname, toname)
GLOBAL global;
KEY fromkey, tokey;
char *fromname, *toname;
{
struct DirBlock *block;
KEY parentkey, prevkey, nextkey, dirkey, ancestor;
int hashval, len;
if (!(fromkey = LocateEntry(global, fromkey, fromname)))
return(DOS_FALSE);
/* get some useful info while it's available, we're sure to need it */
prevkey = global->prevkey;
if ((block = (struct DirBlock *)GetBlock(global, fromkey)) == NULL)
return(DOS_FALSE);
parentkey = block->db_Parent;
hashval = hash(&block->db_Name[1], block->db_Name[0]);
nextkey = block->db_HashChain;
/* Now see if we can find the 'to' file. It shouldn't exist, except */
/* if we are only using rename to change the case of the name. */
len = *toname++;
if (!(dirkey = FindDir(global, tokey, &toname, &len)))
{
global->pkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND; /* or something.. */
return(DOS_FALSE);
}
if(tokey = FindEntry(global, dirkey, toname, len))
/* check this is not same file name with just case changed */
if (tokey != fromkey)
{
global->pkt->dp_Res2 = ERROR_OBJECT_EXISTS;
return(DOS_FALSE);
}
/* Check to see if we're trying to rename an ancestor to a */
/* descendant, which has the unfortunate effect of orphaning it. */
/* Can save some thrashing if source & dest are in same directory */
if (dirkey != parentkey)
{
ancestor = dirkey;
BUG(("RenameEntry: Ancestor = %d\n", ancestor));
while (ancestor)
{
if (ancestor == fromkey)
{
global->pkt->dp_Res2 = ERROR_OBJECT_IN_USE; /* Better ideas? */
return(DOS_FALSE);
}
if ((block = (struct DirBlock *)GetBlock(global, ancestor)) == NULL)
/* haven't located anything oedipal, nothing can be wrong, can */
/* be wrong, can be wrong... */
break;
ancestor = block->db_Parent;
}
BUG(("RenameEntry: Root = %d\n", ancestor));
}
/* Now ready to unlink the 'from' entry from its directory. */
BUG(("RenameEntry: parentkey=%d, prevkey=%d, nextkey=%d\n",
parentkey, prevkey, nextkey));
if (prevkey)
{
if ((block = (struct DirBlock *)ModBlock(global, prevkey))
== NULL)
return(DOS_FALSE);
block->db_HashChain = nextkey;
}
else
{
if ((block = (struct DirBlock *)ModBlock(global, parentkey)) == NULL)
return(DOS_FALSE);
BUG((" hashval = %d\n", hashval));
block->db_HashTable[hashval] = nextkey;
}
/* The disk seems to be writable - let's hope it stays that way */
/* Now link into the new directory under the new name */
nextkey = LinkDir(global, dirkey, fromkey, hash(toname, len));
if (nextkey == -1)
{
/* Relink from file back into from directory and return. */
if ((nextkey = LinkDir(global, parentkey, fromkey, hashval)) == -1)
/* How embarrasing! Still, with the checks we did earlier, this */
/* is definitely not likely to happen (I think...) */
return(DOS_FALSE);
if ((block = (struct DirBlock *)ModBlock(global, fromkey)) == NULL)
return(DOS_FALSE);
block->db_HashChain = nextkey;
return(DOS_FALSE); /* but we maintain our dignity */
}
/* if this next bit fails, we've screwed up the new directory... */
if ((block = (struct DirBlock *)ModBlock(global, fromkey)) == NULL)
return(DOS_FALSE); /* hope ModBlock set a good error in Res2 */
block->db_HashChain = nextkey; /* complete fwd link in directory */
block->db_Parent = dirkey;
memcpy(&block->db_Name[1], toname, len);
block->db_Name[0] = len;
BUG(("RenameEntry: Entry %d linked to directory %d\n", fromkey, dirkey));
return(DOS_TRUE);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Routine: LinkDir */
/* Purpose: link a block into a directory (almost - for logistic reasons, */
/* caller must fill return value into his own block->db_HashChain)*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
KEY LinkDir(global, parentkey, filekey, hashval)
GLOBAL global;
KEY parentkey, filekey;
int hashval;
{
struct DirBlock *block;
KEY nextkey;
block = (struct DirBlock *)ModBlock(global, parentkey);
if (block == NULL)
return(-1); /* not a nice key */
SetDateInfo(global, block); /* timestamp the change in the parent dir */
BUG(("LinkDir: Mod block %ld at %08lx newkey = %ld\n",
parentkey, block, filekey));
/* Enter the key into the list in a sorted fashion. The lowest key must */
/* appear first. */
nextkey = block->db_HashTable[hashval];
if (nextkey == 0 || filekey < nextkey)
{
/* it goes at the head of the chain, so we can simply put it there */
block->db_HashTable[hashval] = filekey;
}
else
{
while(1)
{
/* Not at the head of the chain. We need to find where it goes on */
/* the chain. First we get the next block in the chain and see */
/* what it links to. */
if ((block = (struct DirBlock *)GetBlock(global, nextkey)) == NULL)
return(-1); /* the bad news key */
/* Should we go after this block ? */
if (block->db_HashChain == 0 ||
block->db_HashChain > filekey)
{
/* Yes, make sure we can modify this block */
if ((block = (struct DirBlock *)ModBlock(global, nextkey)) == NULL)
return(-1); /* this'll slay them */
/* Insert us in the chain and stop the search */
nextkey = block->db_HashChain;
block->db_HashChain = filekey;
break;
}
/* advance search */
nextkey = block->db_HashChain;
}
}
/* if we got here we done good */
return(nextkey); /* note 0 is a healthy value here: end of the line */
}