File:  [MW Coherent from dump] / coherent / b / kernel / io.386 / shm1.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:37 2019 UTC (7 years ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/*
 * File: shm1.c
 *
 * Purpose: System V Compatible Shared Memory Device Driver
 *
 * $Log: shm1.c,v $
 * Revision 1.1.1.1  2019/05/29 04:56:37  root
 * coherent
 *
 * Revision 1.3  93/06/14  13:39:25  bin
 * hal: kernel 78 update
 * 
 * Revision 1.3  93/04/14  10:23:10  root
 * r75
 * 
 */

/*
 * ----------------------------------------------------------------------
 * Includes.
 */
#include <sys/coherent.h>
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>

/*
 * ----------------------------------------------------------------------
 * Definitions.
 *	Constants.
 *	Macros with argument lists.
 *	Typedefs.
 *	Enums.
 */
#define SHMBASE	0x80000000	/* Base shared memory address */
/* These macros should be somewhere in the headers ???*/
#define	DATAST	0x400000	/* Start of the data virtual address */
#define DATAEND	0x7FFFFF	/* End of the data virtual address */
#define STACKST	0x7C000000	/* Start of the stack */
#define	STACKEN	0x7FFFFFFF	/* End of the stack virtual address */

/*
 * ----------------------------------------------------------------------
 * Functions.
 *	Import Functions.
 *	Export Functions.
 *	Local Functions.
 */
extern SEG	*shmAlloc();	/* shm0.c */
char		*ushmat();
int		ushmdt();
int		ushmctl();
int		ushmget();
int		iShmPerm();	/* Check permissions */
caddr_t		vCheckReqAdd();	/* Check attach address for shmat */
/*
 * ----------------------------------------------------------------------
 * Global Data.
 *	Import Variables.
 *	Export Variables.
 *	Local Variables.
 */

/* Patchable global variables. */
int	SHMMNI = 100;	  /* Maximum # of shared memory segments, systemwide */
int 	SHMMAX = 0x10000; /* Max size in bytes of shared memory segment */


struct shmid_ds	*shmids = NULL;		/* Array of shared memory segments */
SEG		**shmsegs;		/* Array of pointers to segments */
/*
 * ----------------------------------------------------------------------
 * Code.
 */

/*
 * Shmctl - Shared Memory Control Operations.
 */
int
ushmctl(iShmId, iCmd, pstShmId)
int 		iShmId,		/* Shared memory id */
		iCmd;		/* Command */
struct shmid_ds	*pstShmId;	/* User shmid_ds buffer */
{
	register struct shmid_ds *rstIdp;	
	int iRet = 0;

	/* Check if id is in proper range. */
	if (iShmId >= SHMMNI || iShmId < 0) {
		u.u_error = EINVAL;
		return;
	}
	/* Check we did alloc. All allocatable arrays are alloced after
	 * the first ~correct usage of shmget.
	 */
	if (shmids == NULL) {
		u.u_error = EINVAL;
		return;
	}
	rstIdp = &shmids[iShmId];	/* Requested segment */

	/* Check if segment is in used */
	if ((rstIdp->shm_perm.mode & IPC_ALLOC) == 0) {
		u.u_error = EINVAL;
		return;
	}

	switch (iCmd) {
	case IPC_STAT:
		/* Check read permission for stat. */
		if (iShmPerm((rstIdp), SHM_R)) {
			u.u_error = EACCES;
			return;
		}
		/* Check if user gives a valid buffer */
		if (!useracc(pstShmId, sizeof(struct shmid_ds), 1)) {
			u.u_error = EFAULT;
			return;
		}
		/* kucopy will set u_error if error occurs */
		kucopy(rstIdp, pstShmId, sizeof(struct shmid_ds));
		break;

	case IPC_SET:
		if ((u.u_uid != 0) && (u.u_uid != rstIdp->shm_perm.uid) &&
				(u.u_uid != rstIdp->shm_perm.cuid)) {
			u.u_error = EPERM;
			iRet = -1;
			break;
		}
		rstIdp->shm_perm.uid   = getuwd(&(pstShmId->shm_perm.uid));
		rstIdp->shm_perm.gid   = getuwd(&(pstShmId->shm_perm.gid));
		rstIdp->shm_perm.mode &= ~0777;
		rstIdp->shm_perm.mode |= getuwd(&(pstShmId->shm_perm.mode)) 
									& 0777;
		break;

	case IPC_RMID:
		if ((u.u_uid != 0) && (u.u_uid != rstIdp->shm_perm.uid) &&
				(u.u_uid != rstIdp->shm_perm.cuid)) {
			u.u_error = EPERM;
			iRet = -1;
			break;
		}
		
		/* SVR3 allows removing an attached segment. Even worse, the
		 * process that has the segment attached can keep using it. 
		 * Some buggy third party software uses this "feature". 
		 * So, we have to make it available too;-(
		 */
		rstIdp->shm_perm.seq = 0;
		rstIdp->shm_perm.mode = 0;

		/* If segment is attached, set flag to be removed */
		if (rstIdp->shm_nattch > 0)
			shmsegs[iShmId]->s_flags |= SRFBERM;
		else	/* remove it otherwise */
			shmFree(shmsegs[iShmId]);
		shmsegs[iShmId] = NULL;
		break;

	/* SHM_LOCK and SHM_UNLOCK: lock/unlock shared memory segement 
	 * in core. Is not a part of iBCS2.
	 * Has no meaning for current 4.0.* release of COHERENT.
	 * Have been done for binary portability.
	 */
	case SHM_LOCK:	
		if (!u.u_uid) {
			u.u_error = EPERM;
			return;
		}
		break;
	case SHM_UNLOCK:	
		if (!u.u_uid) {
			u.u_error = EPERM;
			return;
		}
		break;
	
	default:
		u.u_error = EINVAL;
		iRet = -1;
	}

	return iRet;
}

/*
 * Shmget - Get Shared Memory Segment
 * Return shared memory id if succed, -1 and set u_error otheriwse.
 */
int
ushmget(kShmKey, iShmSize, iShmFlg)
key_t	kShmKey;	/* Shared memory key */
int	iShmSize;	/* Shared memory segment size */
int	iShmFlg;	/* Flags */
{
	register struct shmid_ds *rstShmId;	/* Work pointer */
	register struct shmid_ds *rstOldest = 0;/* Oldest free segment */
	register int		 i;		/* Loop index */
	SEG			 *pstSeg;

	/* Check the requested segment size */
	if (iShmSize < 0 || iShmSize > SHMMAX) {
		u.u_error = EINVAL;
		return;
	}
	/* Init the shared memory on the first shmget. */
	if (shmids == NULL) 
		if (shminit()) {
			u.u_error = ENOSPC;
			return;
		}

	/* Search for desire shared memory segment. */
	for (rstShmId = shmids, i = 0; i < SHMMNI; i++, rstShmId++) {
		/* If segment is not alloced, we will look for the oldest
		 * free segment. We will use it to create a new one.
		 * The "oldest" will increase (a little) system reliability.
		 */
		if ((rstShmId->shm_perm.mode & IPC_ALLOC) == 0) {
			if ((rstOldest == NULL) || 
			   (rstOldest->shm_ctime > rstShmId->shm_ctime))
				rstOldest = rstShmId;
			continue;
		}
		/* Do we need a new segment? */
		if (kShmKey == IPC_PRIVATE)
			continue;
		/* Keep going if key is different. The key is an element
		 * number of shmids
		 */
		if (kShmKey != rstShmId->shm_perm.key)
			continue;

		/* We found the segment with requested key. */
		
		/* Request was for the exclusive segment should fail. */
		if ((iShmFlg & IPC_CREAT) && (iShmFlg & IPC_EXCL)) {
			u.u_error = EEXIST;
			return;
		}

		/* Check the requested size */
		if (rstShmId->shm_segsz < iShmSize) {
			u.u_error = EINVAL;
			return;
		}

		/* Check permissions */
		if (iShmPerm(rstShmId, iShmFlg))
			return;
		return i;
	}

	/* We need to create a new segment */
	if (rstOldest == 0) { /* Check system limits */
		u.u_error = ENOSPC;
		return;
	}
	if (!(iShmFlg & IPC_CREAT)) {
		u.u_error = ENOENT;
		return;
	}
	rstShmId = rstOldest;
	/* Allocate a new shared memory segment */
	pstSeg = shmAlloc(iShmSize);
	if (pstSeg == NULL) {
		u.u_error = ENOSPC;
		return;
	}
	/* Save it in shmsegs */
	shmsegs[rstShmId - shmids] = pstSeg;

	rstShmId->shm_perm.seq = (unsigned short) (rstShmId - shmids);
	rstShmId->shm_segsz = iShmSize;
	rstShmId->shm_atime = 0;
	rstShmId->shm_dtime = 0;
	rstShmId->shm_ctime = timer.t_time;
	rstShmId->shm_cpid  = SELF->p_pid;
	rstShmId->shm_perm.cuid = rstShmId->shm_perm.uid = u.u_uid;
	rstShmId->shm_perm.cgid = rstShmId->shm_perm.gid = u.u_gid;
	rstShmId->shm_perm.mode = (iShmFlg & 0777) | IPC_ALLOC;
	rstShmId->shm_perm.key  = kShmKey;

	if (kShmKey == IPC_PRIVATE)
		rstShmId->shm_perm.mode |= SHM_DEST;

	return rstShmId - shmids;
}

/*
 * Allocate space for shared memory data structures.
 */
int
shminit()
{
	shmids = (struct shmid_ds *) kalloc(sizeof(struct shmid_ds) * SHMMNI);
	if (shmids == NULL)
		return -1;

	/* Allocate array of shared memory segments. We do not have to
	 * initalise it
	 */
	shmsegs = (SEG *) kalloc(sizeof(SEG *) * SHMMNI);
	if (shmsegs == NULL)
		return -1;
	return 0;
}

/*
 * Attach shared memory segment.
 */
char *
ushmat(iSysId, pcShmAddr, iShmFlg)
int	iSysId;		/* System segment id */
char	*pcShmAddr;	/* Address to attach */
int	iShmFlg;	/* Flags */
{
	register PROC		*rpstProc;	/* Current process */
	register struct sr	*rpstSr;	/* Shared memory segments */
	struct sr		*pstSrTmp;
	SEG			*pstSegSh;	/* Segment to attach */
	struct shmid_ds		*pstShmId;	/* Pointer to a system segment*/
	unsigned int		uSegId;		/* Segment id */
	caddr_t			vAttAddr;	/* Address to attach */
	int			i;		/* Loop index */
	int			iReadOnly = 0;	/* 1 - read only, 0 - rw */
		
	/* Check if iSysId is a valid shared memory id. */
	if (iSysId < 0 || iSysId > SHMMNI) {
		u.u_error = EINVAL;
		return;
	}
	/* Do we really have this segment? */
	pstShmId = shmids + iSysId;
	if (pstShmId->shm_perm.seq != iSysId) {
		u.u_error = EINVAL;
		return;
	}
	/* Check permissions. */
	if (iShmFlg & SHM_RDONLY)
		iReadOnly = 1;
	if (iReadOnly) {
		if (iShmPerm(pstShmId, 0444)) {
			u.u_error = EACCES;
			return;
		}
	} else {
		if (iShmPerm(pstShmId, 0666)) {
			u.u_error = EACCES;
			return;
		}
	}
	/* Check if process has free shm index. */
	rpstProc = SELF;
	/* We will go through all NSHMSEG segments to see if any is free. */
	for (rpstSr = rpstProc->p_shmsr; rpstSr < rpstProc->p_shmsr + NSHMSEG; 
								rpstSr++)
		if (rpstSr->sr_segp == NULL)
			break;
	/* The segment id is just an element number. */
	uSegId = rpstSr - rpstProc->p_shmsr;
	/* If segment id is >= NSHMSEG we cannot attach any new segment. */
	if (uSegId >= NSHMSEG) {
		u.u_error = EMFILE;
		return;
	}
	/* Get the pointer to the shared memory segment */
	pstSegSh = shmsegs[iSysId];

	/* Find an address to attach.
	 * There are two cases: process does not request the address,
	 * process requests the address.
	 * In the first case we have to take the first available free address.
	 * We will try to put a free page between the segments.
	 * In the second case we have to check if address is an available and
	 * attach to this address.
	 */
	/* First case. We have to find a free address. */
	if (pcShmAddr == NULL) {
		/* Find a free space to attach.  */
		for (pstSrTmp = rpstProc->p_shmsr, i = 0; i < NSHMSEG; 
							i++, pstSrTmp++) 
			if (pstSrTmp->sr_base == NULL)
				break;
		/* Check limit of attaches per process */
		if (i >= NSHMSEG) {
			u.u_error = EINVAL;
			return;
		}
		/* We will use the addresses starting from SHMBASE. 
		 * Each new address can be SHMMAX + NBPC appart.
		 */
		vAttAddr = (caddr_t) (SHMBASE + i * (SHMMAX + NBPC));
	} else {
		/* Requst attach to a specific address. This is none portable 
		 * way to use a shared memory. 
	 	 */
		if ((vAttAddr = vCheckReqAdd(pcShmAddr, iReadOnly)) < 0) {
			printf("%s: attempt attach to 0x%x\n", 
			u.u_comm, pcShmAddr);
			u.u_error = EINVAL;
			return;
		} 
	}

	if (!shmAtt(uSegId, vAttAddr, pstSegSh, iReadOnly)) {
		u.u_error = EINVAL;
		return;
	}
	pstShmId->shm_lpid = SELF->p_pid;
	pstShmId->shm_atime = timer.t_time;
	pstShmId->shm_dtime = 0;
	pstShmId->shm_nattch = pstSegSh->s_urefc - 1;
	/* Keep all attached addresses. We will need them for detach */
	return (char *) vAttAddr;
}

/*
 * Check requested address for attach.
 * Just fail for the first release.
 */
caddr_t
vCheckReqAdd(pcAdd, iFlg)
char	*pcAdd;	/* Address to atatch */
int	iFlg;	/* Mode flag */
{
	return (caddr_t) -1;
}

/*
 * Check permissions of the shared memory segment.
 * Return 0 on success, -1 and set errno on error.
 */
int
iShmPerm(pstShmId, iShmFlg)
struct shmid_ds	*pstShmId;
int		iShmFlg;
{
	int	iShmMode;

	/* We need 9 lower order bits. There is a question what we have to do
	 * if someone sets an execute bits on. At this point we just ignore 
	 * them.
	 */
	iShmMode = iShmFlg & 0666;

	/* For superuser or if mode is 0 */
	if (u.u_uid == 0 || !iShmMode) 
		return 0;
	/* For owner or creator */
	if (u.u_uid == pstShmId->shm_perm.uid || u.u_uid 
						== pstShmId->shm_perm.cuid) {
		if ((iShmMode & pstShmId->shm_perm.mode) & 0600)
			return 0;
		else {
			u.u_error = EACCES;
			return -1;
		}
	}
	/* For group */		
	if (u.u_gid == pstShmId->shm_perm.gid 
					|| u.u_gid == pstShmId->shm_perm.cgid) {
		if ((iShmMode & pstShmId->shm_perm.mode) & 060)
			return 0;
		else {
			u.u_error = EACCES;
			return -1;
		}
	}
	/* For the rest of the world */
	if ((iShmMode & pstShmId->shm_perm.mode) & 06) 
		return 0;
	else {
		u.u_error = EACCES;
		return -1;
	}
	/* We should never come here */
	u.u_error = EACCES;
	return -1;
}

/*
 * ushmdt() - Detach shared memory segment.
 * Find segment number and call shmDetach() (shm0.c).
 */
int
ushmdt(cpShmAddr)
char	*cpShmAddr;	/* Pointer to a segment */
{
	register PROC		*rpstProc;	/* Current process */
	register struct sr	*rpstSr;	/* Shared memory segments */
	int			i;		/* Loop indexe */

	rpstProc = SELF;	/* Get pointer to our process */

	/* Go through all segments. */
	for (rpstSr = rpstProc->p_shmsr, i = 0; i < NSHMSEG; i++, rpstSr++) {
		if (rpstSr->sr_base == (caddr_t) cpShmAddr) {
 			shmDetach(i);
                        return 0;
		}
	}

	/* We can come here only if we have invalid address */
	u.u_error = EINVAL;
	return;
}

/*
 * shmSetDs(). Called from shm0.c.
 *
 * Given a pointer to shared memory segment, set shmid_ds.
 */
void
shmSetDs(rpstSeg)
register SEG	*rpstSeg;
{
	struct shmid_ds		*pstShmId;	/* Shared memory structure */
	int			iShmId;		/* Shared memory id */
	int			j;		/* Loop indexe */

	for (j = 0; j < SHMMNI; j++)
		if (shmsegs[j] == rpstSeg)
			break;

	/* We should have this segment. */
	if (j >= SHMMNI) {
		u.u_error = EINVAL;
		return;
	}

	iShmId = j;
	pstShmId = shmids + iShmId;

	/* Set proper values */
	pstShmId->shm_lpid = SELF->p_pid;
	pstShmId->shm_dtime = timer.t_time;
	pstShmId->shm_nattch = rpstSeg->s_urefc - 1;
	return;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.