/*-
 * $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 %qW1G\cb.Yf
bJPLN..vFrbNv
N
֔PΆ\

*?J|M,f@ 43@ 	y$ڸ6.ĺڴ>>>vPp 0, z ,  Rf 2?VS`DSvHd I|Hސ"'qš%p8  $ތ&~cD6=sh$&%m~
	0RXM**"5\4>c
Ԃ`GH $!]pB @VtA%I|F
ȓ"*Fs8Aabk H. %II#$Ih%,gdҞ'L9q@70cA 0IHNK
 1ĵ Q"aI>hl I$ddx9:(RpBA@H@%
X<pQI0u(&9vYԐ/
Ac+K*,4*Ҡ_P2(%fP%F ("' dPEK9S$@N#j(= 0<3LC<|XC3(
_ 	@@ Zi\+1 !@2B&ُ #`
%OgRA'[/40G<
s  @RVT D%  @aQC4& N``7@   -Nef  /j<V@2`8: 0mJ	3++.83 KtH
	ʹF`tט 	$J㐇Df  ACxGGZ!__*vb
#{N%9A 5~D㷔AƵ\ ̎꒨IFӈ.3Gq'sI+Hc*Q~`<(jiQ? 2 _11	L E\Ui _ 	3_¥h[	JaRf 2?VS`DSvHd I|Hސ"'qš%p8  $ތ&~cD6=sh$&%m~
	0RXM**"5\4>c
Ԃ`GH $!]pB @VtA%I|F
ȓ"*Fs8Aabk H. %II#$Ih%,gdҞ'L9q@70cA 0IHNK
 1ĵ %0Q3ꍽ<d"иbYs JJB brU1D 0P 96	ga `m emuf;l1'Q(0PH H
HA@Ii!ﴛhs/9b1نA-$'YLk!mnb> Ưw{.#CHcbqCۘ*K
sLch|Ȁ1'd`.ʥzבDzN~=$ g,@v}wI e^rx 	~X -}0@
PRmMHAS(Y5$!I	F:ilntl$z@ Xf   p <>8X#M《428X#6,`$@As#A%K"}Nɗّ3hH`%vhn?*]amMl$hq5\	fDy41t:8ϑ33P˲0ǁ?Gƞ!eg	J>detgK/)f>$7CK{Gz$غ!G*BCK
 "?F  8  `&&%<Vz6z:OU$E$DjtT>@D `J?
(l$팴u62pƐaЕ`2; vv<h>#>D4qt񀧔a\	yX; (	XښR# 2XD# NNNN|!F[N  
] ؘch2 T&

*t?@ bp)v@3#u(2d((\~0@l;;r_ZcI8'66~N64k@c""&QF`wX Zhfv0 u;Fe7\AƂ		q921"R1I2SJN)1$m\e(2 I+*P yHءA*	`@m!i4e8`C`  $P@׀ F	$-`4	0D ihH
H(`FT(I.klq 
#rh6ȜP`s.'(0;&*AEnbg8QVC`."D,&? @g0b G(!26F Bj"BÄƗf BYJX
H%/ټ	yt"8{q81ry@6[q80RoPڀ"@s @ψG5o ?/7(X:;00SO%
q9Q A%&JA x	t I[8br	|=FQhV \;KN|pDh e~ sOE:0:Y}?9W{g_{S8>beAA[:Ezɓ[nu!pI'UHVXM4yP`N&	 n8ssfST 5?N!9%9%Ix폘f7f`3L2'^j|ȆV#aIb>G,= o^$>5=AƝ'[c$
Ԃ@8.u@|4  $H,  P)Q HC"p,b&+ ]k &2@% 6I(FmD O֣Ё%	"y2/IC90Nn0@>[	me,
	   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 'subdireV*4!G*ø/۴T^	(!6EX?jX԰c.Q1B	.@HCfgy;*	HثtHZL/]RUeׅ:Vj>۠vuL"z'ygiiǿ|d]$L
ͻxфCh!IC3G1t|4$*^pD
ʎ wrO̰Ϗo&ꏯ.Hy}^+I Ǳ&jp'h3y.|e\pG+ma*VB(:(&)Q=?ج?_!
3ݏ)<vLtf<bmy!FiIR01)^q]uԳ1&*uk8< wшig\.91]Xj'LW4}TÝ>[`P-lh1-C    &wT  dJ$ ![m!0A3͘.%ӊu
зևOOT]h
 x9N?b	.zZR|.ow-iwev#0"$@n.=ro5	`"h_}q0;C[IRl٥tL\b0Lݖ/99c1'U 2|tRgO7ʹ
sߏdkq vREזrҩ^>3TOMs|agcfD+,S|6ࡌ0Ψ:ZPpF4ϜO0 )uvIX\,P2P%Bg<hfOj/b0MG*1}$<n0V3fUzT[μz:b}fvWNro᜝7.PjO"(mMVOϯfݰqp։ÍbĴ%AZ+6uED=|@y1daG e>a ~7P1=~>>}|~0H˔\ŧ$[m%e
Qmn xӢF?OaJ_d̈́X!Y(";..4-j[kwq4BPJk0~PjcYSP5Pa$Zb$Ū]_wF0͡Qb\4+H`M}͑yMgPT?jY0sgHМ@*b1<v|U91@D,ԼM_&%/pvsD}ھIC<cl<;ѧ` 5 Qsrg|*C;9A}j[l',&/tT-Ϙߞ{F6a`xA"`Π]o=n&;!näN~-d^Ã..sE0ސ_Q7Z]Ud~K$i@$C$9cOXUMkIKc,/zڳ/MehewsْSQgO_o2r*o8!BJ֠*ҘP|'3P -85t;+v>N&g[s@<?RO[61!]׷ n{_{/SܸEGE]eUAՎ* '@0WCă{frh
5Cf\C PBь,CP瞭*f\:=AjOY/M.rnnNL&G3qJ/X-,&*$K倛{ cteJw`wZ)U2!hr|Wﻫ|#W6Al~B}r7ǂ
cv4Z@TIB]W&d̱.Wxyg&N:<onc)%XK7)y_)iQm/~ub7ء_UЛTF?h}d=Ed/H~j6.lt("l\aՆ9{eoVᓌ~|Niۺz,̏BuD|Җ%5g^ywAr _XUy<g-P9 uK|!H-lh1-    wT  device.h9mf&Wy_M[m--
Wvvwn=fϾ8MehÛZ }!dCkv?Wo>'P:^}SFB$Q{,m;Wy|z]\ާ/"0X1f3nFFach4:Yz~x<|!w>/X_z4za$,Fa:VꇕP?;"hnֶ?9Bг:zQؖ3XV|j.]AʨqHMK`DgVlo6﷧LepPWF^eA$\7>~T∭ Cy_MAa%`6ߘԲOOeDUijQ|կd	}Z?+"$ߊLO+oyI0HacG({ 8}QX֗O]t	_mas񄐶'aj"im\Ky71^(M
qfĊؠdd3Lj0c+%?A䵡) o.7~I%t(̉k7GJkBگGyPxk/^+!߫Ҝ}{9?/!{2s
5B,X46=l"dm5Q?Oqy)侤԰מ%ಃ3 	'O\e)c7#'8*,y.LLSbĽ]WY_?A+[KK+iZ#V"g,}٦Sv닢##Q- u<vO?5U~" E49Ux,]YM[S6MFW&d!:S~To|bcX }Ho^pTT`Ed鯆3|'ÔsftJ,*G#۱kl4:9O5Y
*ӯ )'' {ߡz@]A+L2TQМ'nnf"gKul&_ ߫0:?#qV{,S}x'$<U%쭵~A-<o=j!JaVIj|Z7ВibF~.6[bv)y'o/]zYN>p$J*[8J(4RrZp)l:;eBS`19ܻSъnҔw:"X!^nM@=Ə߅l{]'~ P<~N^5s\
zE$r`>=sxv.t	&𑖔ٟV!F2xj;0akKUlJ}|	P3NiZt%﬉"ۻ*OR|qZyFe68"bHo&`H}r5$C=2[Pmiy\Z%*lF_)>㋬\N/Wg<]-kqHHGh__+A-r%㸶xj,[R?>RV;:gL?Mf0;ϻdYv[89d>L~hӊD+8.m+K<<s~Ƌ7?-meʵ.}NyTaPˑuqI]fuof{nP' hbGx[	܇+Zs-GDq<)X}D]'7pt
Ob0~k'HĥynFB-ci!w'`aP,0ls>hp;L2Ymb<R{Faf:SvjUL]ġݕ1AҩVt
do>ˁܗl8#S[sȹV$̠@ϝlO1t]2aj2鷴 ncwlz\`cb9Lvұ\#WpqyXÏ/Bǉt{ǮY|ǻA+hG/8O0y>WqT	
wfPƽi+zQr] "'GU?w.&#^f'WiWڬ5+Fx8ዐX=cKmˈ@0[..L_s?/O,M>bv?i~5MI:S_B9&P?n'Rt;4fՈAAJ1Z!sxuV9ZzsuD>K$<K%.\UTS[1kF;03[TS""Bm7LOۗjG}u2B쾏g-*wKIUݑ؉\3$Ә.É*bE,_,,hۥz8G~+xJnS^\/ac\N*'y AO屿gw/1Tw'e/Tq5EH[S)f&~V}
/8wߚk<mF,2f!>00Q6/5IF|2S,xO$%#y&ĊđpM'^%wWVn:)f`VZec?]=tH!ٿeٽvk}vՂjb^>kNfhYPGFH.vUf@./N)x)|?,kvMXZ{VS^R\KȰq>^<Ggl  sector = DirClusterToSector(fl->msfl_Msd.msd_Cluster);
	    offset = 0;
	} else
	    NextDirEntry(&sector, &offset);
    } else {
skip:
	NextDirEntry(&sector, &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;
    }
}
