IPC server: NetBSD sync, general improvements

- switch to the NetBSD identifier system; it is not only better, but
  also required for porting NetBSD ipcs(1) and ipcrm(1); however, it
  requires that slots not be moved, and that results in some changes;
- synchronize some other things with NetBSD: where keys are kept, as
  well as various non-permission mode flags;
- fix semctl(2) vararg retrieval and message field type;
- use SUSPEND instead of weird reply exceptions in the call table;
- fix several memory leaks and at least one missing permission check;
- improve the atomicity of semop(2) by a small amount, even though
  its atomicity is still broken at a fundamental level;
- use the new cheaper way to retrieve the current time;
- resolve all level-5 LLVM warnings.

Change-Id: I0c47aacde478b23bb77d628384aeab855a22fdbf
This commit is contained in:
David van Moolenbroek 2015-11-24 23:01:22 +00:00 committed by Lionel Sambuc
parent 58c1923c9f
commit 4d272e5a97
10 changed files with 353 additions and 285 deletions

View File

@ -365,7 +365,7 @@ typedef struct {
int id;
int num;
int cmd;
int opt;
vir_bytes opt;
int ret;
uint8_t padding[36];
} mess_lc_ipc_semctl;

View File

@ -1,4 +1,3 @@
#define __USE_MISC
#define _SYSTEM 1
#define _MINIX_SYSTEM 1
@ -64,10 +63,23 @@ int semctl(int semid, int semnum, int cmd, ...)
m.m_lc_ipc_semctl.num = semnum;
m.m_lc_ipc_semctl.cmd = cmd;
va_start(ap, cmd);
if (cmd == IPC_STAT || cmd == IPC_SET || cmd == IPC_INFO ||
cmd == SEM_INFO || cmd == SEM_STAT || cmd == GETALL ||
cmd == SETALL || cmd == SETVAL)
m.m_lc_ipc_semctl.opt = (long) va_arg(ap, long);
switch (cmd) {
case SETVAL:
m.m_lc_ipc_semctl.opt = (vir_bytes)va_arg(ap, int);
break;
case IPC_STAT:
case IPC_SET:
case IPC_INFO:
case SEM_INFO:
case SEM_STAT:
case GETALL:
case SETALL:
m.m_lc_ipc_semctl.opt = (vir_bytes)va_arg(ap, void *);
break;
default:
m.m_lc_ipc_semctl.opt = 0;
break;
}
va_end(ap);
r = _syscall(ipc_pt, IPC_SEMCTL, &m);

View File

@ -9,4 +9,6 @@ FILES=ipc.conf
FILESNAME=ipc
FILESDIR= /etc/system.conf.d
WARNS?= 5
.include <minix.service.mk>

View File

@ -14,6 +14,7 @@
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <machine/param.h>
#include <machine/vm.h>
@ -27,6 +28,15 @@
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <assert.h>
/*
* On NetBSD, these macros are only defined when _KERNEL is set. However,
* since ipcs(1) uses IXSEQ_TO_IPCID, NetBSD cannot change these macros without
* breaking the userland API. Thus, having a copy of them here is not risky.
*/
#define IPCID_TO_IX(id) ((id) & 0xffff)
#define IPCID_TO_SEQ(id) (((id) >> 16) & 0xffff)
int do_shmget(message *);
int do_shmat(message *);
@ -41,6 +51,4 @@ int is_sem_nil(void);
int is_shm_nil(void);
void sem_process_vm_notify(void);
EXTERN int identifier;
EXTERN endpoint_t who_e;
EXTERN int call_type;

View File

