/************************************************************
* MultiUser - MultiUser Task/File Support System				*
* ---------------------------------------------------------	*
* Segment Management														*
* ---------------------------------------------------------	*
* © Copyright 1993-1994 Geert Uytterhoeven						*
* All Rights Reserved.													*
************************************************************/


#include <exec/execbase.h>
#include <exec/alerts.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>

#include "Memory.h"
#include "Segment.h"
#include "Misc.h"
#include "Config.h"
#include "Locale.h"
#include "LibHeader.h"
#include "Task.h"


	/*
	 *		Static Routines
	 */

static struct muSegNode *AddSegNode(BPTR seglist, ULONG owner);
static void RemSegNode(struct muSegNode *snode);
static struct muSegNode *FindSegNode(BPTR SegList);
static struct muExtOwner *GetSegOwner(BPTR seglist);


	/*
	 *		Allocate and Add a Segment Node
	 *
	 *		Make sure you have access to the list (via ObtainSemaphore(Shared))!!
	 */

static struct muSegNode *AddSegNode(BPTR seglist, ULONG owner)
{
	struct muSegNode *snode;

	if (snode = MAlloc(sizeof(struct muSegNode))) {
		snode->SegList = seglist;
		snode->Owner.uid = (owner & muMASK_UID)>>16;
		snode->Owner.gid = owner & muMASK_GID;
		AddHead((struct List *)&muBase->SegOwnerList, (struct Node *)&snode->Node);
	}
	return(snode);
}


	/*
	 *		Remove and Deallocate a Segment Node
	 *
	 *		Make sure you have access to the list (via ObtainSemaphore(Shared))!!
	 */

static void __inline RemSegNode(struct muSegNode *snode)
{
	Remove((struct Node *)&snode->Node);
	Free(snode, sizeof(struct muSegNode));
}


	/*
	 *		Find the Segment Node for a given SegList
	 *
	 *		Make sure you have access to the list (via ObtainSemaphore(Shared))!!
	 */

static struct muSegNode *FindSegNode(BPTR seglist)
{
	struct MinNode *node;
	struct muSegNode *snode;

	for (node = muBase->SegOwnerList.mlh_Head;
		  node->mln_Succ && ((snode = (struct muSegNode *)node)->SegList != seglist);
		  node = node->mln_Succ);
	if (!node->mln_Succ)
		snode = NULL;
	return(snode);
}


	/*
	 *		Get the owner of a Segment
	 *
	 *		Make sure you have access to the list (via ObtainSemaphore(Shared))!!
	 */

static struct muExtOwner *GetSegOwner(BPTR seglist)
{
	struct muExtOwner *owner = NULL;
	struct muSegNode *snode;

	ObtainSemaphoreShared(&muBase->SegOwnerSem);
	if (snode = FindSegNode(seglist))
		owner = &snode->Owner;
	ReleaseSemaphore(&muBase->SegOwnerSem);
	return(owner);
}


	/*
	 *		Init Segment List
	 */

void InitSegList(void)
{
	ObtainSemaphore(&muBase->SegOwnerSem);
	NewList((struct List *)&muBase->SegOwnerList);
	ReleaseSemaphore(&muBase->SegOwnerSem);
}


	/*
	 *		Replacement for the dos.library LoadSeg() function
	 */

BPTR __asm __saveds NEWLoadSeg(register __d1 STRPTR name, register __a6 struct DosLibrary *dosbase)
{
	BPTR fl;
	struct FileInfoBlock *fib;
	ULONG owner = muOWNER_NOBODY;
	BPTR seglist;

	if (name && (fl = Lock(name, ACCESS_READ))) {
		if (CheckmuFSVolume(((struct FileLock *)BADDR(fl))->fl_Task) &&
			 (fib = AllocDosObject(DOS_FIB, NULL))) {
			if (Examine(fl, fib) && (fib->fib_Protection & muFIBF_SET_UID))
				owner = (fib->fib_OwnerUID<<16) | fib->fib_OwnerGID;
			FreeDosObject(DOS_FIB, fib);
		}
		UnLock(fl);
	}
	seglist = muBase->OLDLoadSeg(name, dosbase);
	if (owner & muMASK_UID) {
		ObtainSemaphore(&muBase->SegOwnerSem);
		AddSegNode(seglist, owner);
		ReleaseSemaphore(&muBase->SegOwnerSem);
	}
	return(seglist);
}


	/*
	 *		Replacement for the dos.library NewLoadSeg() function
	 */

