summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2011-08-16 20:07:47 +0000
committerkib <kib@FreeBSD.org>2011-08-16 20:07:47 +0000
commit011f42054d1f861cd2435866ba646fa0cf752103 (patch)
tree368bd0d481caa3081d9e36be04c87a458ccd8975 /sys
parenta5a39a26b2ea8ab4d283505d58396a5e76d38c7a (diff)
downloadFreeBSD-src-011f42054d1f861cd2435866ba646fa0cf752103.zip
FreeBSD-src-011f42054d1f861cd2435866ba646fa0cf752103.tar.gz
Add the fo_chown and fo_chmod methods to struct fileops and use them
to implement fchown(2) and fchmod(2) support for several file types that previously lacked it. Add MAC entries for chown/chmod done on posix shared memory and (old) in-kernel posix semaphores. Based on the submission by: glebius Reviewed by: rwatson Approved by: re (bz)
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/streams/streams.c4
-rw-r--r--sys/fs/devfs/devfs_vnops.c2
-rw-r--r--sys/fs/fifofs/fifo_vnops.c2
-rw-r--r--sys/kern/kern_descrip.c33
-rw-r--r--sys/kern/kern_event.c2
-rw-r--r--sys/kern/sys_capability.c22
-rw-r--r--sys/kern/sys_pipe.c2
-rw-r--r--sys/kern/sys_socket.c2
-rw-r--r--sys/kern/tty_pts.c2
-rw-r--r--sys/kern/uipc_mqueue.c49
-rw-r--r--sys/kern/uipc_sem.c64
-rw-r--r--sys/kern/uipc_shm.c77
-rw-r--r--sys/kern/vfs_syscalls.c60
-rw-r--r--sys/kern/vfs_vnops.c41
-rw-r--r--sys/ofed/include/linux/linux_compat.c4
-rw-r--r--sys/opencrypto/cryptodev.c4
-rw-r--r--sys/security/mac/mac_framework.h8
-rw-r--r--sys/security/mac/mac_policy.h16
-rw-r--r--sys/security/mac/mac_posix_sem.c32
-rw-r--r--sys/security/mac/mac_posix_shm.c32
-rw-r--r--sys/security/mac_stub/mac_stub.c36
-rw-r--r--sys/security/mac_test/mac_test.c52
-rw-r--r--sys/sys/file.h27
-rw-r--r--sys/sys/vnode.h10
24 files changed, 533 insertions, 50 deletions
diff --git a/sys/dev/streams/streams.c b/sys/dev/streams/streams.c
index 00f65a3..ad2817f 100644
--- a/sys/dev/streams/streams.c
+++ b/sys/dev/streams/streams.c
@@ -95,7 +95,9 @@ static struct fileops svr4_netops = {
.fo_poll = soo_poll,
.fo_kqfilter = soo_kqfilter,
.fo_stat = soo_stat,
- .fo_close = svr4_soo_close
+ .fo_close = svr4_soo_close,
+ .fo_chmod = invfo_chmod,
+ .fo_chown = invfo_chown,
};
static struct cdevsw streams_cdevsw = {
diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c
index 955bd8b..68ab7ce 100644
--- a/sys/fs/devfs/devfs_vnops.c
+++ b/sys/fs/devfs/devfs_vnops.c
@@ -1665,6 +1665,8 @@ static struct fileops devfs_ops_f = {
.fo_kqfilter = devfs_kqfilter_f,
.fo_stat = devfs_stat_f,
.fo_close = devfs_close_f,
+ .fo_chmod = vn_chmod,
+ .fo_chown = vn_chown,
.fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE
};
diff --git a/sys/fs/fifofs/fifo_vnops.c b/sys/fs/fifofs/fifo_vnops.c
index e339a8a..b8d4a40 100644
--- a/sys/fs/fifofs/fifo_vnops.c
+++ b/sys/fs/fifofs/fifo_vnops.c
@@ -72,6 +72,8 @@ struct fileops fifo_ops_f = {
.fo_kqfilter = fifo_kqfilter_f,
.fo_stat = fifo_stat_f,
.fo_close = fifo_close_f,
+ .fo_chmod = vn_chmod,
+ .fo_chown = vn_chown,
.fo_flags = DFLAG_PASSABLE
};
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index cf4eced..85f866c 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -3774,6 +3774,22 @@ badfo_close(struct file *fp, struct thread *td)
return (EBADF);
}
+static int
+badfo_chmod(struct file *fp, mode_t mode, struct ucred *active_cred,
+ struct thread *td)
+{
+
+ return (EBADF);
+}
+
+static int
+badfo_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
+ struct thread *td)
+{
+
+ return (EBADF);
+}
+
struct fileops badfileops = {
.fo_read = badfo_readwrite,
.fo_write = badfo_readwrite,
@@ -3783,8 +3799,25 @@ struct fileops badfileops = {
.fo_kqfilter = badfo_kqfilter,
.fo_stat = badfo_stat,
.fo_close = badfo_close,
+ .fo_chmod = badfo_chmod,
+ .fo_chown = badfo_chown,
};
+int
+invfo_chmod(struct file *fp, mode_t mode, struct ucred *active_cred,
+ struct thread *td)
+{
+
+ return (EINVAL);
+}
+
+int
+invfo_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
+ struct thread *td)
+{
+
+ return (EINVAL);
+}
/*-------------------------------------------------------------------*/
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
index e282cd5..c512b0a 100644
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -123,6 +123,8 @@ static struct fileops kqueueops = {
.fo_kqfilter = kqueue_kqfilter,
.fo_stat = kqueue_stat,
.fo_close = kqueue_close,
+ .fo_chmod = invfo_chmod,
+ .fo_chown = invfo_chown,
};
static int knote_attach(struct knote *kn, struct kqueue *kq);
diff --git a/sys/kern/sys_capability.c b/sys/kern/sys_capability.c
index 0eb27f3..318ffe4 100644
--- a/sys/kern/sys_capability.c
+++ b/sys/kern/sys_capability.c
@@ -161,6 +161,8 @@ static fo_poll_t capability_poll;
static fo_kqfilter_t capability_kqfilter;
static fo_stat_t capability_stat;
static fo_close_t capability_close;
+static fo_chmod_t capability_chmod;
+static fo_chown_t capability_chown;
static struct fileops capability_ops = {
.fo_read = capability_read,
@@ -171,6 +173,8 @@ static struct fileops capability_ops = {
.fo_kqfilter = capability_kqfilter,
.fo_stat = capability_stat,
.fo_close = capability_close,
+ .fo_chmod = capability_chmod,
+ .fo_chown = capability_chown,
.fo_flags = DFLAG_PASSABLE,
};
@@ -183,6 +187,8 @@ static struct fileops capability_ops_unpassable = {
.fo_kqfilter = capability_kqfilter,
.fo_stat = capability_stat,
.fo_close = capability_close,
+ .fo_chmod = capability_chmod,
+ .fo_chown = capability_chown,
.fo_flags = 0,
};
@@ -484,6 +490,22 @@ capability_stat(struct file *fp, struct stat *sb, struct ucred *active_cred,
panic("capability_stat");
}
+int
+capability_chmod(struct file *fp, mode_t mode, struct ucred *active_cred,
+ struct thread *td)
+{
+
+ panic("capability_chmod");
+}
+
+int
+capability_chown(struct file *fp, uid_t uid, gid_t gid,
+ struct ucred *active_cred, struct thread *td)
+{
+
+ panic("capability_chown");
+}
+
#else /* !CAPABILITIES */
/*
diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c
index 50b0389..14e1207 100644
--- a/sys/kern/sys_pipe.c
+++ b/sys/kern/sys_pipe.c
@@ -155,6 +155,8 @@ static struct fileops pipeops = {
.fo_kqfilter = pipe_kqfilter,
.fo_stat = pipe_stat,
.fo_close = pipe_close,
+ .fo_chmod = invfo_chmod,
+ .fo_chown = invfo_chown,
.fo_flags = DFLAG_PASSABLE
};
diff --git a/sys/kern/sys_socket.c b/sys/kern/sys_socket.c
index c9b0534..cd6f655 100644
--- a/sys/kern/sys_socket.c
+++ b/sys/kern/sys_socket.c
@@ -64,6 +64,8 @@ struct fileops socketops = {
.fo_kqfilter = soo_kqfilter,
.fo_stat = soo_stat,
.fo_close = soo_close,
+ .fo_chmod = invfo_chmod,
+ .fo_chown = invfo_chown,
.fo_flags = DFLAG_PASSABLE
};
diff --git a/sys/kern/tty_pts.c b/sys/kern/tty_pts.c
index a3db59b..cf9f94d 100644
--- a/sys/kern/tty_pts.c
+++ b/sys/kern/tty_pts.c
@@ -597,6 +597,8 @@ static struct fileops ptsdev_ops = {
.fo_kqfilter = ptsdev_kqfilter,
.fo_stat = ptsdev_stat,
.fo_close = ptsdev_close,
+ .fo_chmod = invfo_chmod,
+ .fo_chown = invfo_chown,
.fo_flags = DFLAG_PASSABLE,
};
diff --git a/sys/kern/uipc_mqueue.c b/sys/kern/uipc_mqueue.c
index 0ca4161..fbd78c1 100644
--- a/sys/kern/uipc_mqueue.c
+++ b/sys/kern/uipc_mqueue.c
@@ -2469,6 +2469,7 @@ mqf_stat(struct file *fp, struct stat *st, struct ucred *active_cred,
struct mqfs_node *pn = fp->f_data;
bzero(st, sizeof *st);
+ sx_xlock(&mqfs_data.mi_lock);
st->st_atim = pn->mn_atime;
st->st_mtim = pn->mn_mtime;
st->st_ctim = pn->mn_ctime;
@@ -2476,10 +2477,56 @@ mqf_stat(struct file *fp, struct stat *st, struct ucred *active_cred,
st->st_uid = pn->mn_uid;
st->st_gid = pn->mn_gid;
st->st_mode = S_IFIFO | pn->mn_mode;
+ sx_xunlock(&mqfs_data.mi_lock);
return (0);
}
static int
+mqf_chmod(struct file *fp, mode_t mode, struct ucred *active_cred,
+ struct thread *td)
+{
+ struct mqfs_node *pn;
+ int error;
+
+ error = 0;
+ pn = fp->f_data;
+ sx_xlock(&mqfs_data.mi_lock);
+ error = vaccess(VREG, pn->mn_mode, pn->mn_uid, pn->mn_gid, VADMIN,
+ active_cred, NULL);
+ if (error != 0)
+ goto out;
+ pn->mn_mode = mode & ACCESSPERMS;
+out:
+ sx_xunlock(&mqfs_data.mi_lock);
+ return (error);
+}
+
+static int
+mqf_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
+ struct thread *td)
+{
+ struct mqfs_node *pn;
+ int error;
+
+ error = 0;
+ pn = fp->f_data;
+ sx_xlock(&mqfs_data.mi_lock);
+ if (uid == (uid_t)-1)
+ uid = pn->mn_uid;
+ if (gid == (gid_t)-1)
+ gid = pn->mn_gid;
+ if (((uid != pn->mn_uid && uid != active_cred->cr_uid) ||
+ (gid != pn->mn_gid && !groupmember(gid, active_cred))) &&
+ (error = priv_check_cred(active_cred, PRIV_VFS_CHOWN, 0)))
+ goto out;
+ pn->mn_uid = uid;
+ pn->mn_gid = gid;
+out:
+ sx_xunlock(&mqfs_data.mi_lock);
+ return (error);
+}
+
+static int
mqf_kqfilter(struct file *fp, struct knote *kn)
{
struct mqueue *mq = FPTOMQ(fp);
@@ -2535,6 +2582,8 @@ static struct fileops mqueueops = {
.fo_poll = mqf_poll,
.fo_kqfilter = mqf_kqfilter,
.fo_stat = mqf_stat,
+ .fo_chmod = mqf_chmod,
+ .fo_chown = mqf_chown,
.fo_close = mqf_close
};
diff --git a/sys/kern/uipc_sem.c b/sys/kern/uipc_sem.c
index 82a4622..95c29c0 100644
--- a/sys/kern/uipc_sem.c
+++ b/sys/kern/uipc_sem.c
@@ -135,6 +135,8 @@ static fo_poll_t ksem_poll;
static fo_kqfilter_t ksem_kqfilter;
static fo_stat_t ksem_stat;
static fo_close_t ksem_closef;
+static fo_chmod_t ksem_chmod;
+static fo_chown_t ksem_chown;
/* File descriptor operations. */
static struct fileops ksem_ops = {
@@ -146,6 +148,8 @@ static struct fileops ksem_ops = {
.fo_kqfilter = ksem_kqfilter,
.fo_stat = ksem_stat,
.fo_close = ksem_closef,
+ .fo_chmod = ksem_chmod,
+ .fo_chown = ksem_chown,
.fo_flags = DFLAG_PASSABLE
};
@@ -220,19 +224,75 @@ ksem_stat(struct file *fp, struct stat *sb, struct ucred *active_cred,
* file descriptor.
*/
bzero(sb, sizeof(*sb));
- sb->st_mode = S_IFREG | ks->ks_mode; /* XXX */
+ mtx_lock(&sem_lock);
sb->st_atim = ks->ks_atime;
sb->st_ctim = ks->ks_ctime;
sb->st_mtim = ks->ks_mtime;
- sb->st_birthtim = ks->ks_birthtime;
+ sb->st_birthtim = ks->ks_birthtime;
sb->st_uid = ks->ks_uid;
sb->st_gid = ks->ks_gid;
+ sb->st_mode = S_IFREG | ks->ks_mode; /* XXX */
+ mtx_unlock(&sem_lock);
return (0);
}
static int
+ksem_chmod(struct file *fp, mode_t mode, struct ucred *active_cred,
+ struct thread *td)
+{
+ struct ksem *ks;
+ int error;
+
+ error = 0;
+ ks = fp->f_data;
+ mtx_lock(&sem_lock);
+#ifdef MAC
+ error = mac_posixsem_check_setmode(active_cred, ks, mode);
+ if (error != 0)
+ goto out;
+#endif
+ error = vaccess(VREG, ks->ks_mode, ks->ks_uid, ks->ks_gid, VADMIN,
+ active_cred, NULL);
+ if (error != 0)
+ goto out;
+ ks->ks_mode = mode & ACCESSPERMS;
+out:
+ mtx_unlock(&sem_lock);
+ return (error);
+}
+
+static int
+ksem_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
+ struct thread *td)
+{
+ struct ksem *ks;
+ int error;
+
+ ks = fp->f_data;
+ mtx_lock(&sem_lock);
+#ifdef MAC
+ error = mac_posixsem_check_setowner(active_cred, ks, uid, gid);
+ if (error != 0)
+ goto out;
+#endif
+ if (uid == (uid_t)-1)
+ uid = ks->ks_uid;
+ if (gid == (gid_t)-1)
+ gid = ks->ks_gid;
+ if (((uid != ks->ks_uid && uid != active_cred->cr_uid) ||
+ (gid != ks->ks_gid && !groupmember(gid, active_cred))) &&
+ (error = priv_check_cred(active_cred, PRIV_VFS_CHOWN, 0)))
+ goto out;
+ ks->ks_uid = uid;
+ ks->ks_gid = gid;
+out:
+ mtx_unlock(&sem_lock);
+ return (error);
+}
+
+static int
ksem_closef(struct file *fp, struct thread *td)
{
struct ksem *ks;
diff --git a/sys/kern/uipc_shm.c b/sys/kern/uipc_shm.c
index 0414f12..45989cf 100644
--- a/sys/kern/uipc_shm.c
+++ b/sys/kern/uipc_shm.c
@@ -68,6 +68,7 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/mman.h>
#include <sys/mutex.h>
+#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/refcount.h>
#include <sys/resourcevar.h>
@@ -123,6 +124,8 @@ static fo_poll_t shm_poll;
static fo_kqfilter_t shm_kqfilter;
static fo_stat_t shm_stat;
static fo_close_t shm_close;
+static fo_chmod_t shm_chmod;
+static fo_chown_t shm_chown;
/* File descriptor operations. */
static struct fileops shm_ops = {
@@ -134,6 +137,8 @@ static struct fileops shm_ops = {
.fo_kqfilter = shm_kqfilter,
.fo_stat = shm_stat,
.fo_close = shm_close,
+ .fo_chmod = shm_chmod,
+ .fo_chown = shm_chown,
.fo_flags = DFLAG_PASSABLE
};
@@ -218,16 +223,18 @@ shm_stat(struct file *fp, struct stat *sb, struct ucred *active_cred,
* descriptor.
*/
bzero(sb, sizeof(*sb));
- sb->st_mode = S_IFREG | shmfd->shm_mode; /* XXX */
sb->st_blksize = PAGE_SIZE;
sb->st_size = shmfd->shm_size;
sb->st_blocks = (sb->st_size + sb->st_blksize - 1) / sb->st_blksize;
+ mtx_lock(&shm_timestamp_lock);
sb->st_atim = shmfd->shm_atime;
sb->st_ctim = shmfd->shm_ctime;
sb->st_mtim = shmfd->shm_mtime;
- sb->st_birthtim = shmfd->shm_birthtime;
+ sb->st_birthtim = shmfd->shm_birthtime;
+ sb->st_mode = S_IFREG | shmfd->shm_mode; /* XXX */
sb->st_uid = shmfd->shm_uid;
sb->st_gid = shmfd->shm_gid;
+ mtx_unlock(&shm_timestamp_lock);
return (0);
}
@@ -395,14 +402,18 @@ static int
shm_access(struct shmfd *shmfd, struct ucred *ucred, int flags)
{
accmode_t accmode;
+ int error;
accmode = 0;
if (flags & FREAD)
accmode |= VREAD;
if (flags & FWRITE)
accmode |= VWRITE;
- return (vaccess(VREG, shmfd->shm_mode, shmfd->shm_uid, shmfd->shm_gid,
- accmode, ucred, NULL));
+ mtx_lock(&shm_timestamp_lock);
+ error = vaccess(VREG, shmfd->shm_mode, shmfd->shm_uid, shmfd->shm_gid,
+ accmode, ucred, NULL);
+ mtx_unlock(&shm_timestamp_lock);
+ return (error);
}
/*
@@ -651,3 +662,61 @@ shm_mmap(struct shmfd *shmfd, vm_size_t objsize, vm_ooffset_t foff,
*obj = shmfd->shm_object;
return (0);
}
+
+static int
+shm_chmod(struct file *fp, mode_t mode, struct ucred *active_cred,
+ struct thread *td)
+{
+ struct shmfd *shmfd;
+ int error;
+
+ error = 0;
+ shmfd = fp->f_data;
+ mtx_lock(&shm_timestamp_lock);
+ /*
+ * SUSv4 says that x bits of permission need not be affected.
+ * Be consistent with our shm_open there.
+ */
+#ifdef MAC
+ error = mac_posixshm_check_setmode(active_cred, shmfd, mode);
+ if (error != 0)
+ goto out;
+#endif
+ error = vaccess(VREG, shmfd->shm_mode, shmfd->shm_uid,
+ shmfd->shm_gid, VADMIN, active_cred, NULL);
+ if (error != 0)
+ goto out;
+ shmfd->shm_mode = mode & ACCESSPERMS;
+out:
+ mtx_unlock(&shm_timestamp_lock);
+ return (error);
+}
+
+static int
+shm_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
+ struct thread *td)
+{
+ struct shmfd *shmfd;
+ int error;
+
+ shmfd = fp->f_data;
+ mtx_lock(&shm_timestamp_lock);
+#ifdef MAC
+ error = mac_posixshm_check_setowner(active_cred, shmfd, uid, gid);
+ if (error != 0)
+ goto out;
+#endif
+ if (uid == (uid_t)-1)
+ uid = shmfd->shm_uid;
+ if (gid == (gid_t)-1)
+ gid = shmfd->shm_gid;
+ if (((uid != shmfd->shm_uid && uid != active_cred->cr_uid) ||
+ (gid != shmfd->shm_gid && !groupmember(gid, active_cred))) &&
+ (error = priv_check_cred(active_cred, PRIV_VFS_CHOWN, 0)))
+ goto out;
+ shmfd->shm_uid = uid;
+ shmfd->shm_gid = gid;
+out:
+ mtx_unlock(&shm_timestamp_lock);
+ return (error);
+}
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 35d1aee..c0ae0a7 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -96,8 +96,6 @@ SDT_PROBE_ARGTYPE(vfs, , stat, reg, 1, "int");
static int chroot_refuse_vdir_fds(struct filedesc *fdp);
static int getutimes(const struct timeval *, enum uio_seg, struct timespec *);
-static int setfown(struct thread *td, struct vnode *, uid_t, gid_t);
-static int setfmode(struct thread *td, struct vnode *, int);
static int setfflags(struct thread *td, struct vnode *, int);
static int setutimes(struct thread *td, struct vnode *,
const struct timespec *, int, int);
@@ -2865,9 +2863,10 @@ fchflags(td, uap)
/*
* Common implementation code for chmod(), lchmod() and fchmod().
*/
-static int
-setfmode(td, vp, mode)
+int
+setfmode(td, cred, vp, mode)
struct thread *td;
+ struct ucred *cred;
struct vnode *vp;
int mode;
{
@@ -2881,10 +2880,10 @@ setfmode(td, vp, mode)
VATTR_NULL(&vattr);
vattr.va_mode = mode & ALLPERMS;
#ifdef MAC
- error = mac_vnode_check_setmode(td->td_ucred, vp, vattr.va_mode);
+ error = mac_vnode_check_setmode(cred, vp, vattr.va_mode);
if (error == 0)
#endif
- error = VOP_SETATTR(vp, &vattr, td->td_ucred);
+ error = VOP_SETATTR(vp, &vattr, cred);
VOP_UNLOCK(vp, 0);
vn_finished_write(mp);
return (error);
@@ -2980,7 +2979,7 @@ kern_fchmodat(struct thread *td, int fd, char *path, enum uio_seg pathseg,
return (error);
vfslocked = NDHASGIANT(&nd);
NDFREE(&nd, NDF_ONLY_PNBUF);
- error = setfmode(td, nd.ni_vp, mode);
+ error = setfmode(td, td->td_ucred, nd.ni_vp, mode);
vrele(nd.ni_vp);
VFS_UNLOCK_GIANT(vfslocked);
return (error);
@@ -2996,30 +2995,18 @@ struct fchmod_args {
};
#endif
int
-fchmod(td, uap)
- struct thread *td;
- register struct fchmod_args /* {
- int fd;
- int mode;
- } */ *uap;
+fchmod(struct thread *td, struct fchmod_args *uap)
{
struct file *fp;
- int vfslocked;
int error;
AUDIT_ARG_FD(uap->fd);
AUDIT_ARG_MODE(uap->mode);
- if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_FCHMOD,
- &fp)) != 0)
+
+ error = fget(td, uap->fd, CAP_FCHMOD, &fp);
+ if (error != 0)
return (error);
- vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
-#ifdef AUDIT
- vn_lock(fp->f_vnode, LK_SHARED | LK_RETRY);
- AUDIT_ARG_VNODE1(fp->f_vnode);
- VOP_UNLOCK(fp->f_vnode, 0);
-#endif
- error = setfmode(td, fp->f_vnode, uap->mode);
- VFS_UNLOCK_GIANT(vfslocked);
+ error = fo_chmod(fp, uap->mode, td->td_ucred, td);
fdrop(fp, td);
return (error);
}
@@ -3027,9 +3014,10 @@ fchmod(td, uap)
/*
* Common implementation for chown(), lchown(), and fchown()
*/
-static int
-setfown(td, vp, uid, gid)
+int
+setfown(td, cred, vp, uid, gid)
struct thread *td;
+ struct ucred *cred;
struct vnode *vp;
uid_t uid;
gid_t gid;
@@ -3045,11 +3033,11 @@ setfown(td, vp, uid, gid)
vattr.va_uid = uid;
vattr.va_gid = gid;
#ifdef MAC
- error = mac_vnode_check_setowner(td->td_ucred, vp, vattr.va_uid,
+ error = mac_vnode_check_setowner(cred, vp, vattr.va_uid,
vattr.va_gid);
if (error == 0)
#endif
- error = VOP_SETATTR(vp, &vattr, td->td_ucred);
+ error = VOP_SETATTR(vp, &vattr, cred);
VOP_UNLOCK(vp, 0);
vn_finished_write(mp);
return (error);
@@ -3124,7 +3112,7 @@ kern_fchownat(struct thread *td, int fd, char *path, enum uio_seg pathseg,
return (error);
vfslocked = NDHASGIANT(&nd);
NDFREE(&nd, NDF_ONLY_PNBUF);
- error = setfown(td, nd.ni_vp, uid, gid);
+ error = setfown(td, td->td_ucred, nd.ni_vp, uid, gid);
vrele(nd.ni_vp);
VFS_UNLOCK_GIANT(vfslocked);
return (error);
@@ -3182,22 +3170,14 @@ fchown(td, uap)
} */ *uap;
{
struct file *fp;
- int vfslocked;
int error;
AUDIT_ARG_FD(uap->fd);
AUDIT_ARG_OWNER(uap->uid, uap->gid);
- if ((error = getvnode(td->td_proc->p_fd, uap->fd, CAP_FCHOWN, &fp))
- != 0)
+ error = fget(td, uap->fd, CAP_FCHOWN, &fp);
+ if (error != 0)
return (error);
- vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
-#ifdef AUDIT
- vn_lock(fp->f_vnode, LK_SHARED | LK_RETRY);
- AUDIT_ARG_VNODE1(fp->f_vnode);
- VOP_UNLOCK(fp->f_vnode, 0);
-#endif
- error = setfown(td, fp->f_vnode, uap->uid, uap->gid);
- VFS_UNLOCK_GIANT(vfslocked);
+ error = fo_chown(fp, uap->uid, uap->gid, td->td_ucred, td);
fdrop(fp, td);
return (error);
}
diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c
index e8bcc91..a8f1bf1 100644
--- a/sys/kern/vfs_vnops.c
+++ b/sys/kern/vfs_vnops.c
@@ -61,6 +61,7 @@ __FBSDID("$FreeBSD$");
#include <sys/syslog.h>
#include <sys/unistd.h>
+#include <security/audit/audit.h>
#include <security/mac/mac_framework.h>
static fo_rdwr_t vn_read;
@@ -81,6 +82,8 @@ struct fileops vnops = {
.fo_kqfilter = vn_kqfilter,
.fo_stat = vn_statfile,
.fo_close = vn_closefile,
+ .fo_chmod = vn_chmod,
+ .fo_chown = vn_chown,
.fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE
};
@@ -1357,3 +1360,41 @@ vn_rlimit_fsize(const struct vnode *vp, const struct uio *uio,
PROC_UNLOCK(td->td_proc);
return (0);
}
+
+int
+vn_chmod(struct file *fp, mode_t mode, struct ucred *active_cred,
+ struct thread *td)
+{
+ struct vnode *vp;
+ int error, vfslocked;
+
+ vp = fp->f_vnode;
+ vfslocked = VFS_LOCK_GIANT(vp->v_mount);
+#ifdef AUDIT
+ vn_lock(vp, LK_SHARED | LK_RETRY);
+ AUDIT_ARG_VNODE1(vp);
+ VOP_UNLOCK(vp, 0);
+#endif
+ error = setfmode(td, active_cred, vp, mode);
+ VFS_UNLOCK_GIANT(vfslocked);
+ return (error);
+}
+
+int
+vn_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
+ struct thread *td)
+{
+ struct vnode *vp;
+ int error, vfslocked;
+
+ vp = fp->f_vnode;
+ vfslocked = VFS_LOCK_GIANT(vp->v_mount);
+#ifdef AUDIT
+ vn_lock(vp, LK_SHARED | LK_RETRY);
+ AUDIT_ARG_VNODE1(vp);
+ VOP_UNLOCK(vp, 0);
+#endif
+ error = setfown(td, active_cred, vp, uid, gid);
+ VFS_UNLOCK_GIANT(vfslocked);
+ return (error);
+}
diff --git a/sys/ofed/include/linux/linux_compat.c b/sys/ofed/include/linux/linux_compat.c
index 98ad807..90737f2 100644
--- a/sys/ofed/include/linux/linux_compat.c
+++ b/sys/ofed/include/linux/linux_compat.c
@@ -559,7 +559,9 @@ struct fileops linuxfileops = {
.fo_read = linux_file_read,
.fo_poll = linux_file_poll,
.fo_close = linux_file_close,
- .fo_ioctl = linux_file_ioctl
+ .fo_ioctl = linux_file_ioctl,
+ .fo_chmod = invfo_chmod,
+ .fo_chown = invfo_chown,
};
/*
diff --git a/sys/opencrypto/cryptodev.c b/sys/opencrypto/cryptodev.c
index 2c0c503..2bc0e1f 100644
--- a/sys/opencrypto/cryptodev.c
+++ b/sys/opencrypto/cryptodev.c
@@ -301,7 +301,9 @@ static struct fileops cryptofops = {
.fo_poll = cryptof_poll,
.fo_kqfilter = cryptof_kqfilter,
.fo_stat = cryptof_stat,
- .fo_close = cryptof_close
+ .fo_close = cryptof_close,
+ .fo_chmod = invfo_chmod,
+ .fo_chown = invfo_chown,
};
static struct csession *csefind(struct fcrypt *, u_int);
diff --git a/sys/security/mac/mac_framework.h b/sys/security/mac/mac_framework.h
index f515e98e..011cb86 100644
--- a/sys/security/mac/mac_framework.h
+++ b/sys/security/mac/mac_framework.h
@@ -225,6 +225,10 @@ int mac_posixsem_check_getvalue(struct ucred *active_cred,
int mac_posixsem_check_open(struct ucred *cred, struct ksem *ks);
int mac_posixsem_check_post(struct ucred *active_cred,
struct ucred *file_cred, struct ksem *ks);
+int mac_posixsem_check_setmode(struct ucred *cred, struct ksem *ks,
+ mode_t mode);
+int mac_posixsem_check_setowner(struct ucred *cred, struct ksem *ks,
+ uid_t uid, gid_t gid);
int mac_posixsem_check_stat(struct ucred *active_cred,
struct ucred *file_cred, struct ksem *ks);
int mac_posixsem_check_unlink(struct ucred *cred, struct ksem *ks);
@@ -237,6 +241,10 @@ void mac_posixsem_init(struct ksem *);
int mac_posixshm_check_mmap(struct ucred *cred, struct shmfd *shmfd,
int prot, int flags);
int mac_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd);
+int mac_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd,
+ mode_t mode);
+int mac_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd,
+ uid_t uid, gid_t gid);
int mac_posixshm_check_stat(struct ucred *active_cred,
struct ucred *file_cred, struct shmfd *shmfd);
int mac_posixshm_check_truncate(struct ucred *active_cred,
diff --git a/sys/security/mac/mac_policy.h b/sys/security/mac/mac_policy.h
index b874c41..b7ef07b 100644
--- a/sys/security/mac/mac_policy.h
+++ b/sys/security/mac/mac_policy.h
@@ -336,6 +336,12 @@ typedef int (*mpo_posixsem_check_open_t)(struct ucred *cred,
typedef int (*mpo_posixsem_check_post_t)(struct ucred *active_cred,
struct ucred *file_cred, struct ksem *ks,
struct label *kslabel);
+typedef int (*mpo_posixsem_check_setmode_t)(struct ucred *cred,
+ struct ksem *ks, struct label *shmlabel,
+ mode_t mode);
+typedef int (*mpo_posixsem_check_setowner_t)(struct ucred *cred,
+ struct ksem *ks, struct label *shmlabel,
+ uid_t uid, gid_t gid);
typedef int (*mpo_posixsem_check_stat_t)(struct ucred *active_cred,
struct ucred *file_cred, struct ksem *ks,
struct label *kslabel);
@@ -354,6 +360,12 @@ typedef int (*mpo_posixshm_check_mmap_t)(struct ucred *cred,
int flags);
typedef int (*mpo_posixshm_check_open_t)(struct ucred *cred,
struct shmfd *shmfd, struct label *shmlabel);
+typedef int (*mpo_posixshm_check_setmode_t)(struct ucred *cred,
+ struct shmfd *shmfd, struct label *shmlabel,
+ mode_t mode);
+typedef int (*mpo_posixshm_check_setowner_t)(struct ucred *cred,
+ struct shmfd *shmfd, struct label *shmlabel,
+ uid_t uid, gid_t gid);
typedef int (*mpo_posixshm_check_stat_t)(struct ucred *active_cred,
struct ucred *file_cred, struct shmfd *shmfd,
struct label *shmlabel);
@@ -791,6 +803,8 @@ struct mac_policy_ops {
mpo_posixsem_check_getvalue_t mpo_posixsem_check_getvalue;
mpo_posixsem_check_open_t mpo_posixsem_check_open;
mpo_posixsem_check_post_t mpo_posixsem_check_post;
+ mpo_posixsem_check_setmode_t mpo_posixsem_check_setmode;
+ mpo_posixsem_check_setowner_t mpo_posixsem_check_setowner;
mpo_posixsem_check_stat_t mpo_posixsem_check_stat;
mpo_posixsem_check_unlink_t mpo_posixsem_check_unlink;
mpo_posixsem_check_wait_t mpo_posixsem_check_wait;
@@ -800,6 +814,8 @@ struct mac_policy_ops {
mpo_posixshm_check_mmap_t mpo_posixshm_check_mmap;
mpo_posixshm_check_open_t mpo_posixshm_check_open;
+ mpo_posixshm_check_setmode_t mpo_posixshm_check_setmode;
+ mpo_posixshm_check_setowner_t mpo_posixshm_check_setowner;
mpo_posixshm_check_stat_t mpo_posixshm_check_stat;
mpo_posixshm_check_truncate_t mpo_posixshm_check_truncate;
mpo_posixshm_check_unlink_t mpo_posixshm_check_unlink;
diff --git a/sys/security/mac/mac_posix_sem.c b/sys/security/mac/mac_posix_sem.c
index 9035d60..461e5db 100644
--- a/sys/security/mac/mac_posix_sem.c
+++ b/sys/security/mac/mac_posix_sem.c
@@ -198,3 +198,35 @@ mac_posixsem_check_wait(struct ucred *active_cred, struct ucred *file_cred,
return (error);
}
+
+MAC_CHECK_PROBE_DEFINE3(posixsem_check_setmode, "struct ucred *",
+ "struct ksem *", "mode_t");
+
+int
+mac_posixsem_check_setmode(struct ucred *cred, struct ksem *ks, mode_t mode)
+{
+ int error;
+
+ MAC_POLICY_CHECK_NOSLEEP(posixsem_check_setmode, cred, ks,
+ ks->ks_label, mode);
+ MAC_CHECK_PROBE3(posixsem_check_setmode, error, cred, ks, mode);
+
+ return (error);
+}
+
+MAC_CHECK_PROBE_DEFINE4(posixsem_check_setowner, "struct ucred *",
+ "struct ks *", "uid_t", "gid_t");
+
+int
+mac_posixsem_check_setowner(struct ucred *cred, struct ksem *ks, uid_t uid,
+ gid_t gid)
+{
+ int error;
+
+ MAC_POLICY_CHECK_NOSLEEP(posixsem_check_setowner, cred, ks,
+ ks->ks_label, uid, gid);
+ MAC_CHECK_PROBE4(posixsem_check_setowner, error, cred, ks,
+ uid, gid);
+
+ return (error);
+}
diff --git a/sys/security/mac/mac_posix_shm.c b/sys/security/mac/mac_posix_shm.c
index 4432a98..7e1c3f7 100644
--- a/sys/security/mac/mac_posix_shm.c
+++ b/sys/security/mac/mac_posix_shm.c
@@ -181,3 +181,35 @@ mac_posixshm_check_unlink(struct ucred *cred, struct shmfd *shmfd)
return (error);
}
+
+MAC_CHECK_PROBE_DEFINE3(posixshm_check_setmode, "struct ucred *",
+ "struct shmfd *", "mode_t");
+
+int
+mac_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd, mode_t mode)
+{
+ int error;
+
+ MAC_POLICY_CHECK_NOSLEEP(posixshm_check_setmode, cred, shmfd,
+ shmfd->shm_label, mode);
+ MAC_CHECK_PROBE3(posixshm_check_setmode, error, cred, shmfd, mode);
+
+ return (error);
+}
+
+MAC_CHECK_PROBE_DEFINE4(posixshm_check_setowner, "struct ucred *",
+ "struct shmfd *", "uid_t", "gid_t");
+
+int
+mac_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd, uid_t uid,
+ gid_t gid)
+{
+ int error;
+
+ MAC_POLICY_CHECK_NOSLEEP(posixshm_check_setowner, cred, shmfd,
+ shmfd->shm_label, uid, gid);
+ MAC_CHECK_PROBE4(posixshm_check_setowner, error, cred, shmfd,
+ uid, gid);
+
+ return (error);
+}
diff --git a/sys/security/mac_stub/mac_stub.c b/sys/security/mac_stub/mac_stub.c
index 007efb8..361bb15 100644
--- a/sys/security/mac_stub/mac_stub.c
+++ b/sys/security/mac_stub/mac_stub.c
@@ -687,6 +687,22 @@ stub_posixsem_check_post(struct ucred *active_cred, struct ucred *file_cred,
}
static int
+stub_posixsem_check_setmode(struct ucred *cred, struct ksem *ks,
+ struct label *kslabel, mode_t mode)
+{
+
+ return (0);
+}
+
+static int
+stub_posixsem_check_setowner(struct ucred *cred, struct ksem *ks,
+ struct label *kslabel, uid_t uid, gid_t gid)
+{
+
+ return (0);
+}
+
+static int
stub_posixsem_check_stat(struct ucred *active_cred, struct ucred *file_cred,
struct ksem *ks, struct label *kslabel)
{
@@ -734,6 +750,22 @@ stub_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd,
}
static int
+stub_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd,
+ struct label *shmlabel, mode_t mode)
+{
+
+ return (0);
+}
+
+static int
+stub_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd,
+ struct label *shmlabel, uid_t uid, gid_t gid)
+{
+
+ return (0);
+}
+
+static int
stub_posixshm_check_stat(struct ucred *active_cred, struct ucred *file_cred,
struct shmfd *shmfd, struct label *shmlabel)
{
@@ -1731,6 +1763,8 @@ static struct mac_policy_ops stub_ops =
.mpo_posixsem_check_getvalue = stub_posixsem_check_getvalue,
.mpo_posixsem_check_open = stub_posixsem_check_open,
.mpo_posixsem_check_post = stub_posixsem_check_post,
+ .mpo_posixsem_check_setmode = stub_posixsem_check_setmode,
+ .mpo_posixsem_check_setowner = stub_posixsem_check_setowner,
.mpo_posixsem_check_stat = stub_posixsem_check_stat,
.mpo_posixsem_check_unlink = stub_posixsem_check_unlink,
.mpo_posixsem_check_wait = stub_posixsem_check_wait,
@@ -1740,6 +1774,8 @@ static struct mac_policy_ops stub_ops =
.mpo_posixshm_check_mmap = stub_posixshm_check_mmap,
.mpo_posixshm_check_open = stub_posixshm_check_open,
+ .mpo_posixshm_check_setmode = stub_posixshm_check_setmode,
+ .mpo_posixshm_check_setowner = stub_posixshm_check_setowner,
.mpo_posixshm_check_stat = stub_posixshm_check_stat,
.mpo_posixshm_check_truncate = stub_posixshm_check_truncate,
.mpo_posixshm_check_unlink = stub_posixshm_check_unlink,
diff --git a/sys/security/mac_test/mac_test.c b/sys/security/mac_test/mac_test.c
index bef0cb7..2aa2e38 100644
--- a/sys/security/mac_test/mac_test.c
+++ b/sys/security/mac_test/mac_test.c
@@ -1297,6 +1297,30 @@ test_posixsem_check_post(struct ucred *active_cred, struct ucred *file_cred,
return (0);
}
+COUNTER_DECL(posixsem_check_setmode);
+static int
+test_posixsem_check_setmode(struct ucred *cred, struct ksem *ks,
+ struct label *kslabel, mode_t mode)
+{
+
+ LABEL_CHECK(cred->cr_label, MAGIC_CRED);
+ LABEL_CHECK(kslabel, MAGIC_POSIX_SHM);
+ COUNTER_INC(posixsem_check_setmode);
+ return (0);
+}
+
+COUNTER_DECL(posixsem_check_setowner);
+static int
+test_posixsem_check_setowner(struct ucred *cred, struct ksem *ks,
+ struct label *kslabel, uid_t uid, gid_t gid)
+{
+
+ LABEL_CHECK(cred->cr_label, MAGIC_CRED);
+ LABEL_CHECK(kslabel, MAGIC_POSIX_SHM);
+ COUNTER_INC(posixsem_check_setowner);
+ return (0);
+}
+
COUNTER_DECL(posixsem_check_stat);
static int
test_posixsem_check_stat(struct ucred *active_cred,
@@ -1390,6 +1414,30 @@ test_posixshm_check_open(struct ucred *cred, struct shmfd *shmfd,
return (0);
}
+COUNTER_DECL(posixshm_check_setmode);
+static int
+test_posixshm_check_setmode(struct ucred *cred, struct shmfd *shmfd,
+ struct label *shmfdlabel, mode_t mode)
+{
+
+ LABEL_CHECK(cred->cr_label, MAGIC_CRED);
+ LABEL_CHECK(shmfdlabel, MAGIC_POSIX_SHM);
+ COUNTER_INC(posixshm_check_setmode);
+ return (0);
+}
+
+COUNTER_DECL(posixshm_check_setowner);
+static int
+test_posixshm_check_setowner(struct ucred *cred, struct shmfd *shmfd,
+ struct label *shmfdlabel, uid_t uid, gid_t gid)
+{
+
+ LABEL_CHECK(cred->cr_label, MAGIC_CRED);
+ LABEL_CHECK(shmfdlabel, MAGIC_POSIX_SHM);
+ COUNTER_INC(posixshm_check_setowner);
+ return (0);
+}
+
COUNTER_DECL(posixshm_check_stat);
static int
test_posixshm_check_stat(struct ucred *active_cred,
@@ -3045,6 +3093,8 @@ static struct mac_policy_ops test_ops =
.mpo_posixsem_check_getvalue = test_posixsem_check_getvalue,
.mpo_posixsem_check_open = test_posixsem_check_open,
.mpo_posixsem_check_post = test_posixsem_check_post,
+ .mpo_posixsem_check_setmode = test_posixsem_check_setmode,
+ .mpo_posixsem_check_setowner = test_posixsem_check_setowner,
.mpo_posixsem_check_stat = test_posixsem_check_stat,
.mpo_posixsem_check_unlink = test_posixsem_check_unlink,
.mpo_posixsem_check_wait = test_posixsem_check_wait,
@@ -3054,6 +3104,8 @@ static struct mac_policy_ops test_ops =
.mpo_posixshm_check_mmap = test_posixshm_check_mmap,
.mpo_posixshm_check_open = test_posixshm_check_open,
+ .mpo_posixshm_check_setmode = test_posixshm_check_setmode,
+ .mpo_posixshm_check_setowner = test_posixshm_check_setowner,
.mpo_posixshm_check_stat = test_posixshm_check_stat,
.mpo_posixshm_check_truncate = test_posixshm_check_truncate,
.mpo_posixshm_check_unlink = test_posixshm_check_unlink,
diff --git a/sys/sys/file.h b/sys/sys/file.h
index 2c64bcf..5a4af33 100644
--- a/sys/sys/file.h
+++ b/sys/sys/file.h
@@ -85,6 +85,10 @@ typedef int fo_kqfilter_t(struct file *fp, struct knote *kn);
typedef int fo_stat_t(struct file *fp, struct stat *sb,
struct ucred *active_cred, struct thread *td);
typedef int fo_close_t(struct file *fp, struct thread *td);
+typedef int fo_chmod_t(struct file *fp, mode_t mode,
+ struct ucred *active_cred, struct thread *td);
+typedef int fo_chown_t(struct file *fp, uid_t uid, gid_t gid,
+ struct ucred *active_cred, struct thread *td);
typedef int fo_flags_t;
struct fileops {
@@ -96,6 +100,8 @@ struct fileops {
fo_kqfilter_t *fo_kqfilter;
fo_stat_t *fo_stat;
fo_close_t *fo_close;
+ fo_chmod_t *fo_chmod;
+ fo_chown_t *fo_chown;
fo_flags_t fo_flags; /* DFLAG_* below */
};
@@ -200,6 +206,9 @@ fo_kqfilter_t soo_kqfilter;
fo_stat_t soo_stat;
fo_close_t soo_close;
+fo_chmod_t invfo_chmod;
+fo_chown_t invfo_chown;
+
void finit(struct file *, u_int, short, void *, struct fileops *);
int fgetvp(struct thread *td, int fd, cap_rights_t rights, struct vnode **vpp);
int fgetvp_rights(struct thread *td, int fd, cap_rights_t need,
@@ -233,6 +242,8 @@ static __inline fo_poll_t fo_poll;
static __inline fo_kqfilter_t fo_kqfilter;
static __inline fo_stat_t fo_stat;
static __inline fo_close_t fo_close;
+static __inline fo_chmod_t fo_chmod;
+static __inline fo_chown_t fo_chown;
static __inline int
fo_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
@@ -296,6 +307,22 @@ fo_kqfilter(struct file *fp, struct knote *kn)
return ((*fp->f_ops->fo_kqfilter)(fp, kn));
}
+static __inline int
+fo_chmod(struct file *fp, mode_t mode, struct ucred *active_cred,
+ struct thread *td)
+{
+
+ return ((*fp->f_ops->fo_chmod)(fp, mode, active_cred, td));
+}
+
+static __inline int
+fo_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
+ struct thread *td)
+{
+
+ return ((*fp->f_ops->fo_chown)(fp, uid, gid, active_cred, td));
+}
+
#endif /* _KERNEL */
#endif /* !SYS_FILE_H */
diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h
index 1c4c7b7..81be360 100644
--- a/sys/sys/vnode.h
+++ b/sys/sys/vnode.h
@@ -778,7 +778,15 @@ void vfs_mark_atime(struct vnode *vp, struct ucred *cred);
struct dirent;
int vfs_read_dirent(struct vop_readdir_args *ap, struct dirent *dp, off_t off);
-int vfs_unixify_accmode(accmode_t *accmode);
+int vfs_unixify_accmode(accmode_t *accmode);
+
+int setfmode(struct thread *td, struct ucred *cred, struct vnode *vp, int mode);
+int setfown(struct thread *td, struct ucred *cred, struct vnode *vp, uid_t uid,
+ gid_t gid);
+int vn_chmod(struct file *fp, mode_t mode, struct ucred *active_cred,
+ struct thread *td);
+int vn_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred,
+ struct thread *td);
#endif /* _KERNEL */
OpenPOWER on IntegriCloud