/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* |_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 */ }