static int shm_delete_mapping(__unused struct proc *p, struct shmmap_state *shmmap_s, int deallocate) { struct shmid_kernel *shmseg; int segnum, result; mach_vm_size_t size; segnum = IPCID_TO_IX(shmmap_s->shmid); shmseg = &shmsegs[segnum]; size = mach_vm_round_page(shmseg->u.shm_segsz); /* XXX done for us? */ if (deallocate) { result = mach_vm_deallocate(current_map(), shmmap_s->va, size); if (result != KERN_SUCCESS) return EINVAL; } shmmap_s->shmid = SHMID_UNALLOCATED; shmseg->u.shm_dtime = sysv_shmtime(); if ((--shmseg->u.shm_nattch <= 0) && (shmseg->u.shm_perm.mode & SHMSEG_REMOVED)) { shm_deallocate_segment(shmseg); shm_last_free = segnum; } return 0; }
/* Handle a shmdt() request. */ int handle_shmdt(pid_t pid, int shmid) { struct shmid_ds *shmseg; int segnum; struct shm_handle *handle; struct pid_attached *pidatt; struct id_attached *idatt; struct client *cl; sysvd_print("shmdt pid %d shmid %d\n", pid, shmid); /*if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL) return (ENOSYS); */ segnum = IPCID_TO_IX(shmid); shmseg = &shmsegs[segnum]; handle = shmseg->shm_internal; /* Check if pid is attached. */ LIST_FOREACH(pidatt, &handle->attached_list, link) if (pidatt->pid == pid) break; if (!pidatt) { sysvd_print_err("process %d is not attached to %d (1)\n", pid, shmid); return (EINVAL); } LIST_REMOVE(pidatt, link); /* Remove the segment from the list of attached segments of the pid.*/ cl = _hash_lookup(clientshash, pid); LIST_FOREACH(idatt, &cl->ids_attached, link) if (idatt->shmid == shmid) break; if (!idatt) { sysvd_print_err("process %d is not attached to %d (2)\n", pid, shmid); return (EINVAL); } LIST_REMOVE(idatt, link); shmseg->shm_dtime = time(NULL); /* If no other process attaced remove the segment. */ if ((--shmseg->shm_nattch <= 0) && (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { shm_deallocate_segment(segnum); shm_last_free = segnum; } return (0); }
int shmctl1(struct proc *p, int shmid, int cmd, caddr_t buf, int (*ds_copyin)(const void *, void *, size_t), int (*ds_copyout)(const void *, void *, size_t)) { struct ucred *cred = p->p_ucred; struct shmid_ds inbuf, *shmseg; int error; shmseg = shm_find_segment_by_shmid(shmid); if (shmseg == NULL) return (EINVAL); switch (cmd) { case IPC_STAT: if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_R)) != 0) return (error); error = ds_copyout(shmseg, buf, sizeof(inbuf)); if (error) return (error); break; case IPC_SET: if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_M)) != 0) return (error); error = ds_copyin(buf, &inbuf, sizeof(inbuf)); if (error) return (error); shmseg->shm_perm.uid = inbuf.shm_perm.uid; shmseg->shm_perm.gid = inbuf.shm_perm.gid; shmseg->shm_perm.mode = (shmseg->shm_perm.mode & ~ACCESSPERMS) | (inbuf.shm_perm.mode & ACCESSPERMS); shmseg->shm_ctime = time_second; break; case IPC_RMID: if ((error = ipcperm(cred, &shmseg->shm_perm, IPC_M)) != 0) return (error); shmseg->shm_perm.key = IPC_PRIVATE; shmseg->shm_perm.mode |= SHMSEG_REMOVED; if (shmseg->shm_nattch <= 0) { shm_deallocate_segment(shmseg); shm_last_free = IPCID_TO_IX(shmid); shmsegs[shm_last_free] = NULL; } break; case SHM_LOCK: case SHM_UNLOCK: default: return (EINVAL); } return (0); }
int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s) { struct shmid_ds *shmseg; int segnum; size_t size; segnum = IPCID_TO_IX(shmmap_s->shmid); if (segnum < 0 || segnum >= shminfo.shmmni || (shmseg = shmsegs[segnum]) == NULL) return (EINVAL); size = round_page(shmseg->shm_segsz); uvm_deallocate(&vm->vm_map, shmmap_s->va, size); shmmap_s->shmid = -1; shmseg->shm_dtime = time_second; if ((--shmseg->shm_nattch <= 0) && (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { shm_deallocate_segment(shmseg); shm_last_free = segnum; shmsegs[shm_last_free] = NULL; } return (0); }
/* Handle a shmctl() request. */ int handle_shmctl(struct shmctl_msg *shmctl_msg, struct cmsgcred *cred ) { int error = 0; struct shmid_ds *shmseg, *inbuf; /* if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL) return (ENOSYS); */ shmseg = shm_find_segment_by_shmid(shmctl_msg->shmid); if (shmseg == NULL) { error = EINVAL; goto done; } switch (shmctl_msg->cmd) { case IPC_STAT: sysvd_print("IPC STAT\n"); error = ipcperm(cred, &shmseg->shm_perm, IPC_R); if (error) { sysvd_print("IPC_STAT not allowed\n"); break; } shmctl_msg->buf = *shmseg; break; case IPC_SET: sysvd_print("IPC SET\n"); error = ipcperm(cred, &shmseg->shm_perm, IPC_M); if (error) { sysvd_print("IPC_SET not allowed\n"); break; } inbuf = &shmctl_msg->buf; shmseg->shm_perm.uid = inbuf->shm_perm.uid; shmseg->shm_perm.gid = inbuf->shm_perm.gid; shmseg->shm_perm.mode = (shmseg->shm_perm.mode & ~ACCESSPERMS) | (inbuf->shm_perm.mode & ACCESSPERMS); shmseg->shm_ctime = time(NULL); break; case IPC_RMID: sysvd_print("IPC RMID shmid = %d\n", shmctl_msg->shmid); error = ipcperm(cred, &shmseg->shm_perm, IPC_M); if (error) { sysvd_print("IPC_RMID not allowed\n"); break; } shmseg->shm_perm.key = IPC_PRIVATE; shmseg->shm_perm.mode |= SHMSEG_REMOVED; if (shmseg->shm_nattch <= 0) { shm_deallocate_segment(IPCID_TO_IX(shmctl_msg->shmid)); shm_last_free = IPCID_TO_IX(shmctl_msg->shmid); } else { /* In sem and msg cases, other process must be * noticed about the removal. */ struct shm_handle *internal = (struct shm_handle *)shmseg->shm_internal; mark_segment_removed(shmctl_msg->shmid, internal->type); } break; #if 0 case SHM_LOCK: case SHM_UNLOCK: #endif default: error = EINVAL; break; } done: return (error); }