aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/vfs_syscalls.c
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2021-03-18 10:41:47 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2021-04-15 09:48:24 +0000
commit8d9ed174f3afba5f114742447e622fc1173d4774 (patch)
tree12c1f979472855bf6ed586e82d66cefc43e00817 /sys/kern/vfs_syscalls.c
parent509124b62616f73dcdc42263ee109392dafafd99 (diff)
downloadsrc-8d9ed174f3afba5f114742447e622fc1173d4774.tar.gz
src-8d9ed174f3afba5f114742447e622fc1173d4774.zip
open(2): Implement O_PATH
Reviewed by: markj Tested by: pho Discussed with: walker.aj325_gmail.com, wulf Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D29323
Diffstat (limited to 'sys/kern/vfs_syscalls.c')
-rw-r--r--sys/kern/vfs_syscalls.c61
1 files changed, 48 insertions, 13 deletions
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 45f155ebff3d..5a1efcdec467 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -375,7 +375,7 @@ kern_fstatfs(struct thread *td, int fd, struct statfs *buf)
int error;
AUDIT_ARG_FD(fd);
- error = getvnode(td, fd, &cap_fstatfs_rights, &fp);
+ error = getvnode_path(td, fd, &cap_fstatfs_rights, &fp);
if (error != 0)
return (error);
vp = fp->f_vnode;
@@ -891,7 +891,7 @@ sys_fchdir(struct thread *td, struct fchdir_args *uap)
int error;
AUDIT_ARG_FD(uap->fd);
- error = getvnode(td, uap->fd, &cap_fchdir_rights,
+ error = getvnode_path(td, uap->fd, &cap_fchdir_rights,
&fp);
if (error != 0)
return (error);
@@ -1023,9 +1023,10 @@ change_dir(struct vnode *vp, struct thread *td)
static __inline void
flags_to_rights(int flags, cap_rights_t *rightsp)
{
-
if (flags & O_EXEC) {
cap_rights_set_one(rightsp, CAP_FEXECVE);
+ if (flags & O_PATH)
+ return;
} else {
switch ((flags & O_ACCMODE)) {
case O_RDONLY:
@@ -1112,11 +1113,15 @@ kern_openat(struct thread *td, int fd, const char *path, enum uio_seg pathseg,
AUDIT_ARG_MODE(mode);
cap_rights_init_one(&rights, CAP_LOOKUP);
flags_to_rights(flags, &rights);
+
/*
* Only one of the O_EXEC, O_RDONLY, O_WRONLY and O_RDWR flags
- * may be specified.
+ * may be specified. On the other hand, for O_PATH any mode
+ * except O_EXEC is ignored.
*/
- if (flags & O_EXEC) {
+ if ((flags & O_PATH) != 0) {
+ flags &= ~(O_CREAT | O_ACCMODE);
+ } else if ((flags & O_EXEC) != 0) {
if (flags & O_ACCMODE)
return (EINVAL);
} else if ((flags & O_ACCMODE) == O_ACCMODE) {
@@ -1145,8 +1150,10 @@ kern_openat(struct thread *td, int fd, const char *path, enum uio_seg pathseg,
* wonderous happened deep below and we just pass it up
* pretending we know what we do.
*/
- if (error == ENXIO && fp->f_ops != &badfileops)
+ if (error == ENXIO && fp->f_ops != &badfileops) {
+ MPASS((flags & O_PATH) == 0);
goto success;
+ }
/*
* Handle special fdopen() case. bleh.
@@ -1176,14 +1183,16 @@ kern_openat(struct thread *td, int fd, const char *path, enum uio_seg pathseg,
* files that switched type in the cdevsw fdopen() method.
*/
fp->f_vnode = vp;
+
/*
* If the file wasn't claimed by devfs bind it to the normal
* vnode operations here.
*/
if (fp->f_ops == &badfileops) {
- KASSERT(vp->v_type != VFIFO,
+ KASSERT(vp->v_type != VFIFO || (flags & O_PATH) != 0,
("Unexpected fifo fp %p vp %p", fp, vp));
- finit_vnode(fp, flags, NULL, &vnops);
+ finit_vnode(fp, flags, NULL, (flags & O_PATH) != 0 ?
+ &path_fileops : &vnops);
}
VOP_UNLOCK(vp);
@@ -1882,7 +1891,7 @@ kern_funlinkat(struct thread *td, int dfd, const char *path, int fd,
fp = NULL;
if (fd != FD_NONE) {
- error = getvnode(td, fd, &cap_no_rights, &fp);
+ error = getvnode_path(td, fd, &cap_no_rights, &fp);
if (error != 0)
return (error);
}
@@ -4255,12 +4264,13 @@ out:
}
/*
- * Convert a user file descriptor to a kernel file entry and check that, if it
- * is a capability, the correct rights are present. A reference on the file
- * entry is held upon returning.
+ * This variant of getvnode() allows O_PATH files. Caller should
+ * ensure that returned file and vnode are only used for compatible
+ * semantics.
*/
int
-getvnode(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp)
+getvnode_path(struct thread *td, int fd, cap_rights_t *rightsp,
+ struct file **fpp)
{
struct file *fp;
int error;
@@ -4285,11 +4295,36 @@ getvnode(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp)
fdrop(fp, td);
return (EINVAL);
}
+
*fpp = fp;
return (0);
}
/*
+ * Convert a user file descriptor to a kernel file entry and check
+ * that, if it is a capability, the correct rights are present.
+ * A reference on the file entry is held upon returning.
+ */
+int
+getvnode(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp)
+{
+ int error;
+
+ error = getvnode_path(td, fd, rightsp, fpp);
+
+ /*
+ * Filter out O_PATH file descriptors, most getvnode() callers
+ * do not call fo_ methods.
+ */
+ if (error == 0 && (*fpp)->f_ops == &path_fileops) {
+ fdrop(*fpp, td);
+ error = EBADF;
+ }
+
+ return (error);
+}
+
+/*
* Get an (NFS) file handle.
*/
#ifndef _SYS_SYSPROTO_H_