@ -1,25 +1,19 @@
#include "inc.h"
int identifier = 0x1234;
endpoint_t who_e;
int call_type;
static unsigned int call_type;
static struct {
int type;
int (*func)(message *);
int reply; /* whether the reply action is passed through */
} ipc_calls[] = {
{ IPC_SHMGET, do_shmget, 0 },
{ IPC_SHMAT, do_shmat, 0 },
{ IPC_SHMDT, do_shmdt, 0 },
{ IPC_SHMCTL, do_shmctl, 0 },
{ IPC_SEMGET, do_semget, 0 },
{ IPC_SEMCTL, do_semctl, 0 },
{ IPC_SEMOP, do_semop, 1 },
#define CALL(n) [((n) - IPC_BASE)]
static int (* const call_vec[])(message *) = {
CALL(IPC_SHMGET) = do_shmget,
CALL(IPC_SHMAT) = do_shmat,
CALL(IPC_SHMDT) = do_shmdt,
CALL(IPC_SHMCTL) = do_shmctl,
CALL(IPC_SEMGET) = do_semget,
CALL(IPC_SEMCTL) = do_semctl,
CALL(IPC_SEMOP) = do_semop,
};
#define SIZE(a) (sizeof(a)/sizeof(a[0]))
static int verbose = 0;
/* SEF functions and variables. */
@ -30,15 +24,14 @@ static void sef_cb_signal_handler(int signo);
int main(int argc, char *argv[])
{
message m;
unsigned int ipc_number;
int r;
/* SEF local startup. */
env_setargs(argc, argv);
sef_local_startup();
while (TRUE) {
int r;
int ipc_number;
if ((r = sef_receive(ANY, &m)) != OK)
printf("sef_receive failed %d.\n", r);
who_e = m.m_source;
@ -61,22 +54,11 @@ int main(int argc, char *argv[])
continue;
}
/*
* The ipc number in the table can be obtained
* with a simple equation because the values of
* IPC system calls are consecutive and begin
* at ( IPC_BASE + 1 )
*/
ipc_number = call_type - (IPC_BASE + 1);
ipc_number = (unsigned int)(call_type - IPC_BASE);
/* dispatch message */
if (ipc_number >= 0 && ipc_number < SIZE(ipc_calls)) {
int result;
if (ipc_calls[ipc_number].type != call_type)
panic("IPC: call table order mismatch");
if (ipc_number < __arraycount(call_vec) &&
call_vec[ipc_number] != NULL) {
/* If any process does an IPC call,
* we have to know about it exiting.
* Tell VM to watch it for us.
@ -85,19 +67,18 @@ int main(int argc, char *argv[])
printf("IPC: watch failed on %d\n", m.m_source);
}
result = ipc_calls[ipc_number].func(&m);
r = call_vec[ipc_number](&m);
/*
* The handler of the IPC call did not
* post a reply.
*/
if (!ipc_calls[ipc_number].reply) {
if (r != SUSPEND) {
m.m_type = r;
m.m_type = result;
if(verbose && result != OK)
if(verbose && r != OK)
printf("IPC: error for %d: %d\n",
call_type, result);
call_type, r);
if ((r = ipc_sendnb(who_e, &m)) != OK)
printf("IPC send error %d.\n", r);
@ -117,7 +98,7 @@ int main(int argc, char *argv[])
/*===========================================================================*
* sef_local_startup *
*===========================================================================*/
static void sef_local_startup()
static void sef_local_startup(void)
{
/* Register init callbacks. */
sef_setcb_init_fresh(sef_cb_init_fresh);
@ -152,7 +133,8 @@ static void sef_cb_signal_handler(int signo)
if (signo != SIGTERM) return;
/* Checkout if there are still IPC keys. Inform the user in that case. */
if (!is_sem_nil() || !is_shm_nil())
printf("IPC: exit with un-clean states.\n");
}
if (is_sem_nil() && is_shm_nil())
sef_exit(0);
printf("IPC: exit with un-clean states.\n");
}

View File

@ -1,7 +1,3 @@
#define __USE_MISC
#include <minix/vm.h>
#include "inc.h"
struct waiting {
@ -19,33 +15,43 @@ struct semaphore {
};
struct sem_struct {
key_t key;
int id;
struct semid_ds semid_ds;
struct semaphore sems[SEMMSL];
};
static struct sem_struct sem_list[SEMMNI];
static int sem_list_nr = 0;
static unsigned int sem_list_nr = 0; /* highest in-use slot number plus one */
static struct sem_struct *sem_find_key(key_t key)
{
int i;
unsigned int i;
if (key == IPC_PRIVATE)
return NULL;
for (i = 0; i < sem_list_nr; i++)
if (sem_list[i].key == key)
return sem_list+i;
for (i = 0; i < sem_list_nr; i++) {
if (!(sem_list[i].semid_ds.sem_perm.mode & SEM_ALLOC))
continue;
if (sem_list[i].semid_ds.sem_perm._key == key)
return &sem_list[i];
}
return NULL;
}
static struct sem_struct *sem_find_id(int id)
{
int i;
for (i = 0; i < sem_list_nr; i++)
if (sem_list[i].id == id)
return sem_list+i;
return NULL;
struct sem_struct *sem;
unsigned int i;
i = IPCID_TO_IX(id);
if (i >= sem_list_nr)
return NULL;
sem = &sem_list[i];
if (!(sem->semid_ds.sem_perm.mode & SEM_ALLOC))
return NULL;
if (sem->semid_ds.sem_perm._seq != IPCID_TO_SEQ(id))
return NULL;
return sem;
}
/*===========================================================================*
@ -53,9 +59,10 @@ static struct sem_struct *sem_find_id(int id)
*===========================================================================*/
int do_semget(message *m)
{
key_t key;
int nsems, flag, id;
struct sem_struct *sem;
unsigned int i, seq;
key_t key;
int nsems, flag;
key = m->m_lc_ipc_semget.key;
nsems = m->m_lc_ipc_semget.nr;
@ -68,33 +75,41 @@ int do_semget(message *m)
return EACCES;
if (nsems > sem->semid_ds.sem_nsems)
return EINVAL;
id = sem->id;
i = sem - sem_list;
} else {
if (!(flag & IPC_CREAT))
return ENOENT;
if (nsems < 0 || nsems >= SEMMSL)
return EINVAL;
if (sem_list_nr == SEMMNI)
/* Find a free entry. */
for (i = 0; i < __arraycount(sem_list); i++)
if (!(sem_list[i].semid_ds.sem_perm.mode & SEM_ALLOC))
break;
if (i == __arraycount(sem_list))
return ENOSPC;
/* create a new semaphore set */
sem = &sem_list[sem_list_nr];
/* Initialize the entry. */
sem = &sem_list[i];
seq = sem->semid_ds.sem_perm._seq;
memset(sem, 0, sizeof(struct sem_struct));
sem->semid_ds.sem_perm._key = key;
sem->semid_ds.sem_perm.cuid =
sem->semid_ds.sem_perm.uid = getnuid(who_e);
sem->semid_ds.sem_perm.cgid =
sem->semid_ds.sem_perm.gid = getngid(who_e);
sem->semid_ds.sem_perm.mode = flag & 0777;
sem->semid_ds.sem_perm.mode = SEM_ALLOC | (flag & ACCESSPERMS);
sem->semid_ds.sem_perm._seq = (seq + 1) & 0x7fff;
sem->semid_ds.sem_nsems = nsems;
sem->semid_ds.sem_otime = 0;
sem->semid_ds.sem_ctime = time(NULL);
sem->id = id = identifier++;
sem->key = key;
sem->semid_ds.sem_ctime = clock_time(NULL);
sem_list_nr++;
assert(i <= sem_list_nr);
if (i == sem_list_nr)
sem_list_nr++;
}
m->m_lc_ipc_semget.retid = id;
m->m_lc_ipc_semget.retid = IXSEQ_TO_IPCID(i, sem->semid_ds.sem_perm);
return OK;
}
@ -119,22 +134,29 @@ static void remove_semaphore(struct sem_struct *sem)
free(sem->sems[i].nlist);
}
for (i = 0; i < sem_list_nr; i++) {
if (&sem_list[i] == sem)
break;
}
/* Mark the entry as free. */
sem->semid_ds.sem_perm.mode &= ~SEM_ALLOC;
if (i < sem_list_nr && --sem_list_nr != i)
sem_list[i] = sem_list[sem_list_nr];
/*
* This may have been the last in-use slot in the list. Ensure that
* sem_list_nr again equals the highest in-use slot number plus one.
*/
while (sem_list_nr > 0 &&
!(sem_list[sem_list_nr - 1].semid_ds.sem_perm.mode & SEM_ALLOC))
sem_list_nr--;
}
#if 0
static void show_semaphore(void)
{
int i, j, k;
unsigned int i;
int j, k, nr;
for (i = 0; i < sem_list_nr; i++) {
int nr = sem_list[i].semid_ds.sem_nsems;
if (!(sem_list[i].semid_ds.sem_perm.mode & SEM_ALLOC))
continue;
nr = sem_list[i].semid_ds.sem_nsems;
printf("===== [%d] =====\n", i);
for (j = 0; j < nr; j++) {
@ -166,13 +188,16 @@ static void show_semaphore(void)
static void remove_process(endpoint_t pt)
{
int i;
struct sem_struct *sem;
unsigned int i;
int j, nr;
for (i = 0; i < sem_list_nr; i++) {
struct sem_struct *sem = &sem_list[i];
int nr = sem->semid_ds.sem_nsems;
int j;
sem = &sem_list[i];
if (!(sem->semid_ds.sem_perm.mode & SEM_ALLOC))
continue;
nr = sem->semid_ds.sem_nsems;
for (j = 0; j < nr; j++) {
struct semaphore *semaphore = &sem->sems[j];
int k;
@ -268,10 +293,13 @@ static void update_one_semaphore(struct sem_struct *sem, int is_remove)
static void update_semaphores(void)
{
int i;
unsigned int i;
for (i = 0; i < sem_list_nr; i++)
update_one_semaphore(sem_list+i, 0 /* not remove */);
for (i = 0; i < sem_list_nr; i++) {
if (!(sem_list[i].semid_ds.sem_perm.mode & SEM_ALLOC))
continue;
update_one_semaphore(&sem_list[i], FALSE /*is_remove*/);
}
}
/*===========================================================================*
@ -279,10 +307,10 @@ static void update_semaphores(void)
*===========================================================================*/
int do_semctl(message *m)
{
int r, i;
long opt = 0;
unsigned int i;
vir_bytes opt;
uid_t uid;
int id, num, cmd, val;
int r, id, num, cmd, val;
unsigned short *buf;
struct semid_ds *ds, tmp_ds;
struct sem_struct *sem;
@ -291,11 +319,7 @@ int do_semctl(message *m)
id = m->m_lc_ipc_semctl.id;
num = m->m_lc_ipc_semctl.num;
cmd = m->m_lc_ipc_semctl.cmd;
if (cmd == IPC_STAT || cmd == IPC_SET || cmd == IPC_INFO ||
cmd == SEM_INFO || cmd == SEM_STAT || cmd == GETALL ||
cmd == SETALL || cmd == SETVAL)
opt = m->m_lc_ipc_semctl.opt;
opt = m->m_lc_ipc_semctl.opt;
switch (cmd) {
case IPC_INFO:
@ -303,13 +327,16 @@ int do_semctl(message *m)
sem = NULL;
break;
case SEM_STAT:
if (id < 0 || id >= sem_list_nr)
if (id < 0 || (unsigned int)id >= sem_list_nr)
return EINVAL;
sem = &sem_list[id];
if (!(sem->semid_ds.sem_perm.mode & SEM_ALLOC))
return EINVAL;
break;
default:
if (!(sem = sem_find_id(id)))
return EINVAL;
break;
}
/* IPC_SET and IPC_RMID as its own permission check */
@ -325,7 +352,9 @@ int do_semctl(message *m)
if ((r = sys_datacopy(SELF, (vir_bytes)&sem->semid_ds, who_e,
(vir_bytes)opt, sizeof(sem->semid_ds))) != OK)
return r;
m->m_lc_ipc_semctl.ret = sem->id;
if (cmd == SEM_STAT)
m->m_lc_ipc_semctl.ret =
IXSEQ_TO_IPCID(id, sem->semid_ds.sem_perm);
break;
case IPC_SET:
uid = getnuid(who_e);
@ -334,15 +363,15 @@ int do_semctl(message *m)
uid != 0)
return EPERM;
ds = (struct semid_ds *) opt;
r = sys_datacopy(who_e, (vir_bytes) ds,
SELF, (vir_bytes) &tmp_ds, sizeof(struct semid_ds));
if (r != OK)
return EINVAL;
if ((r = sys_datacopy(who_e, (vir_bytes)ds, SELF,
(vir_bytes)&tmp_ds, sizeof(struct semid_ds))) != OK)
return r;
sem->semid_ds.sem_perm.uid = tmp_ds.sem_perm.uid;
sem->semid_ds.sem_perm.gid = tmp_ds.sem_perm.gid;
sem->semid_ds.sem_perm.mode &= ~0777;
sem->semid_ds.sem_perm.mode |= tmp_ds.sem_perm.mode & 0666;
sem->semid_ds.sem_ctime = time(NULL);
sem->semid_ds.sem_perm.mode &= ~ACCESSPERMS;
sem->semid_ds.sem_perm.mode |=
tmp_ds.sem_perm.mode & ACCESSPERMS;
sem->semid_ds.sem_ctime = clock_time(NULL);
break;
case IPC_RMID:
uid = getnuid(who_e);
@ -353,7 +382,7 @@ int do_semctl(message *m)
/* awaken all processes block in semop
* and remove the semaphore set.
*/
update_one_semaphore(sem, 1);
update_one_semaphore(sem, TRUE /*is_remove*/);
break;
case IPC_INFO:
case SEM_INFO:
@ -395,16 +424,15 @@ int do_semctl(message *m)
break;
case GETALL:
buf = malloc(sizeof(unsigned short) * sem->semid_ds.sem_nsems);
if (!buf)
if (buf == NULL)
return ENOMEM;
for (i = 0; i < sem->semid_ds.sem_nsems; i++)
buf[i] = sem->sems[i].semval;
r = sys_datacopy(SELF, (vir_bytes) buf,
who_e, (vir_bytes) opt,
sizeof(unsigned short) * sem->semid_ds.sem_nsems);
r = sys_datacopy(SELF, (vir_bytes)buf, who_e, (vir_bytes)opt,
sizeof(unsigned short) * sem->semid_ds.sem_nsems);
free(buf);
if (r != OK)
return EINVAL;
free(buf);
break;
case GETNCNT:
if (num < 0 || num >= sem->semid_ds.sem_nsems)
@ -428,13 +456,14 @@ int do_semctl(message *m)
break;
case SETALL:
buf = malloc(sizeof(unsigned short) * sem->semid_ds.sem_nsems);
if (!buf)
if (buf == NULL)
return ENOMEM;
r = sys_datacopy(who_e, (vir_bytes) opt,
SELF, (vir_bytes) buf,
sizeof(unsigned short) * sem->semid_ds.sem_nsems);
if (r != OK)
r = sys_datacopy(who_e, (vir_bytes)opt, SELF, (vir_bytes)buf,
sizeof(unsigned short) * sem->semid_ds.sem_nsems);
if (r != OK) {
free(buf);
return EINVAL;
}
#ifdef DEBUG_SEM
printf("SEMCTL: SETALL: opt: %lu\n", (vir_bytes) opt);
for (i = 0; i < sem->semid_ds.sem_nsems; i++)
@ -465,7 +494,7 @@ int do_semctl(message *m)
#ifdef DEBUG_SEM
printf("SEMCTL: SETVAL: %d %d\n", num, val);
#endif
sem->semid_ds.sem_ctime = time(NULL);
sem->semid_ds.sem_ctime = clock_time(NULL);
/* awaken if possible */
update_semaphores();
break;
@ -481,7 +510,8 @@ int do_semctl(message *m)
*===========================================================================*/
int do_semop(message *m)
{
int id, i, j, r;
unsigned int i, j, mask;
int id, r;
struct sembuf *sops;
unsigned int nsops;
struct sem_struct *sem;
@ -490,34 +520,23 @@ int do_semop(message *m)
id = m->m_lc_ipc_semop.id;
nsops = m->m_lc_ipc_semop.size;
r = EINVAL;
if (!(sem = sem_find_id(id)))
goto out;
return EINVAL;
if (nsops <= 0)
goto out;
r = E2BIG;
return EINVAL;
if (nsops > SEMOPM)
goto out;
/* check for read permission */
r = EACCES;
if (!check_perm(&sem->semid_ds.sem_perm, who_e, 0444))
goto out;
return E2BIG;
/* get the array from user application */
r = ENOMEM;
sops = malloc(sizeof(struct sembuf) * nsops);
if (!sops)
goto out_free;
return ENOMEM;
r = sys_datacopy(who_e, (vir_bytes) m->m_lc_ipc_semop.ops,
SELF, (vir_bytes) sops,
sizeof(struct sembuf) * nsops);
if (r != OK) {
r = EINVAL;
if (r != OK)
goto out_free;
}
#ifdef DEBUG_SEM
for (i = 0; i < nsops; i++)
@ -530,6 +549,18 @@ int do_semop(message *m)
if (sops[i].sem_num >= sem->semid_ds.sem_nsems)
goto out_free;
/* check for permissions */
r = EACCES;
mask = 0;
for (i = 0; i < nsops; i++) {
if (sops[i].sem_op != 0)
mask |= 0222; /* check for write permission */
else
mask |= 0444; /* check for read permission */
}
if (mask && !check_perm(&sem->semid_ds.sem_perm, who_e, mask))
goto out_free;
/* check for duplicate number */
r = EINVAL;
for (i = 0; i < nsops; i++)
@ -537,7 +568,7 @@ int do_semop(message *m)
if (sops[i].sem_num == sops[j].sem_num)
goto out_free;
/* check for EAGAIN error */
/* check for nonblocking operations */
r = EAGAIN;
for (i = 0; i < nsops; i++) {
int op_n, val;
@ -550,8 +581,8 @@ int do_semop(message *m)
(op_n < 0 &&
-op_n > val)))
goto out_free;
}
/* there will be no errors left, so we can go ahead */
for (i = 0; i < nsops; i++) {
struct semaphore *s;
@ -563,20 +594,16 @@ int do_semop(message *m)
s->sempid = getnpid(who_e);
if (op_n > 0) {
/* check for alter permission */
r = EACCES;
if (!check_perm(&sem->semid_ds.sem_perm, who_e, 0222))
goto out_free;
s->semval += sops[i].sem_op;
} else if (!op_n) {
if (s->semval) {
/* put the process asleep */
s->semzcnt++;
s->zlist = realloc(s->zlist, sizeof(struct waiting) * s->semzcnt);
if (!s->zlist) {
printf("IPC: zero waiting list lost...\n");
break;
}
s->zlist = realloc(s->zlist,
sizeof(struct waiting) * s->semzcnt);
/* continuing if NULL would lead to disaster */
if (s->zlist == NULL)
panic("out of memory");
s->zlist[s->semzcnt-1].who = who_e;
s->zlist[s->semzcnt-1].val = op_n;
@ -586,20 +613,16 @@ int do_semop(message *m)
no_reply++;
}
} else {
/* check for alter permission */
r = EACCES;
if (!check_perm(&sem->semid_ds.sem_perm, who_e, 0222))
goto out_free;
if (s->semval >= -op_n)
s->semval += op_n;
else {
/* put the process asleep */
s->semncnt++;
s->nlist = realloc(s->nlist, sizeof(struct waiting) * s->semncnt);
if (!s->nlist) {
printf("IPC: increase waiting list lost...\n");
break;
}
s->nlist = realloc(s->nlist,
sizeof(struct waiting) * s->semncnt);
/* continuing if NULL would lead to disaster */
if (s->nlist == NULL)
panic("out of memory");
s->nlist[s->semncnt-1].who = who_e;
s->nlist[s->semncnt-1].val = -op_n;
@ -608,23 +631,14 @@ int do_semop(message *m)
}
}
r = OK;
r = no_reply ? SUSPEND : OK;
out_free:
free(sops);
out:
/* if we reach here by errors
* or with no errors but we should reply back.
*/
if (r != OK || !no_reply) {
m->m_type = r;
ipc_sendnb(who_e, m);
}
/* awaken process if possible */
update_semaphores();
return 0;
return r;
}
/*===========================================================================*

View File

@ -1,35 +1,46 @@
#include "inc.h"
#define MAX_SHM_NR 1024
/* Private shm_perm.mode flags, synchronized with NetBSD kernel values */
#define SHM_ALLOC 0x0800 /* slot is in use (SHMSEG_ALLOCATED) */
struct shm_struct {
key_t key;
int id;
struct shmid_ds shmid_ds;
vir_bytes page;
int vm_id;
phys_bytes vm_id;
};
static struct shm_struct shm_list[MAX_SHM_NR];
static int shm_list_nr = 0;
static struct shm_struct shm_list[SHMMNI];
static unsigned int shm_list_nr = 0; /* highest in-use slot number plus one */
static struct shm_struct *shm_find_key(key_t key)
{
int i;
unsigned int i;
if (key == IPC_PRIVATE)
return NULL;
for (i = 0; i < shm_list_nr; i++)
if (shm_list[i].key == key)
return shm_list+i;
for (i = 0; i < shm_list_nr; i++) {
if (!(shm_list[i].shmid_ds.shm_perm.mode & SHM_ALLOC))
continue;
if (shm_list[i].shmid_ds.shm_perm._key == key)
return &shm_list[i];
}
return NULL;
}
static struct shm_struct *shm_find_id(int id)
{
int i;
for (i = 0; i < shm_list_nr; i++)
if (shm_list[i].id == id)
return shm_list+i;
return NULL;
struct shm_struct *shm;
unsigned int i;
i = IPCID_TO_IX(id);
if (i >= shm_list_nr)
return NULL;
shm = &shm_list[i];
if (!(shm->shmid_ds.shm_perm.mode & SHM_ALLOC))
return NULL;
if (shm->shmid_ds.shm_perm._seq != IPCID_TO_SEQ(id))
return NULL;
return shm;
}
/*===========================================================================*
@ -38,9 +49,11 @@ static struct shm_struct *shm_find_id(int id)
int do_shmget(message *m)
{
struct shm_struct *shm;
long key, size, old_size;
unsigned int i, seq;
key_t key;
size_t size, old_size;
int flag;
int id;
void *page;
key = m->m_lc_ipc_shmget.key;
old_size = size = m->m_lc_ipc_shmget.size;
@ -53,7 +66,7 @@ int do_shmget(message *m)
return EEXIST;
if (size && shm->shmid_ds.shm_segsz < size)
return EINVAL;
id = shm->id;
i = shm - shm_list;
} else { /* no key found */
if (!(flag & IPC_CREAT))
return ENOENT;
@ -65,39 +78,51 @@ int do_shmget(message *m)
if (size <= 0)
return EINVAL;
if (shm_list_nr == MAX_SHM_NR)
return ENOMEM;
/* TODO: shmmni should be changed... */
if (identifier == SHMMNI)
/* Find a free entry. */
for (i = 0; i < __arraycount(shm_list); i++)
if (!(shm_list[i].shmid_ds.shm_perm.mode & SHM_ALLOC))
break;
if (i == __arraycount(shm_list))
return ENOSPC;
shm = &shm_list[shm_list_nr];
memset(shm, 0, sizeof(struct shm_struct));
shm->page = (vir_bytes) mmap(0, size,
PROT_READ|PROT_WRITE, MAP_ANON, -1, 0);
if (shm->page == (vir_bytes) MAP_FAILED)
return ENOMEM;
shm->vm_id = vm_getphys(sef_self(), (void *) shm->page);
memset((void *)shm->page, 0, size);
/*
* Allocate memory to share. For now, we store the page
* reference as a numerical value so as to avoid issues with
* live update. TODO: a proper solution.
*/
page = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
if (page == MAP_FAILED)
return ENOMEM;
memset(page, 0, size);
/* Initialize the entry. */
shm = &shm_list[i];
seq = shm->shmid_ds.shm_perm._seq;
memset(shm, 0, sizeof(struct shm_struct));
shm->shmid_ds.shm_perm._key = key;
shm->shmid_ds.shm_perm.cuid =
shm->shmid_ds.shm_perm.uid = getnuid(who_e);
shm->shmid_ds.shm_perm.cgid =
shm->shmid_ds.shm_perm.gid = getngid(who_e);
shm->shmid_ds.shm_perm.mode = flag & 0777;
shm->shmid_ds.shm_perm.mode = SHM_ALLOC | (flag & ACCESSPERMS);
shm->shmid_ds.shm_perm._seq = (seq + 1) & 0x7fff;
shm->shmid_ds.shm_segsz = old_size;
shm->shmid_ds.shm_atime = 0;
shm->shmid_ds.shm_dtime = 0;
shm->shmid_ds.shm_ctime = time(NULL);
shm->shmid_ds.shm_ctime = clock_time(NULL);
shm->shmid_ds.shm_cpid = getnpid(who_e);
shm->shmid_ds.shm_lpid = 0;
shm->shmid_ds.shm_nattch = 0;
shm->id = id = identifier++;
shm->key = key;
shm->page = (vir_bytes)page;
shm->vm_id = vm_getphys(sef_self(), page);
shm_list_nr++;
assert(i <= shm_list_nr);
if (i == shm_list_nr)
shm_list_nr++;
}
m->m_lc_ipc_shmget.retid = id;
m->m_lc_ipc_shmget.retid = IXSEQ_TO_IPCID(i, shm->shmid_ds.shm_perm);
return OK;
}
@ -133,11 +158,11 @@ int do_shmat(message *m)
return EACCES;
ret = vm_remap(who_e, sef_self(), (void *)addr, (void *)shm->page,
shm->shmid_ds.shm_segsz);
shm->shmid_ds.shm_segsz);
if (ret == MAP_FAILED)
return ENOMEM;
shm->shmid_ds.shm_atime = time(NULL);
shm->shmid_ds.shm_atime = clock_time(NULL);
shm->shmid_ds.shm_lpid = getnpid(who_e);
/* nattach is updated lazily */
@ -150,10 +175,13 @@ int do_shmat(message *m)
*===========================================================================*/
void update_refcount_and_destroy(void)
{
int i, j;
size_t size;
u8_t rc;
unsigned int i;
for (i = 0, j = 0; i < shm_list_nr; i++) {
u8_t rc;
for (i = 0; i < shm_list_nr; i++) {
if (!(shm_list[i].shmid_ds.shm_perm.mode & SHM_ALLOC))
continue;
rc = vm_getrefcount(sef_self(), (void *) shm_list[i].page);
if (rc == (u8_t) -1) {
@ -162,19 +190,24 @@ void update_refcount_and_destroy(void)
}
shm_list[i].shmid_ds.shm_nattch = rc - 1;
if (shm_list[i].shmid_ds.shm_nattch ||
!(shm_list[i].shmid_ds.shm_perm.mode & SHM_DEST)) {
if (i != j)
shm_list[j] = shm_list[i];
j++;
} else {
int size = shm_list[i].shmid_ds.shm_segsz;
if (shm_list[i].shmid_ds.shm_nattch == 0 &&
(shm_list[i].shmid_ds.shm_perm.mode & SHM_DEST)) {
size = shm_list[i].shmid_ds.shm_segsz;
if (size % PAGE_SIZE)
size += PAGE_SIZE - size % PAGE_SIZE;
munmap((void *)shm_list[i].page, size);
/* Mark the entry as free. */
shm_list[i].shmid_ds.shm_perm.mode &= ~SHM_ALLOC;
}
}
shm_list_nr = j;
/*
* Now that we may have removed an arbitrary set of slots, ensure that
* shm_list_nr again equals the highest in-use slot number plus one.
*/
while (shm_list_nr > 0 &&
!(shm_list[shm_list_nr - 1].shmid_ds.shm_perm.mode & SHM_ALLOC))
shm_list_nr--;
}
/*===========================================================================*
@ -182,9 +215,10 @@ void update_refcount_and_destroy(void)
*===========================================================================*/
int do_shmdt(message *m)
{
struct shm_struct *shm;
vir_bytes addr;
phys_bytes vm_id;
int i;
unsigned int i;
addr = (vir_bytes) m->m_lc_ipc_shmdt.addr;
@ -192,10 +226,13 @@ int do_shmdt(message *m)
return EINVAL;
for (i = 0; i < shm_list_nr; i++) {
if (shm_list[i].vm_id == vm_id) {
struct shm_struct *shm = &shm_list[i];
shm = &shm_list[i];
shm->shmid_ds.shm_atime = time(NULL);
if (!(shm->shmid_ds.shm_perm.mode & SHM_ALLOC))
continue;
if (shm->vm_id == vm_id) {
shm->shmid_ds.shm_atime = clock_time(NULL);
shm->shmid_ds.shm_lpid = getnpid(who_e);
/* nattch is updated lazily */
@ -217,36 +254,56 @@ int do_shmdt(message *m)
*===========================================================================*/
int do_shmctl(message *m)
{
int id = m->m_lc_ipc_shmctl.id;
int cmd = m->m_lc_ipc_shmctl.cmd;
struct shmid_ds *ds = (struct shmid_ds *)m->m_lc_ipc_shmctl.buf;
struct shmid_ds *ds;
struct shmid_ds tmp_ds;
struct shm_struct *shm = NULL;
struct shm_struct *shm;
struct shminfo sinfo;
struct shm_info s_info;
unsigned int i;
uid_t uid;
int r, i;
int r, id, cmd;
if (cmd == IPC_STAT)
id = m->m_lc_ipc_shmctl.id;
cmd = m->m_lc_ipc_shmctl.cmd;
ds = (struct shmid_ds *)m->m_lc_ipc_shmctl.buf;
/*
* For stat calls, sure that all information is up-to-date. Since this
* may free the slot, do this before mapping from ID to slot below.
*/
if (cmd == IPC_STAT || cmd == SHM_STAT)
update_refcount_and_destroy();
if ((cmd == IPC_STAT ||
cmd == IPC_SET ||
cmd == IPC_RMID) &&
!(shm = shm_find_id(id)))
return EINVAL;
switch (cmd) {
case IPC_INFO:
case SHM_INFO:
shm = NULL;
break;
case SHM_STAT:
if (id < 0 || (unsigned int)id >= shm_list_nr)
return EINVAL;
shm = &shm_list[id];
if (!(shm->shmid_ds.shm_perm.mode & SHM_ALLOC))
return EINVAL;
break;
default:
if (!(shm = shm_find_id(id)))
return EINVAL;
break;
}
switch (cmd) {
case IPC_STAT:
if (!ds)
return EFAULT;
/* check whether it has read permission */
case SHM_STAT:
/* Check whether the caller has read permission. */
if (!check_perm(&shm->shmid_ds.shm_perm, who_e, 0444))
return EACCES;
r = sys_datacopy(SELF, (vir_bytes)&shm->shmid_ds,
who_e, (vir_bytes)ds, sizeof(struct shmid_ds));
if (r != OK)
return EFAULT;
if ((r = sys_datacopy(SELF, (vir_bytes)&shm->shmid_ds, who_e,
(vir_bytes)ds, sizeof(struct shmid_ds))) != OK)
return r;
if (cmd == SHM_STAT)
m->m_lc_ipc_shmctl.ret =
IXSEQ_TO_IPCID(id, shm->shmid_ds.shm_perm);
break;
case IPC_SET:
uid = getnuid(who_e);
@ -254,15 +311,15 @@ int do_shmctl(message *m)
uid != shm->shmid_ds.shm_perm.uid &&
uid != 0)
return EPERM;
r = sys_datacopy(who_e, (vir_bytes)ds,
SELF, (vir_bytes)&tmp_ds, sizeof(struct shmid_ds));
if (r != OK)
return EFAULT;
if ((r = sys_datacopy(who_e, (vir_bytes)ds, SELF,
(vir_bytes)&tmp_ds, sizeof(struct shmid_ds))) != OK)
return r;
shm->shmid_ds.shm_perm.uid = tmp_ds.shm_perm.uid;
shm->shmid_ds.shm_perm.gid = tmp_ds.shm_perm.gid;
shm->shmid_ds.shm_perm.mode &= ~0777;
shm->shmid_ds.shm_perm.mode |= tmp_ds.shm_perm.mode & 0666;
shm->shmid_ds.shm_ctime = time(NULL);
shm->shmid_ds.shm_perm.mode &= ~ACCESSPERMS;
shm->shmid_ds.shm_perm.mode |=
tmp_ds.shm_perm.mode & ACCESSPERMS;
shm->shmid_ds.shm_ctime = clock_time(NULL);
break;
case IPC_RMID:
uid = getnuid(who_e);
@ -275,24 +332,22 @@ int do_shmctl(message *m)
update_refcount_and_destroy();
break;
case IPC_INFO:
if (!ds)
return EFAULT;
memset(&sinfo, 0, sizeof(sinfo));
sinfo.shmmax = (unsigned long) -1;
sinfo.shmmin = 1;
sinfo.shmmni = MAX_SHM_NR;
sinfo.shmmni = __arraycount(shm_list);
sinfo.shmseg = (unsigned long) -1;
sinfo.shmall = (unsigned long) -1;
r = sys_datacopy(SELF, (vir_bytes)&sinfo,
who_e, (vir_bytes)ds, sizeof(struct shminfo));
if (r != OK)
return EFAULT;
m->m_lc_ipc_shmctl.ret = (shm_list_nr - 1);
if (m->m_lc_ipc_shmctl.ret < 0)
if ((r = sys_datacopy(SELF, (vir_bytes)&sinfo, who_e,
(vir_bytes)ds, sizeof(sinfo))) != OK)
return r;
if (shm_list_nr > 0)
m->m_lc_ipc_shmctl.ret = shm_list_nr - 1;
else
m->m_lc_ipc_shmctl.ret = 0;
break;
case SHM_INFO:
if (!ds)
return EFAULT;
memset(&s_info, 0, sizeof(s_info));
s_info.used_ids = shm_list_nr;
s_info.shm_tot = 0;
for (i = 0; i < shm_list_nr; i++)
@ -302,24 +357,14 @@ int do_shmctl(message *m)
s_info.shm_swp = 0;
s_info.swap_attempts = 0;
s_info.swap_successes = 0;
r = sys_datacopy(SELF, (vir_bytes)&s_info,
who_e, (vir_bytes)ds, sizeof(struct shm_info));
if (r != OK)
return EFAULT;
m->m_lc_ipc_shmctl.ret = shm_list_nr - 1;
if (m->m_lc_ipc_shmctl.ret < 0)
if ((r = sys_datacopy(SELF, (vir_bytes)&s_info, who_e,
(vir_bytes)ds, sizeof(s_info))) != OK)
return r;
if (shm_list_nr > 0)
m->m_lc_ipc_shmctl.ret = shm_list_nr - 1;
else
m->m_lc_ipc_shmctl.ret = 0;
break;
case SHM_STAT:
if (id < 0 || id >= shm_list_nr)
return EINVAL;
shm = &shm_list[id];
r = sys_datacopy(SELF, (vir_bytes)&shm->shmid_ds,
who_e, (vir_bytes)ds, sizeof(struct shmid_ds));
if (r != OK)
return EFAULT;
m->m_lc_ipc_shmctl.ret = shm->id;
break;
default:
return EINVAL;
}
@ -329,13 +374,16 @@ int do_shmctl(message *m)
#if 0
static void list_shm_ds(void)
{
int i;
unsigned int i;
printf("key\tid\tpage\n");
for (i = 0; i < shm_list_nr; i++)
for (i = 0; i < shm_list_nr; i++) {
if (!(shm_list[i].shmid_ds.shm_perm.mode & SHM_ALLOC))
continue;
printf("%ld\t%d\t%lx\n",
shm_list[i].key,
shm_list[i].id,
shm_list[i].shmid_ds.shm_perm._key,
IXSEQ_TO_IPCID(i, shm_list[i].shmid_ds.shm_perm),
shm_list[i].page);
}
}
#endif

View File

@ -326,7 +326,7 @@ ipc_semctl_out(struct trace_proc * proc, const message * m_out)
return CT_DONE;
case SETVAL:
put_value(proc, "val", "%d", m_out->m_lc_ipc_semctl.opt);
put_value(proc, "val", "%lu", m_out->m_lc_ipc_semctl.opt);
return CT_DONE;

View File

@ -187,11 +187,13 @@ struct sem_sysctl_info {
/* actual size of an undo structure */
#define SEMUSZ (sizeof(struct sem_undo)+sizeof(struct sem_undo_entry)*SEMUME)
#ifndef __minix
/*
* Structures allocated in machdep.c
*/
extern struct seminfo seminfo;
extern struct semid_ds *sema; /* semaphore id pool */
#endif /* !__minix */
/*
* Parameters to the semconfig system call

View File

@ -215,12 +215,12 @@ struct shm_info
unsigned long int swap_successes;
};
#define SHMMNI 4096
#define SHMMNI 1024
#define SHMSEG 32 /* max shared segs per process */
/* shm_mode upper byte flags */
#define SHM_DEST 01000 /* segment will be destroyed on last detach */
#define SHM_LOCKED 02000 /* segment will not be swapped */
/* Public shm_perm.mode flags, synchronized with NetBSD kernel values above */
#define SHM_DEST 0x0400 /* destroy on last detach (SHMSEG_REMOVED) */
#define SHM_LOCKED 0x4000 /* pages will not be swapped (SHMSEG_WIRED) */
#endif /* defined(__minix) */