BPTR __asm __saveds NEWNewLoadSeg(register __d1 STRPTR name, register __d2 struct TagItem *tags,
											 register __a6 struct DosLibrary *dosbase)
{
	BPTR fl;
	struct FileInfoBlock *fib;
	ULONG owner = muOWNER_NOBODY;
	BPTR seglist;

	if (name && (fl = Lock(name, ACCESS_READ))) {
		if (CheckmuFSVolume(((struct FileLock *)BADDR(fl))->fl_Task) &&
			 (fib = AllocDosObject(DOS_FIB, NULL))) {
			if (Examine(fl, fib) && (fib->fib_Protection & muFIBF_SET_UID))
				owner = (fib->fib_OwnerUID<<16) | fib->fib_OwnerGID;
			FreeDosObject(DOS_FIB, fib);
		}
		UnLock(fl);
	}
	seglist = muBase->OLDNewLoadSeg(name, tags, dosbase);
	if (owner & muMASK_UID) {
		ObtainSemaphore(&muBase->SegOwnerSem);
		AddSegNode(seglist, owner);
		ReleaseSemaphore(&muBase->SegOwnerSem);
	}
	return(seglist);
}


	/*
	 *		Replacement for the dos.library UnLoadSeg() function
	 */

BOOL __asm __saveds NEWUnLoadSeg(register __d1 BPTR seglist, register __a6 struct DosLibrary *dosbase)
{
	struct muSegNode *snode;

	ObtainSemaphoreShared(&muBase->SegOwnerSem);
	if (snode = FindSegNode(seglist))
		RemSegNode(snode);
	ReleaseSemaphore(&muBase->SegOwnerSem);
	return(muBase->OLDUnLoadSeg(seglist, dosbase));
}


	/*
	 *		Replacement for the dos.library InternalLoadSeg() function
	 */

BPTR __asm __saveds NEWInternalLoadSeg(register __d0 BPTR fh, register __a0 BPTR table,
													register __a1 LONG *functionarray, register __a2 LONG *stack,
													register __a6 struct DosLibrary *dosbase)
{
	struct FileInfoBlock *fib;
	ULONG owner = muOWNER_NOBODY;
	BPTR seglist;

	if (fh && CheckmuFSVolume(((struct FileHandle *)BADDR(fh))->fh_Type) &&
		 (fib = AllocDosObject(DOS_FIB, NULL))) {
		if (ExamineFH(fh, fib) && (fib->fib_Protection & muFIBF_SET_UID))
			owner = (fib->fib_OwnerUID<<16) | fib->fib_OwnerGID;
		FreeDosObject(DOS_FIB, fib);
	}
	seglist = muBase->OLDInternalLoadSeg(fh, table, functionarray, stack, dosbase);
	if (owner & muMASK_UID) {
		ObtainSemaphore(&muBase->SegOwnerSem);
		AddSegNode(seglist, owner);
		ReleaseSemaphore(&muBase->SegOwnerSem);
	}
	return(seglist);
}


	/*
	 *		Replacement for the dos.library InternalUnLoadSeg() function
	 */

BOOL __asm __saveds NEWInternalUnLoadSeg(register __d1 BPTR seglist, register __a1 void (*freefunc)(),
													  register __a6 struct DosLibrary *dosbase)
{
	struct muSegNode *snode;

	ObtainSemaphoreShared(&muBase->SegOwnerSem);
	if (snode = FindSegNode(seglist))
		RemSegNode(snode);
	ReleaseSemaphore(&muBase->SegOwnerSem);

	return(muBase->OLDInternalUnLoadSeg(seglist, freefunc, dosbase));
}


	/*
	 *		Replacement for the dos.library CreateProc() function
	 */

struct Process __asm __saveds *NEWCreateProc(register __d1 STRPTR name, register __d2 LONG pri,
															register __d3 BPTR seglist, register __d4 LONG stacksize,
															register __a6 struct DosLibrary *dosbase)
{
	struct muExtOwner *owner;
	struct Process *proc;

	if (owner = GetSegOwner(seglist))
		PushTask(SysBase->ThisTask, owner);
	proc = muBase->OLDCreateProc(name, pri, seglist, stacksize, dosbase);
	if (owner)
		PopTask(SysBase->ThisTask);
	return(proc);
}


	/*
	 *		Replacement for the dos.library CreateNewProc() function
	 */

struct Process __asm __saveds *NEWCreateNewProc(register __d1 struct TagItem *tags,
																register __a6 struct DosLibrary *dosbase)
{
	struct muExtOwner *owner = NULL;
	struct Process *proc;
	BPTR seglist;

	if (tags && (seglist = (BPTR)GetTagData(NP_Seglist, NULL, tags)) && (owner = GetSegOwner(seglist)))
		PushTask(SysBase->ThisTask, owner);
	proc = muBase->OLDCreateNewProc(tags, dosbase);
	if (owner)
		PopTask(SysBase->ThisTask);
	return(proc);
}


	/*
	 *		Replacement for the dos.library RunCommand() function
	 */

LONG __asm __saveds NEWRunCommand(register __d1 BPTR seglist, register __d2 ULONG stacksize,
											 register __d3 STRPTR argptr, register __d4 ULONG argsize,
											 register __a6 struct DosLibrary *dosbase)
{
	struct muExtOwner *owner;
	LONG rc;

	if (owner = GetSegOwner(seglist))
		PushTask(SysBase->ThisTask, owner);
	rc = muBase->OLDRunCommand(seglist, stacksize, argptr, argsize, dosbase);
	if (owner)
		PopTask(SysBase->ThisTask);
	return(rc);
}
