aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/uipc_shm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/uipc_shm.c')
-rw-r--r--sys/kern/uipc_shm.c90
1 files changed, 87 insertions, 3 deletions
diff --git a/sys/kern/uipc_shm.c b/sys/kern/uipc_shm.c
index 55f350a0c589..d0340713c0d3 100644
--- a/sys/kern/uipc_shm.c
+++ b/sys/kern/uipc_shm.c
@@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sx.h>
#include <sys/time.h>
#include <sys/vnode.h>
+#include <sys/unistd.h>
#include <security/mac/mac_framework.h>
@@ -119,6 +120,7 @@ static fo_stat_t shm_stat;
static fo_close_t shm_close;
static fo_chmod_t shm_chmod;
static fo_chown_t shm_chown;
+static fo_seek_t shm_seek;
/* File descriptor operations. */
static struct fileops shm_ops = {
@@ -133,7 +135,8 @@ static struct fileops shm_ops = {
.fo_chmod = shm_chmod,
.fo_chown = shm_chown,
.fo_sendfile = invfo_sendfile,
- .fo_flags = DFLAG_PASSABLE
+ .fo_seek = shm_seek,
+ .fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE
};
FEATURE(posix_shm, "POSIX shared memory");
@@ -233,19 +236,96 @@ uiomove_object(vm_object_t obj, off_t obj_size, struct uio *uio)
}
static int
+shm_seek(struct file *fp, off_t offset, int whence, struct thread *td)
+{
+ struct shmfd *shmfd;
+ off_t foffset;
+ int error;
+
+ shmfd = fp->f_data;
+ foffset = foffset_lock(fp, 0);
+ error = 0;
+ switch (whence) {
+ case L_INCR:
+ if (foffset < 0 ||
+ (offset > 0 && foffset > OFF_MAX - offset)) {
+ error = EOVERFLOW;
+ break;
+ }
+ offset += foffset;
+ break;
+ case L_XTND:
+ if (offset > 0 && shmfd->shm_size > OFF_MAX - offset) {
+ error = EOVERFLOW;
+ break;
+ }
+ offset += shmfd->shm_size;
+ break;
+ case L_SET:
+ break;
+ default:
+ error = EINVAL;
+ }
+ if (error == 0) {
+ if (offset < 0 || offset > shmfd->shm_size)
+ error = EINVAL;
+ else
+ *(off_t *)(td->td_retval) = offset;
+ }
+ foffset_unlock(fp, offset, error != 0 ? FOF_NOUPDATE : 0);
+ return (error);
+}
+
+static int
shm_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
int flags, struct thread *td)
{
+ struct shmfd *shmfd;
+ void *rl_cookie;
+ int error;
- return (EOPNOTSUPP);
+ shmfd = fp->f_data;
+ foffset_lock_uio(fp, uio, flags);
+ rl_cookie = rangelock_rlock(&shmfd->shm_rl, uio->uio_offset,
+ uio->uio_offset + uio->uio_resid, &shmfd->shm_mtx);
+#ifdef MAC
+ error = mac_posixshm_check_read(active_cred, fp->f_cred, shmfd);
+ if (error)
+ return (error);
+#endif
+ error = uiomove_object(shmfd->shm_object, shmfd->shm_size, uio);
+ rangelock_unlock(&shmfd->shm_rl, rl_cookie, &shmfd->shm_mtx);
+ foffset_unlock_uio(fp, uio, flags);
+ return (error);
}
static int
shm_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
int flags, struct thread *td)
{
+ struct shmfd *shmfd;
+ void *rl_cookie;
+ int error;
- return (EOPNOTSUPP);
+ shmfd = fp->f_data;
+#ifdef MAC
+ error = mac_posixshm_check_write(active_cred, fp->f_cred, shmfd);
+ if (error)
+ return (error);
+#endif
+ foffset_lock_uio(fp, uio, flags);
+ if ((flags & FOF_OFFSET) == 0) {
+ rl_cookie = rangelock_wlock(&shmfd->shm_rl, 0, OFF_MAX,
+ &shmfd->shm_mtx);
+ } else {
+ rl_cookie = rangelock_wlock(&shmfd->shm_rl, uio->uio_offset,
+ uio->uio_offset + uio->uio_resid, &shmfd->shm_mtx);
+ }
+
+ error = uiomove_object(shmfd->shm_object, shmfd->shm_size, uio);
+ rangelock_unlock(&shmfd->shm_rl, rl_cookie, &shmfd->shm_mtx);
+ foffset_unlock_uio(fp, uio, flags);
+ return (error);
}
static int
@@ -471,6 +551,8 @@ shm_alloc(struct ucred *ucred, mode_t mode)
shmfd->shm_atime = shmfd->shm_mtime = shmfd->shm_ctime =
shmfd->shm_birthtime;
refcount_init(&shmfd->shm_refs, 1);
+ mtx_init(&shmfd->shm_mtx, "shmrl", NULL, MTX_DEF);
+ rangelock_init(&shmfd->shm_rl);
#ifdef MAC
mac_posixshm_init(shmfd);
mac_posixshm_create(ucred, shmfd);
@@ -495,6 +577,8 @@ shm_drop(struct shmfd *shmfd)
#ifdef MAC
mac_posixshm_destroy(shmfd);
#endif
+ rangelock_destroy(&shmfd->shm_rl);
+ mtx_destroy(&shmfd->shm_mtx);
vm_object_deallocate(shmfd->shm_object);
free(shmfd, M_SHMFD);
}