asmlinkage int sys_semctl (int semid, int semnum, int cmd, union semun arg) { struct semid_ds *buf = NULL; struct semid_ds tbuf; int i, id, val = 0; struct semid_ds *sma; struct ipc_perm *ipcp; struct sem *curr = NULL; struct sem_undo *un; unsigned int nsems; ushort *array = NULL; ushort sem_io[SEMMSL]; int err = -EINVAL; lock_kernel(); if (semid < 0 || semnum < 0 || cmd < 0) goto out; switch (cmd) { case IPC_INFO: case SEM_INFO: { struct seminfo seminfo, *tmp = arg.__buf; seminfo.semmni = SEMMNI; seminfo.semmns = SEMMNS; seminfo.semmsl = SEMMSL; seminfo.semopm = SEMOPM; seminfo.semvmx = SEMVMX; seminfo.semmnu = SEMMNU; seminfo.semmap = SEMMAP; seminfo.semume = SEMUME; seminfo.semusz = SEMUSZ; seminfo.semaem = SEMAEM; if (cmd == SEM_INFO) { seminfo.semusz = used_semids; seminfo.semaem = used_sems; } err = -EFAULT; if (copy_to_user (tmp, &seminfo, sizeof(struct seminfo))) goto out; err = max_semid; goto out; } case SEM_STAT: buf = arg.buf; err = -EINVAL; if (semid > max_semid) goto out; sma = semary[semid]; if (sma == IPC_UNUSED || sma == IPC_NOID) goto out; err = -EACCES; if (ipcperms (&sma->sem_perm, S_IRUGO)) goto out; id = (unsigned int) sma->sem_perm.seq * SEMMNI + semid; tbuf.sem_perm = sma->sem_perm; tbuf.sem_otime = sma->sem_otime; tbuf.sem_ctime = sma->sem_ctime; tbuf.sem_nsems = sma->sem_nsems; err = -EFAULT; if (copy_to_user (buf, &tbuf, sizeof(*buf)) == 0) err = id; goto out; } id = (unsigned int) semid % SEMMNI; sma = semary [id]; err = -EINVAL; if (sma == IPC_UNUSED || sma == IPC_NOID) goto out; ipcp = &sma->sem_perm; nsems = sma->sem_nsems; err = -EIDRM; if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI) goto out; switch (cmd) { case GETVAL: case GETPID: case GETNCNT: case GETZCNT: case SETVAL: err = -EINVAL; if (semnum >= nsems) goto out; curr = &sma->sem_base[semnum]; break; } switch (cmd) { case GETVAL: case GETPID: case GETNCNT: case GETZCNT: case GETALL: err = -EACCES; if (ipcperms (ipcp, S_IRUGO)) goto out; switch (cmd) { case GETVAL : err = curr->semval; goto out; case GETPID : err = curr->sempid & 0xffff; goto out; case GETNCNT: err = count_semncnt(sma,semnum); goto out; case GETZCNT: err = count_semzcnt(sma,semnum); goto out; case GETALL: array = arg.array; break; } break; case SETVAL: val = arg.val; err = -ERANGE; if (val > SEMVMX || val < 0) goto out; break; case IPC_RMID: if (current->euid == ipcp->cuid || current->euid == ipcp->uid || capable(CAP_SYS_ADMIN)) { freeary (id); err = 0; goto out; } err = -EPERM; goto out; case SETALL: /* arg is a pointer to an array of ushort */ array = arg.array; err = -EFAULT; if (copy_from_user (sem_io, array, nsems*sizeof(ushort))) goto out; err = 0; for (i = 0; i < nsems; i++) if (sem_io[i] > SEMVMX) { err = -ERANGE; goto out; } break; case IPC_STAT: buf = arg.buf; break; case IPC_SET: buf = arg.buf; err = copy_from_user (&tbuf, buf, sizeof (*buf)); if (err) err = -EFAULT; break; } err = -EIDRM; if (semary[id] == IPC_UNUSED || semary[id] == IPC_NOID) goto out; if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI) goto out; switch (cmd) { case GETALL: err = -EACCES; if (ipcperms (ipcp, S_IRUGO)) goto out; for (i = 0; i < sma->sem_nsems; i++) sem_io[i] = sma->sem_base[i].semval; if (copy_to_user (array, sem_io, nsems*sizeof(ushort))) err = -EFAULT; break; case SETVAL: err = -EACCES; if (ipcperms (ipcp, S_IWUGO)) goto out; for (un = sma->undo; un; un = un->id_next) un->semadj[semnum] = 0; curr->semval = val; sma->sem_ctime = CURRENT_TIME; /* maybe some queued-up processes were waiting for this */ update_queue(sma); break; case IPC_SET: if (current->euid == ipcp->cuid || current->euid == ipcp->uid || capable(CAP_SYS_ADMIN)) { ipcp->uid = tbuf.sem_perm.uid; ipcp->gid = tbuf.sem_perm.gid; ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | (tbuf.sem_perm.mode & S_IRWXUGO); sma->sem_ctime = CURRENT_TIME; err = 0; goto out; } err = -EPERM; goto out; case IPC_STAT: err = -EACCES; if (ipcperms (ipcp, S_IRUGO)) goto out; tbuf.sem_perm = sma->sem_perm; tbuf.sem_otime = sma->sem_otime; tbuf.sem_ctime = sma->sem_ctime; tbuf.sem_nsems = sma->sem_nsems; if (copy_to_user (buf, &tbuf, sizeof(*buf))) err = -EFAULT; break; case SETALL: err = -EACCES; if (ipcperms (ipcp, S_IWUGO)) goto out; for (i = 0; i < nsems; i++) sma->sem_base[i].semval = sem_io[i]; for (un = sma->undo; un; un = un->id_next) for (i = 0; i < nsems; i++) un->semadj[i] = 0; sma->sem_ctime = CURRENT_TIME; /* maybe some queued-up processes were waiting for this */ update_queue(sma); break; default: err = -EINVAL; goto out; } err = 0; out: unlock_kernel(); return err; }
int sys_semctl (int semid, int semnum, int cmd, union semun arg) { struct semid_ds *buf = NULL; struct semid_ds tbuf; int i, id, val = 0; struct semid_ds *sma; struct ipc_perm *ipcp; struct sem *curr = NULL; struct sem_undo *un; unsigned int nsems; ushort *array = NULL; ushort sem_io[SEMMSL]; if (semid < 0 || semnum < 0 || cmd < 0) return -EINVAL; switch (cmd) { case IPC_INFO: case SEM_INFO: { struct seminfo seminfo, *tmp = arg.__buf; seminfo.semmni = SEMMNI; seminfo.semmns = SEMMNS; seminfo.semmsl = SEMMSL; seminfo.semopm = SEMOPM; seminfo.semvmx = SEMVMX; seminfo.semmnu = SEMMNU; seminfo.semmap = SEMMAP; seminfo.semume = SEMUME; seminfo.semusz = SEMUSZ; seminfo.semaem = SEMAEM; if (cmd == SEM_INFO) { seminfo.semusz = used_semids; seminfo.semaem = used_sems; } i = verify_area(VERIFY_WRITE, tmp, sizeof(struct seminfo)); if (i) return i; memcpy_tofs (tmp, &seminfo, sizeof(struct seminfo)); return max_semid; } case SEM_STAT: buf = arg.buf; i = verify_area (VERIFY_WRITE, buf, sizeof (*buf)); if (i) return i; if (semid > max_semid) return -EINVAL; sma = semary[semid]; if (sma == IPC_UNUSED || sma == IPC_NOID) return -EINVAL; if (ipcperms (&sma->sem_perm, S_IRUGO)) return -EACCES; id = (unsigned int) sma->sem_perm.seq * SEMMNI + semid; tbuf.sem_perm = sma->sem_perm; tbuf.sem_otime = sma->sem_otime; tbuf.sem_ctime = sma->sem_ctime; tbuf.sem_nsems = sma->sem_nsems; memcpy_tofs (buf, &tbuf, sizeof(*buf)); return id; } id = (unsigned int) semid % SEMMNI; sma = semary [id]; if (sma == IPC_UNUSED || sma == IPC_NOID) return -EINVAL; ipcp = &sma->sem_perm; nsems = sma->sem_nsems; if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI) return -EIDRM; switch (cmd) { case GETVAL: case GETPID: case GETNCNT: case GETZCNT: case SETVAL: if (semnum >= nsems) return -EINVAL; curr = &sma->sem_base[semnum]; break; } switch (cmd) { case GETVAL: case GETPID: case GETNCNT: case GETZCNT: case GETALL: if (ipcperms (ipcp, S_IRUGO)) return -EACCES; switch (cmd) { case GETVAL : return curr->semval; case GETPID : return curr->sempid; case GETNCNT: return count_semncnt(sma,semnum); case GETZCNT: return count_semzcnt(sma,semnum); case GETALL: array = arg.array; i = verify_area (VERIFY_WRITE, array, nsems*sizeof(ushort)); if (i) return i; } break; case SETVAL: val = arg.val; if (val > SEMVMX || val < 0) return -ERANGE; break; case IPC_RMID: if (suser() || current->euid == ipcp->cuid || current->euid == ipcp->uid) { freeary (id); return 0; } return -EPERM; case SETALL: /* arg is a pointer to an array of ushort */ array = arg.array; if ((i = verify_area (VERIFY_READ, array, nsems*sizeof(ushort)))) return i; memcpy_fromfs (sem_io, array, nsems*sizeof(ushort)); for (i = 0; i < nsems; i++) if (sem_io[i] > SEMVMX) return -ERANGE; break; case IPC_STAT: buf = arg.buf; if ((i = verify_area (VERIFY_WRITE, buf, sizeof(*buf)))) return i; break; case IPC_SET: buf = arg.buf; if ((i = verify_area (VERIFY_READ, buf, sizeof (*buf)))) return i; memcpy_fromfs (&tbuf, buf, sizeof (*buf)); break; } if (semary[id] == IPC_UNUSED || semary[id] == IPC_NOID) return -EIDRM; if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI) return -EIDRM; switch (cmd) { case GETALL: if (ipcperms (ipcp, S_IRUGO)) return -EACCES; for (i = 0; i < sma->sem_nsems; i++) sem_io[i] = sma->sem_base[i].semval; memcpy_tofs (array, sem_io, nsems*sizeof(ushort)); break; case SETVAL: if (ipcperms (ipcp, S_IWUGO)) return -EACCES; for (un = sma->undo; un; un = un->id_next) un->semadj[semnum] = 0; curr->semval = val; sma->sem_ctime = CURRENT_TIME; /* maybe some queued-up processes were waiting for this */ update_queue(sma); break; case IPC_SET: if (suser() || current->euid == ipcp->cuid || current->euid == ipcp->uid) { ipcp->uid = tbuf.sem_perm.uid; ipcp->gid = tbuf.sem_perm.gid; ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | (tbuf.sem_perm.mode & S_IRWXUGO); sma->sem_ctime = CURRENT_TIME; return 0; } return -EPERM; case IPC_STAT: if (ipcperms (ipcp, S_IRUGO)) return -EACCES; tbuf.sem_perm = sma->sem_perm; tbuf.sem_otime = sma->sem_otime; tbuf.sem_ctime = sma->sem_ctime; tbuf.sem_nsems = sma->sem_nsems; memcpy_tofs (buf, &tbuf, sizeof(*buf)); break; case SETALL: if (ipcperms (ipcp, S_IWUGO)) return -EACCES; for (i = 0; i < nsems; i++) sma->sem_base[i].semval = sem_io[i]; for (un = sma->undo; un; un = un->id_next) for (i = 0; i < nsems; i++) un->semadj[i] = 0; sma->sem_ctime = CURRENT_TIME; /* maybe some queued-up processes were waiting for this */ update_queue(sma); break; default: return -EINVAL; } return 0; }
static int semctl_main(int semid, int semnum, int cmd, int version, union semun arg) { struct sem_array *sma; struct sem* curr; int err; ushort fast_sem_io[SEMMSL_FAST]; ushort* sem_io = fast_sem_io; int nsems; sma = sem_lock(semid); if(sma==NULL) return -EINVAL; nsems = sma->sem_nsems; err=-EIDRM; if (sem_checkid(sma,semid)) goto out_unlock; err = -EACCES; if (ipcperms (&sma->sem_perm, (cmd==SETVAL||cmd==SETALL)?S_IWUGO:S_IRUGO)) goto out_unlock; switch (cmd) { case GETALL: { ushort *array = arg.array; int i; if(nsems > SEMMSL_FAST) { sem_unlock(semid); sem_io = ipc_alloc(sizeof(ushort)*nsems); if(sem_io == NULL) return -ENOMEM; err = sem_revalidate(semid, sma, nsems, S_IRUGO); if(err) goto out_free; } for (i = 0; i < sma->sem_nsems; i++) sem_io[i] = sma->sem_base[i].semval; sem_unlock(semid); err = 0; if(copy_to_user(array, sem_io, nsems*sizeof(ushort))) err = -EFAULT; goto out_free; } case SETALL: { int i; struct sem_undo *un; sem_unlock(semid); if(nsems > SEMMSL_FAST) { sem_io = ipc_alloc(sizeof(ushort)*nsems); if(sem_io == NULL) return -ENOMEM; } if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) { err = -EFAULT; goto out_free; } for (i = 0; i < nsems; i++) { if (sem_io[i] > SEMVMX) { err = -ERANGE; goto out_free; } } err = sem_revalidate(semid, sma, nsems, S_IWUGO); if(err) goto out_free; for (i = 0; i < nsems; i++) sma->sem_base[i].semval = sem_io[i]; for (un = sma->undo; un; un = un->id_next) for (i = 0; i < nsems; i++) un->semadj[i] = 0; sma->sem_ctime = CURRENT_TIME; /* maybe some queued-up processes were waiting for this */ update_queue(sma); err = 0; goto out_unlock; } case IPC_STAT: { struct semid64_ds tbuf; memset(&tbuf,0,sizeof(tbuf)); kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm); tbuf.sem_otime = sma->sem_otime; tbuf.sem_ctime = sma->sem_ctime; tbuf.sem_nsems = sma->sem_nsems; sem_unlock(semid); if (copy_semid_to_user (arg.buf, &tbuf, version)) return -EFAULT; return 0; } /* GETVAL, GETPID, GETNCTN, GETZCNT, SETVAL: fall-through */ } err = -EINVAL; if(semnum < 0 || semnum >= nsems) goto out_unlock; curr = &sma->sem_base[semnum]; switch (cmd) { case GETVAL: err = curr->semval; goto out_unlock; case GETPID: err = curr->sempid & 0xffff; goto out_unlock; case GETNCNT: err = count_semncnt(sma,semnum); goto out_unlock; case GETZCNT: err = count_semzcnt(sma,semnum); goto out_unlock; case SETVAL: { int val = arg.val; struct sem_undo *un; err = -ERANGE; if (val > SEMVMX || val < 0) goto out_unlock; for (un = sma->undo; un; un = un->id_next) un->semadj[semnum] = 0; curr->semval = val; curr->sempid = current->tgid; sma->sem_ctime = CURRENT_TIME; /* maybe some queued-up processes were waiting for this */ update_queue(sma); err = 0; goto out_unlock; } } out_unlock: sem_unlock(semid); out_free: if(sem_io != fast_sem_io) ipc_free(sem_io, sizeof(ushort)*nsems); return err; }