asmlinkage long sys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf) { struct shmid_kernel *shp; int err, version; struct ipc_namespace *ns;
if (cmd < 0 || shmid < 0) { err = -EINVAL; goto out; }
version = ipc_parse_version(&cmd); ns = current->nsproxy->ipc_ns;
switch (cmd) { /* replace with proc interface ? */ case IPC_INFO: { struct shminfo64 shminfo;
err = security_shm_shmctl(NULL, cmd); if (err) return err;
memset(&shminfo,0,sizeof(shminfo)); shminfo.shmmni = shminfo.shmseg = ns->shm_ctlmni; shminfo.shmmax = ns->shm_ctlmax; shminfo.shmall = ns->shm_ctlall;
shminfo.shmmin = SHMMIN; if(copy_shminfo_to_user (buf, &shminfo, version)) return -EFAULT;
down_read(&shm_ids(ns).rw_mutex); err = ipc_get_maxid(&shm_ids(ns)); up_read(&shm_ids(ns).rw_mutex);
if(err<0) err = 0; goto out; } case SHM_INFO: { struct shm_info shm_info;
err = security_shm_shmctl(NULL, cmd); if (err) return err;
memset(&shm_info,0,sizeof(shm_info)); down_read(&shm_ids(ns).rw_mutex); shm_info.used_ids = shm_ids(ns).in_use; shm_get_stat (ns, &shm_info.shm_rss, &shm_info.shm_swp); shm_info.shm_tot = ns->shm_tot; shm_info.swap_attempts = 0; shm_info.swap_successes = 0; err = ipc_get_maxid(&shm_ids(ns)); up_read(&shm_ids(ns).rw_mutex); if(copy_to_user (buf, &shm_info, sizeof(shm_info))) { err = -EFAULT; goto out; }
err = err < 0 ? 0 : err; goto out; } case SHM_STAT: case IPC_STAT: { struct shmid64_ds tbuf; int result;
if (!buf) { err = -EFAULT; goto out; }
if (cmd == SHM_STAT) { shp = shm_lock(ns, shmid); if (IS_ERR(shp)) { err = PTR_ERR(shp); goto out; } result = shp->shm_perm.id; } else { shp = shm_lock_check(ns, shmid); if (IS_ERR(shp)) { err = PTR_ERR(shp); goto out; } result = 0; } err=-EACCES; if (ipcperms (&shp->shm_perm, S_IRUGO)) goto out_unlock; err = security_shm_shmctl(shp, cmd); if (err) goto out_unlock; memset(&tbuf, 0, sizeof(tbuf)); kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm); tbuf.shm_segsz = shp->shm_segsz; tbuf.shm_atime = shp->shm_atim; tbuf.shm_dtime = shp->shm_dtim; tbuf.shm_ctime = shp->shm_ctim; tbuf.shm_cpid = shp->shm_cprid; tbuf.shm_lpid = shp->shm_lprid; tbuf.shm_nattch = shp->shm_nattch; shm_unlock(shp); if(copy_shmid_to_user (buf, &tbuf, version)) err = -EFAULT; else err = result; goto out; } case SHM_LOCK: case SHM_UNLOCK: { shp = shm_lock_check(ns, shmid); if (IS_ERR(shp)) { err = PTR_ERR(shp); goto out; }
err = audit_ipc_obj(&(shp->shm_perm)); if (err) goto out_unlock;
if (!capable(CAP_IPC_LOCK)) { err = -EPERM; if (current->euid != shp->shm_perm.uid && current->euid != shp->shm_perm.cuid) goto out_unlock; if (cmd == SHM_LOCK && !current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur) goto out_unlock; }
err = security_shm_shmctl(shp, cmd); if (err) goto out_unlock; if(cmd==SHM_LOCK) { struct user_struct * user = current->user; if (!is_file_hugepages(shp->shm_file)) { err = shmem_lock(shp->shm_file, 1, user); if (!err && !(shp->shm_perm.mode & SHM_LOCKED)){ shp->shm_perm.mode |= SHM_LOCKED; shp->mlock_user = user; } } } else if (!is_file_hugepages(shp->shm_file)) { shmem_lock(shp->shm_file, 0, shp->mlock_user); shp->shm_perm.mode &= ~SHM_LOCKED; shp->mlock_user = NULL; } shm_unlock(shp); goto out; } case IPC_RMID: case IPC_SET: err = shmctl_down(ns, shmid, cmd, buf, version); return err; default: return -EINVAL; }
out_unlock: shm_unlock(shp); out: return err; }
|