Files
2015-04-16 09:16:48 +02:00

290 lines
6.3 KiB
C

#define _KERNEL
#include "ixemul.h"
#include <sys/ipc.h>
#include <sys/ucred.h>
#include <sys/shm.h>
#include <time.h>
struct shm_list *shmlist = NULL;
static struct shmid_ds *shm_find_ds_by_key(key_t key)
{
struct shm_list *l = shmlist;
while (l && l->ds.shm_perm.key != key)
l = l->next;
return (struct shmid_ds *)l;
}
static struct shmid_ds *shm_find_ds_by_shmid(int shmid)
{
struct shm_list *l = shmlist;
while (l && IPC_SHMID(l->ds.shm_perm) != shmid)
l = l->next;
return (struct shmid_ds *)l;
}
static struct shmid_ds *shm_find_ds_by_addr(void *addr)
{
struct shm_list *l = shmlist;
while (l && l->ds.shm_internal != addr)
l = l->next;
return (struct shmid_ds *)l;
}
static int
shmget_allocate_segment(struct ucred *cred, key_t key, int size, int shmflg)
{
struct shmid_ds *shmseg;
struct shm_list *l;
void *data;
static ushort seq = 0;
usetup;
if (size < 1)
errno_return(EINVAL, -1);
l = kmalloc(sizeof(struct shm_list));
if (l == NULL)
errno_return(ENOSPC, -1);
data = kmalloc(size);
if (data == NULL)
{
kfree(l);
errno_return(ENOSPC, -1);
}
shmseg = &l->ds;
shmseg->shm_perm.key = key;
shmseg->shm_perm.seq = ++seq;
shmseg->shm_internal = data;
shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid;
shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid;
shmseg->shm_perm.mode = (shmflg & ACCESSPERMS) | SHMSEG_ALLOCATED;
shmseg->shm_segsz = size;
shmseg->shm_cpid = getpid();
shmseg->shm_lpid = shmseg->shm_nattch = 0;
shmseg->shm_atime = shmseg->shm_dtime = 0;
shmseg->shm_ctime = time(NULL);
l->next = shmlist;
shmlist = l;
return IPC_SHMID(shmseg->shm_perm);
}
static int _shmget(key_t key, int size, int shmflg)
{
struct shmid_ds *ds = NULL;
struct ucred cred;
usetup;
cred.cr_uid = geteuid();
cred.cr_gid = getegid();
if (key != IPC_PRIVATE)
{
ds = shm_find_ds_by_key(key);
if (!ds && !(shmflg & IPC_CREAT))
errno_return(ENOENT, -1);
if (ds)
{
int error;
if ((error = ipcperm(&cred, &ds->shm_perm, shmflg)) != 0)
errno_return(error, -1);
if (size && size > ds->shm_segsz)
errno_return(EINVAL, -1);
if ((shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL))
errno_return(EEXIST, -1);
return IPC_SHMID(ds->shm_perm);
}
}
return shmget_allocate_segment(&cred, key, size, shmflg);
}
int shmget(key_t key, int size, int shmflg)
{
int result;
ix_lock_base();
result = _shmget(key, size, shmflg);
ix_unlock_base();
return result;
}
static void *_shmat(int shmid, char *shmaddr, int shmflg)
{
struct shmid_ds *ds;
struct ucred cred;
int error;
usetup;
cred.cr_uid = geteuid();
cred.cr_gid = getegid();
ds = shm_find_ds_by_shmid(shmid);
if (ds == NULL)
errno_return(EINVAL, (void *)-1);
if (shmaddr && shmaddr != ds->shm_internal)
errno_return(EINVAL, (void *)-1);
if ((error = ipcperm(&cred, &ds->shm_perm,
(shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W)))
errno_return(error, (void *)-1);
ds->shm_lpid = getpid();
ds->shm_atime = time(NULL);
ds->shm_nattch++;
if (u.u_shmused == u.u_shmsize)
{
u.u_shmsize += 256;
u.u_shmarray = krealloc(u.u_shmarray, 4 * u.u_shmsize);
}
u.u_shmarray[u.u_shmused++] = ds;
return ds->shm_internal;
}
void *shmat(int shmid, void *shmaddr, int shmflg)
{
void *result;
ix_lock_base();
result = _shmat(shmid, shmaddr, shmflg);
ix_unlock_base();
return result;
}
static void shm_deallocate_segment(struct shmid_ds *ds)
{
struct shm_list *l = (struct shm_list *)ds, *p;
kfree(ds->shm_internal);
if (shmlist == l)
{
shmlist = l->next;
}
else
{
for (p = shmlist; p && p->next != l; p = p->next);
if (p)
p->next = l->next;
}
kfree(l);
}
static void shm_delete_mapping(struct shmid_ds *ds)
{
ds->shm_dtime = time(NULL);
if ((--ds->shm_nattch <= 0) && (ds->shm_perm.mode & SHMSEG_REMOVED))
shm_deallocate_segment(ds);
}
static int _shmdt(void *addr)
{
struct shmid_ds *ds;
int i;
usetup;
ds = shm_find_ds_by_addr(addr);
if (ds == NULL)
errno_return(EINVAL, -1);
for (i = 0; i < u.u_shmused && u.u_shmarray[i] != ds; i++) ;
if (i < u.u_shmused)
memcpy(&u.u_shmarray[i], &u.u_shmarray[i + 1], 4 * (--u.u_shmused - i));
shm_delete_mapping(ds);
return 0;
}
int shmdt(void *addr)
{
int result;
ix_lock_base();
result = _shmdt(addr);
ix_unlock_base();
return result;
}
static int _shmctl(int shmid, int cmd, struct shmid_ds *buf)
{
struct shmid_ds *ds;
struct ucred cred;
int error;
usetup;
if (cmd == IPC_GETLIST)
return (int)shmlist;
cred.cr_uid = geteuid();
cred.cr_gid = getegid();
ds = shm_find_ds_by_shmid(shmid);
if (ds == NULL)
errno_return(EINVAL, -1);
switch (cmd)
{
case IPC_STAT:
if ((error = ipcperm(&cred, &ds->shm_perm, IPC_R)) != 0)
errno_return(error, -1);
*buf = *ds;
return 0;
case IPC_SET:
if ((error = ipcperm(&cred, &ds->shm_perm, IPC_M)) != 0)
errno_return(error, -1);
ds->shm_perm.uid = buf->shm_perm.uid;
ds->shm_perm.gid = buf->shm_perm.gid;
ds->shm_perm.mode = (ds->shm_perm.mode & ~ACCESSPERMS) |
(buf->shm_perm.mode & ACCESSPERMS);
ds->shm_ctime = time(NULL);
return 0;
case IPC_RMID:
if ((error = ipcperm(&cred, &ds->shm_perm, IPC_M)) != 0)
errno_return(error, -1);
ds->shm_perm.key = IPC_PRIVATE;
ds->shm_perm.mode |= SHMSEG_REMOVED;
if (ds->shm_nattch <= 0)
shm_deallocate_segment(ds);
return 0;
}
errno_return(EINVAL, -1);
}
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
{
int result;
ix_lock_base();
result = _shmctl(shmid, cmd, buf);
ix_unlock_base();
return result;
}
int shmfork(struct user *parent, struct user *child)
{
if (parent->u_shmused)
{
int i;
child->u_shmarray = kmalloc(parent->u_shmused * 4);
if (child->u_shmarray == NULL)
return -1;
child->u_shmused = child->u_shmsize = parent->u_shmused;
memcpy(child->u_shmarray, parent->u_shmarray, 4 * child->u_shmused);
for (i = 0; i < child->u_shmsize; i++)
((struct shmid_ds *)(child->u_shmarray[i]))->shm_nattch++;
}
return 0;
}
void shmexit(struct user *p)
{
int i;
for (i = 0; i < p->u_shmused; i++)
shm_delete_mapping(p->u_shmarray[i]);
if (p->u_shmsize)
kfree(p->u_shmarray);
}