#define _KERNEL #include "ixemul.h" #include #include #include #include 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); }