/*- * $Id: hanlock.c,v 1.4 90/01/27 20:22:17 Rhialto Exp $ * $Log: hanlock.c,v $ * Revision 1.4 90/01/27 20:22:17 Rhialto * Added extra check when freeing MSFileLocks. * * Revision 1.3 90/01/23 00:36:57 Rhialto * Add an #ifndef READONLY. * * Revision 1.2 89/12/17 23:05:33 Rhialto * Add MSSetProtect * * Revision 1.1 89/12/17 20:03:01 Rhialto * * HANLOCK.C * * The code for the messydos file system handler * * The Lock department. Takes care of operations on locks, and consequently, * on directories. * * This code is (C) Copyright 1989 by Olaf Seibert. All rights reserved. May * not be used or copied without a licence. -*/ #include "han.h" #include "dos.h" #ifdef DEBUG # define debug(x) dbprintf x #else # define debug(x) #endif struct LockList *LockList; /* List of all locked files we have. Note * this is not the same as all locks we * have */ struct MSFileLock *RootLock; /* Lock on root directory */ struct MSFileLock *EmptyFileLock; /* 2nd result of MSLock() */ struct DirEntry FakeRootDirEntry = { { /* de_Msd */ "Unnamed ", /* msd_Name */ " ", /* msd_Ext */ ATTR_VOLUMELABEL, /* msd_Attributes */ {0}, /* msd_Pad1 */ 0, /* msd_Time */ 0x21, /* msd_Date, 1/1/80 */ 0, /* msd_Cluster */ 0 /* msd_Filesize */ }, -1, /* de_Sector */ 0 /* de_Offset */ }; byte DotDot[1 + 8 + 3] = ".. "; /* * This routine compares a name in a directory entry with a given name */ int CompareNames(dir, name) register struct MsDirEntry *dir; register byte *name; { #ifdef DEBUG if (name[0] == '\\') { extern short DBEnable; DBEnable = name[1] & 0x0F; return CMP_END_OF_DIR; } #endif if (dir->msd_Name[0] & DIR_DELETED_MASK) return CMP_FREE_SLOT; if (dir->msd_Name[0] == 0) return CMP_END_OF_DIR; /* end of directory */ if (dir->msd_Attributes & ATTR_VOLUMELABEL) return CMP_NOT_EQUAL; if (strncmp(dir->msd_Name, name, 8 + 3)) return CMP_NOT_EQUAL; if (dir->msd_Attributes & ATTR_DIRECTORY) return CMP_OK_DIR; return CMP_OK_FILE; } void NextDirEntry(sector, offset) register word *sector; register word *offset; { if ((*offset += MS_DIRENTSIZE) >= Disk.bps) { *offset = 0; if (*sector >= Disk.datablock) { /* Must be subdirectory */ *sector = NextClusteredSector(*sector); debug(("NextClusteredSector: %d\n", *sector)); } else { if (++*sector >= Disk.datablock) { *sector = SEC_EOF; } } } /* else no more work needed */ } /* * Get the directory entry following the given one. If requested, we make * the directory longer. */ struct DirEntry * FindNext(previous, createit) register struct DirEntry *previous; int createit; { byte *sector; word prevsec = previous->de_Sector; NextDirEntry(&previous->de_Sector, &previous->de_Offset); if (previous->de_Sector == SEC_EOF) { error = ERROR_OBJECT_NOT_FOUND; #ifndef READONLY if (createit) { if (prevsec < Disk.datablock - 1) { /* Should not be necessary */ previous->de_Sector = prevsec + 1; } else if (prevsec >= Disk.datablock) { previous->de_Sector = FindFreeSector(prevsec); } if (previous->de_Sector != SEC_EOF) { sector = EmptySec(previous->de_Sector); setmem(sector, (int) Disk.bps, 0); MarkSecDirty(sector); FreeSec(sector); setmem(&previous->de_Msd, sizeof (previous->de_Msd), 0); return previous; } } #endif } else if (sector = GetSec(previous->de_Sector)) { CopyMem(sector + previous->de_Offset, &previous->de_Msd, (long) MS_DIRENTSIZE); OtherEndianMsd(&previous->de_Msd); FreeSec(sector); return previous; } return NULL; } #ifdef DEBUG void PrintDirEntry(de) struct DirEntry *de; { debug(("%d,%d ", de->de_Sector, de->de_Offset)); debug(("%.8s.%.3s attr:%x time:%x date:%x start:%x size:%lx\n", de->de_Msd.msd_Name, de->de_Msd.msd_Ext, de->de_Msd.msd_Attributes, de->de_Msd.msd_Time, de->de_Msd.msd_Date, de->de_Msd.msd_Cluster, de->de_Msd.msd_Filesize )); } #endif /* * MakeLock makes a struct MSFileLock from a directory entry and the * parent directory MSFileLock pointer. It looks if it already has a Lock * on it. In that case, it simply increments its reference count, when * possible. */ struct MSFileLock * MakeLock(parentdir, dir, mode) struct MSFileLock *parentdir; struct DirEntry *dir; ulong mode; { register struct MSFileLock *fl; struct MSFileLock *nextfl; if (mode != EXCLUSIVE_LOCK || (dir->de_Msd.msd_Attributes & ATTR_DIR)) mode = SHARED_LOCK; #ifdef DEBUG debug(("MakeLock: ")); PrintDirEntry(dir); #endif /* * Look through our list to see if we already have it. The criteria * for this are: 1. the directory entries are the same or 2. they have * the same first cluster and are both directories (which can have * multiple directory entries). Sigh. */ for (fl = (struct MSFileLock *) LockList->ll_List.mlh_Head; nextfl = (struct MSFileLock *) fl->msfl_Node.mln_Succ; fl = nextfl) { #ifdef DEBUG debug(("> ")); PrintDirEntry(&fl->msfl_Msd); #endif if ((fl->msfl_DirSector == dir->de_Sector && fl->msfl_DirOffset == dir->de_Offset) || (fl->msfl_Msd.msd_Cluster == dir->de_Msd.msd_Cluster && (dir->de_Msd.msd_Attributes & ATTR_DIR) && (fl->msfl_Msd.msd_Attributes & ATTR_DIR)) ) { /* Found existing lock on file */ if (fl->msfl_Refcount < 0 || mode == EXCLUSIVE_LOCK) { error = ERROR_OBJECT_IN_USE; return NULL; } fl->msfl_Refcount++; return fl; } } fl = AllocMem((long) sizeof (*fl), MEMF_PUBLIC); if (fl == NULL) { error = ERROR_NO_FREE_STORE; return NULL; } fl->msfl_Parent = parentdir ? MSDupLock(parentdir) : NULL; fl->msfl_Refcount = (mode == EXCLUSIVE_LOCK) ? -1 : 1; fl->msfl_DirSector = dir->de_Sector; fl->msfl_DirOffset = dir->de_Offset; fl->msfl_Msd = dir->de_Msd; AddHead(&LockList->ll_List, fl); return fl; } /* * This routine Locks a file. It first searches it in the directory, then * lets the rest of the work be done by MakeLock(). If it encounters an * empty slot in the directory, it remembers where, in case we need it. If * you clear the MODE_CREATEFILE bit in the mode parameter, we fabricate a * new MSFileLock from the empty directory entry. It then becomes the * caller's responsibility to MSUnLock() it eventually. */ struct MSFileLock * MSLock(parentdir, name, mode) struct MSFileLock *parentdir; byte *name; ulong mode; { byte *sector; struct MSFileLock *newlock; register struct DirEntry *de; struct DirEntry sde; byte *nextpart; byte component[8 + 3]; /* Note: not null-terminated */ int createit; word freesec; word freeoffset; de = &sde; newlock = NULL; /* * See if we have an absolute path name (starting at the root). */ { register byte *colon; if (colon = index(name, ':')) { name = colon + 1; parentdir = RootLock; } } /* * Get a copy of the parent dir lock, so we can walk it over the * directory tree. */ parentdir = MSDupLock(parentdir); /* * Start with the directory entry of the parent dir. */ sde.de_Msd = parentdir->msfl_Msd; sde.de_Sector = parentdir->msfl_DirSector; sde.de_Offset = parentdir->msfl_DirOffset; #ifdef DEBUG debug(("pdir %08lx: ", parentdir)); PrintDirEntry(&parentdir->msfl_Msd); #endif newdir: freesec = SEC_EOF; /* Means none found yet */ nextpart = ToMSName(component, name); debug(("Component: '%11s'\n", component)); if (nextpart[0] != '/') { nextpart = NULL; #ifndef READONLY /* * See if we are requested to get an empty spot in the directory * if the given name does not exist already. The value of mode is * not important until we actually create the filelock. */ if (!(mode & MODE_CREATEFILE)) { mode ^= MODE_CREATEFILE; createit = 1; } else createit = 0; #endif } else nextpart++; /* * Are we at the end of the name? Then we are finished now. This works * because sde contains the directory entry of parentdir. */ if (name[0] == '\0') goto exit; /* * If there is more name, we enter the directory, and here we get the * first entry. */ sde.de_Sector = DirClusterToSector(sde.de_Msd.msd_Cluster); sde.de_Offset = 0; if ((sector = GetSec(sde.de_Sector)) == NULL) goto error; CopyMem(sector, &sde.de_Msd, (long) sizeof (struct MsDirEntry)); OtherEndianMsd(&sde.de_Msd); FreeSec(sector); while (de) { switch (CompareNames(&sde.de_Msd, component)) { case CMP_FREE_SLOT: if (freesec == SEC_EOF) { freesec = sde.de_Sector; freeoffset = sde.de_Offset; } /* Fall through */ case CMP_NOT_EQUAL: de = FindNext(&sde, createit); /* Try next directory * entry */ continue; case CMP_OK_DIR: if (name = nextpart) { /* * We want to keep locks on all directories between each * bottom directory and file lock, so we can easily find * the parent of a lock. There just seems to be a problem * here when we enter the 'subdirectories' . or .. , but * that in not so: MakeLock will detect that it has * already a lock on those, and NOT make :one/two the * parent of :one/two/.. . */ newlock = MakeLock(parentdir, de, SHARED_LOCK); MSUnLock(parentdir); parentdir = newlock; goto newdir; } goto exit; case CMP_OK_FILE: if (nextpart) { error = ERROR_OBJECT_WRONG_TYPE; de = NULL; } goto exit; case CMP_END_OF_DIR: /* means we will never find it */ error = ERROR_OBJECT_NOT_FOUND; if (freesec == SEC_EOF) { freesec = sde.de_Sector; freeoffset = sde.de_Offset; } de = NULL; goto exit; } } exit: if (de) { newlock = MakeLock(parentdir, &sde, mode); } else { newlock = NULL; #ifndef READONLY if (createit && /* Do we want to make it? */ error == ERROR_OBJECT_NOT_FOUND && /* does it not exist yet? */ nextpart == NULL) { /* do we have the last part of the name */ if (freesec != SEC_EOF) { /* is there any room? */ if (IDDiskState == ID_VALIDATED) { error = 0; setmem(&sde.de_Msd, sizeof (sde.de_Msd), 0); sde.de_Sector = freesec; sde.de_Offset = freeoffset; /* ToMSName(sde.de_Msd.msd_Name, name); */ strncpy(sde.de_Msd.msd_Name, component, 8 + 3); EmptyFileLock = MakeLock(parentdir, &sde, mode); WriteFileLock(EmptyFileLock); } else error = ERROR_DISK_WRITE_PROTECTED; } else error = ERROR_DISK_FULL; } #endif } error: MSUnLock(parentdir); return newlock; } /* * This routine DupLocks a file. This simply means incrementing the * reference count, if it was not an exclusive Lock. */ struct MSFileLock * MSDupLock(fl) struct MSFileLock *fl; { if (fl == NULL) fl = RootLock; if (fl->msfl_Refcount <= 0) { error = ERROR_OBJECT_IN_USE; return NULL; } else { fl->msfl_Refcount++; } return fl; } /* * This routine DupLocks the parent of a lock, if there is one. */ struct MSFileLock * MSParentDir(fl) register struct MSFileLock *fl; { if (fl == NULL || fl == RootLock) { error = ERROR_OBJECT_NOT_FOUND; } else if (fl->msfl_Parent) return MSDupLock(fl->msfl_Parent); return NULL; } /* * This routine UnLocks a file. */ int MSUnLock(fl) struct MSFileLock *fl; { #ifdef DEBUG debug(("MSUnLock %08lx: ", fl)); PrintDirEntry(&fl->msfl_Msd); #endif if (fl) { if (--fl->msfl_Refcount <= 0) { struct LockList *list; list = (struct LockList *) fl->msfl_Node.mln_Pred; Remove(fl); debug(("Remove()d %08lx\n", fl)); /* * We may need to get rid of the LockList if it is empty. This * is the current LockList iff we are called from * MSDiskRemoved(). Please note that we are not even sure that * 'list' really is the list header, therefore the careful * test if fl refers to a volume label (root lock) which is * finally UnLock()ed. Because of the recursion, we only try to * free the LockList iff there is no parent anymore, since * otherwise list may be invalid by the time we use it. */ if (fl->msfl_Parent) { MSUnLock(fl->msfl_Parent); } else { if ((fl->msfl_Msd.msd_Attributes & ATTR_VOLUMELABEL) && ((void *) list->ll_List.mlh_Head == (void *) &list->ll_List.mlh_Tail) ) { FreeLockList(list); } } FreeMem(fl, (long) sizeof (*fl)); } } return DOSTRUE; } /* * This is (among other things) the inverse of ToMSName(). */ void ExamineDirEntry(msd, fib) struct MsDirEntry *msd; register struct FileInfoBlock *fib; { register byte *end, *dot; #ifdef DEBUG debug(("+ ")); PrintDirEntry(msd); #endif strncpy(&fib->fib_FileName[1], msd->msd_Name, 8); /* Keep at least one character, even a space, before the dot */ dot = ZapSpaces(&fib->fib_FileName[2], &fib->fib_FileName[1 + 8]); dot[0] = ' '; strncpy(dot + 1, msd->msd_Ext, 3); dot[4] = '\0'; end = ZapSpaces(dot, dot + 4); if (end > dot) dot[0] = '.'; fib->fib_FileName[0] = strlen(&fib->fib_FileName[1]); fib->fib_DirEntryType = (msd->msd_Attributes & ATTR_DIR) ? FILE_DIR : FILE_FILE; fib->fib_Protection = 0; if (!(msd->msd_Attributes & ATTR_ARCHIVED)) fib->fib_Protection |= FIBF_ARCHIVE; if (msd->msd_Attributes & ATTR_READONLY) fib->fib_Protection |= (FIBF_WRITE | FIBF_DELETE); if (msd->msd_Attributes & (ATTR_HIDDEN|ATTR_SYSTEM)) fib->fib_Protection |= FIBF_HIDDEN; fib->fib_Size = msd->msd_Filesize; fib->fib_NumBlocks = (msd->msd_Filesize + Disk.bps - 1) / Disk.bps; ToDateStamp(&fib->fib_Date, msd->msd_Date, msd->msd_Time); fib->fib_Comment[0] = 0; } int MSExamine(fl, fib) struct MSFileLock *fl; register struct FileInfoBlock *fib; { if (fl == NULL) fl = RootLock; fib->fib_DiskKey = ((ulong) fl->msfl_DirSector << 16) | fl->msfl_DirOffset; fib->fib_EntryType = 0; /* No ExNext called yet */ ExamineDirEntry(&fl->msfl_Msd, fib); return DOSTRUE; } int MSExNext(fl, fib) struct MSFileLock *fl; register struct FileInfoBlock *fib; { word sector = fib->fib_DiskKey >> 16; word offset = (word) fib->fib_DiskKey; byte *buf; if (fl == NULL) fl = RootLock; if (fib->fib_EntryType == 0) { fib->fib_EntryType = 1; if (fl->msfl_Msd.msd_Attributes & ATTR_DIR) { /* Enter subdirectory */ sector = DirClusterToSector(fl->msfl_Msd.msd_Cluster); offset = 0; } else NextDirEntry(§or, &offset); } else { skip: NextDirEntry(§or, &offset); } if (sector != SEC_EOF) { register struct MsDirEntry *msd; if (buf = GetSec(sector)) { msd = (struct MsDirEntry *) (buf + offset); if (msd->msd_Name[0] == '\0') { FreeSec(buf); goto end; } if (msd->msd_Name[0] & DIR_DELETED_MASK || msd->msd_Name[0] == '.' || /* Hide "." and ".." */ (msd->msd_Attributes & ATTR_VOLUMELABEL)) { FreeSec(buf); goto skip; } OtherEndianMsd(msd);/* Get correct endianness */ fib->fib_DiskKey = ((ulong) sector << 16) | offset; ExamineDirEntry(msd, fib); OtherEndianMsd(msd);/* Get wrong endianness */ FreeSec(buf); return DOSTRUE; } } end: error = ERROR_NO_MORE_ENTRIES; return DOSFALSE; } /* * Convert AmigaDOS protection bits to messy attribute bits. */ long MSSetProtect(parentdir, name, mask) register struct MSFileLock *parentdir; char *name; long mask; { register struct MSFileLock *lock; if (parentdir == NULL) parentdir = RootLock; lock = MSLock(parentdir, name, EXCLUSIVE_LOCK); if (lock) { /* Leave SYSTEM bit as-is */ lock->msfl_Msd.msd_Attributes &= ATTR_SYSTEM; /* write or delete protected -> READONLY */ if (mask & (FIBF_WRITE|FIBF_DELETE)) lock->msfl_Msd.msd_Attributes |= (ATTR_READONLY); /* hidden -> hidden */ if (mask & FIBF_HIDDEN) lock->msfl_Msd.msd_Attributes |= (ATTR_HIDDEN); /* archived=0 (default) -> archived=1 (default) */ if (!(mask & FIBF_ARCHIVE)) lock->msfl_Msd.msd_Attributes |= (ATTR_ARCHIVED); WriteFileLock(lock); MSUnLock(lock); return TRUE; } return FALSE; } int CheckLock(lock) register struct MSFileLock *lock; { register struct MSFileLock *parent; if (lock) { while (parent = lock->msfl_Parent) lock = parent; if (lock != RootLock) error = ERROR_DEVICE_NOT_MOUNTED; } return error; } #ifndef READONLY void WriteFileLock(fl) register struct MSFileLock *fl; { debug(("WriteFileLock %08lx\n", fl)); if (fl) { register byte *block = GetSec(fl->msfl_DirSector); if (block) { CopyMem(&fl->msfl_Msd, block + fl->msfl_DirOffset, (long) sizeof (fl->msfl_Msd)); OtherEndianMsd(block + fl->msfl_DirOffset); MarkSecDirty(block); FreeSec(block); } } } void UpdateFileLock(fl) register struct MSFileLock *fl; { struct DateStamp dateStamp; debug(("UpdateFileLock %08lx\n", fl)); DateStamp(&dateStamp); ToMSDate(&fl->msfl_Msd.msd_Date, &fl->msfl_Msd.msd_Time, &dateStamp); WriteFileLock(fl); } #endif struct LockList * NewLockList(cookie) void *cookie; { struct LockList *ll; if (ll = AllocMem((long) sizeof (*ll), MEMF_PUBLIC)) { NewList(&ll->ll_List); ll->ll_Cookie = cookie; } else error = ERROR_NO_FREE_STORE; return ll; } void FreeLockList(ll) struct LockList *ll; { debug(("FreeLockList %08lx\n", ll)); if (ll) { MayFreeVolNode(ll->ll_Cookie); /* not too happy about this */ FreeMem(ll, (long) sizeof (*ll)); if (ll == LockList) /* locks on current volume */ LockList = NULL; } }